Docker容器之间的数据库连接被拒绝

73次阅读
没有评论

问题描述

在使用Docker时遇到了一个问题,他在一个MySQL数据库容器和一个Golang REST API容器之间建立数据库连接时遇到了连接被拒绝的错误。
以下是用户的具体情况:
– 用户有一个运行MySQL数据库的Docker容器,使用了以下的docker-compose.yml配置:

version: '3.3'
services:
  db:
    image: mysql:5.7
    restart: always
    environment:
      MYSQL_DATABASE: 'demo'
      MYSQL_USER: 'user'
      MYSQL_PASSWORD: 'password'
      MYSQL_ROOT_PASSWORD: 'password'
    ports:
      - '3306:3306'
    expose:
      - '3306'
    volumes:
      - my-db:/var/lib/mysql
    networks:
      - backend
networks:
  backend:
    driver: bridge
volumes:
  my-db:
  • 用户还有一个基本的Golang REST API,使用了以下的Dockerfile配置:
# Start from golang:1.12-alpine base image
FROM golang:1.12-alpine

# Adding git, bash and openssh to the image
RUN apk update && apk upgrade && \
    apk add --no-cache bash git openssh

# Set the Current Working Directory inside the container
WORKDIR /app

# Copy the source from the current directory to the Working Directory inside the container
COPY . .

RUN go get -d github.com/gorilla/mux
RUN go get -d github.com/go-sql-driver/mysql
RUN go get -d github.com/golang-migrate/migrate
RUN go get -d github.com/golang-migrate/migrate/database/mysql
RUN go get -d github.com/golang-migrate/migrate/source/file

# Build the Go app
RUN go build -o main .

# Expose port 8080 to the outside world
EXPOSE 8080

# Run the executable
CMD ["./main"]
  • 用户在Golang代码中调用了以下函数来建立数据库连接:
func CreateDatabase() (*sql.DB, error) {
    serverName := "localhost:3306"
    user := "user"
    password := "password"
    dbName := "demo"
    connectionString := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&collation=utf8mb4_unicode_ci&parseTime=true&multiStatements=true", user, password, serverName, dbName)
    db, err := sql.Open("mysql", connectionString)
    if err != nil {
        return nil, err
    }
    if err := migrateDatabase(db); err != nil {
        return db, err
    }
    return db, nil
}
  • 用户尝试运行Golang API容器时,使用了以下命令:
$ docker run -p 80:8080 --network=<appname>-mysql_backend <imageid>
  • 用户遇到了以下错误信息:
$ Database connection failed: dial tcp 127.0.0.1:3306: connect: connection refused

用户希望能够解决这个问题,使Golang API容器能够成功连接到MySQL数据库容器。

解决方案

请注意以下操作注意版本差异及修改前做好备份。

方案1

首先,确保Golang API容器和MySQL数据库容器在同一个网络中。根据用户提供的docker-compose.yml文件,我们可以看到两个容器都在名为backend的网络中。因此,我们需要将Golang API容器添加到相同的网络中。
以下是解决问题的步骤:
1. 在docker-compose.yml文件中添加Golang API服务的定义,并将其添加到backend网络中。
2. 使用docker-compose build命令构建镜像。
3. 使用docker-compose up命令启动容器。
下面是一个示例的docker-compose.yml文件:

version: '3.3'
services:
  db:
    image: mysql:5.7
    restart: always
    environment:
      MYSQL_DATABASE: 'demo'
      MYSQL_USER: 'user'
      MYSQL_PASSWORD: 'password'
      MYSQL_ROOT_PASSWORD: 'password'
    ports:
      - '3306:3306'
    expose:
      - '3306'
    volumes:
      - my-db:/var/lib/mysql
    networks:
      - backend
  api:
    build: .
    ports:
      - '80:8080'
    networks:
      - backend
networks:
  backend:
    driver: bridge
volumes:
  my-db:

在上面的示例中,我们添加了一个名为api的服务,并将其构建为当前目录中的Dockerfile。我们还将api服务添加到了backend网络中。

方案2

除了将Golang API容器添加到相同的backend网络中之外,还需要修复连接字符串中的MySQL主机参数。
根据用户提供的代码,我们可以看到MySQL主机被设置为localhost

serverName := "localhost:3306"

然而,MySQL并不在Golang API容器的localhost上监听,所以我们需要将其更改为docker-compose.yml文件中MySQL服务的名称,即db
以下是修复连接字符串的步骤:
1. 在Golang代码中将MySQL主机更改为db
2. 重新构建Golang API容器。
下面是修复后的代码示例:

func CreateDatabase() (*sql.DB, error) {
    serverName := "db:3306"
    user := "user"
    password := "password"
    dbName := "demo"
    connectionString := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&collation=utf8mb4_unicode_ci&parseTime=true&multiStatements=true", user, password, serverName, dbName)
    db, err := sql.Open("mysql", connectionString)
    if err != nil {
        return nil, err
    }
    if err := migrateDatabase(db); err != nil {
        return db, err
    }
    return db, nil
}

在上面的示例中,我们将MySQL主机从localhost更改为db
请注意,如果MySQL容器需要一些额外的时间来启动并变为可连接状态,您可能需要在连接之前等待适当的时间。

总结

在本文中,我们讨论了如何解决在Docker容器之间建立数据库连接时连接被拒绝的问题。我们提供了两种解决方案,包括将Golang API容器添加到相同的网络中,并修复连接字符串中的MySQL主机参数。根据您的具体情况,您可以选择适合您的解决方案。希望本文对您有所帮助!

正文完