【问题标题】:Unable to connect mysql from docker container?无法从 docker 容器连接 mysql?
【发布时间】:2019-07-22 08:10:20
【问题描述】:

我创建了一个 docker-compose 文件,它有两个服务:GoMysql。它为 go 和 mysql 创建容器。现在我正在运行尝试连接到作为 docker 容器运行的 mysql 数据库的代码。但我得到了错误。

docker-compose.yml

version: "2"
services:
  app:
    container_name: golang
    restart: always
    build: .
    ports:
      - "49160:8800"
    links:
      - "mysql"
    depends_on:
      - "mysql" 

  mysql:
    image: mysql
    container_name: mysql
    volumes:
      - dbdata:/var/lib/mysql
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=root
      - MYSQL_DATABASE=testDB
      - MYSQL_USER=root
      - MYSQL_PASSWORD=root
    ports:
      - "3307:3306"
volumes:
  dbdata:

连接mysql数据库时出错

golang   | 2019/02/28 11:33:05 dial tcp 127.0.0.1:3306: connect: connection refused
golang   | 2019/02/28 11:33:05 http: panic serving 172.24.0.1:49066: dial tcp 127.0.0.1:3306: connect: connection refused
golang   | goroutine 19 [running]:

与 MySql 数据库的连接

func DB() *gorm.DB {
    db, err := gorm.Open("mysql", "root:root@tcp(mysql:3306)/testDB?charset=utf8&parseTime=True&loc=Local")
    if err != nil {
        log.Panic(err)
    }
    log.Println("Connection Established")
    return db
}

编辑:更新 docker 文件

FROM golang:latest 
RUN go get -u github.com/gorilla/mux
RUN go get -u github.com/jinzhu/gorm
RUN go get -u github.com/go-sql-driver/mysql
COPY ./wait-for-it.sh .
RUN chmod +x /wait-for-it.sh
WORKDIR /go/src/app
ADD . src
EXPOSE 8800
CMD ["go", "run", "src/main.go"]

我正在使用 gorm 包,它可以让我连接到数据库

【问题讨论】:

    标签: docker docker-compose database-connection


    【解决方案1】:

    depends_on 不是验证 MySQL 实际上已准备好接收连接。一旦数据库容器运行,它将启动第二个容器,无论它是否已准备好连接,这可能会导致您的应用程序出现此类问题,因为它期望数据库已准备好,这可能不是真的。

    引用自the documentation

    depends_on 在启动 web 之前不会等待 db 和 redis “准备好” - 仅在它们启动之前。

    有许多工具/脚本可用于解决此问题,例如 wait-forsh 兼容,以防您的图像基于 Alpine(如果您的图像中有 bash,则可以使用 wait-for-it图片)

    您所要做的就是通过Dockerfile 将脚本添加到您的映像中,然后在docker-compose.yml 中使用此命令来为您希望使其等待数据库的服务。

    -- 之后是您通常用来启动应用程序的命令

    version: "2"
    services:
      app:
        container_name: golang
        ...
            command: ["./wait-for", "mysql:3306", "--", "go", "run", "myapplication"]
        links:
          - "mysql"
        depends_on:
          - "mysql" 
    
      mysql:
        image: mysql
      ...
    

    为了便于阅读,我从 docker-compose 中删除了一些部分。

    用你的golang镜像的CMD修改这部分go run myapplication

    请参阅Controlling startup order 了解有关此问题的更多信息以及解决此问题的策略。


    解决连接问题后会出现的另一个问题如下:

    使用root 值设置MYSQL_USER 将导致MySQL 失败并显示以下错误消息:

    ERROR 1396 (HY000) at line 1: Operation CREATE USER failed for 'root'@'%'
    

    这是因为该用户已存在于数据库中,并且它试图创建另一个用户。如果您需要使用 root 用户本身,您可以只使用这个变量 MYSQL_ROOT_PASSWORD 或更改 MYSQL_USER 的值,这样您就可以在您的应用程序中安全地使用它而不是 root 用户。

    更新:如果找不到并且路径正确,您可能需要编写如下命令:

    command: sh -c "./wait-for mysql:3306 -- go run myapplication"
    

    【讨论】:

    • 你能告诉我我应该写什么确切的代码来摆脱这个。
    • 您是否已将其添加到图像并使其可执行?
    • 不,我没有将它添加到图像中。请告诉我如何将其添加到图像中?虽然我在 DOCKER 文件中将其写为 COPY wait-for-it.sh /wait-for-it.sh
    • 请将更新后的 Dockerfile 发布到问题中,以便我检查它,如果您已经使用了 COPY,那么这意味着您将它包含在映像中并使其可执行
    • 请检查我更新的问题。我已经添加了 docker 文件
    【解决方案2】:

    首先,如果您使用的是最新版本的 docker compose,则您的应用服务中不需要链接参数。我引用 docker compose 文档Warning: The --link flag is a legacy feature of Docker. It may eventually be removed. Unless you absolutely need to continue using it,https://docs.docker.com/compose/compose-file/#links

    我认为解决方案是使用networks 参数。这将创建一个 docker 网络并向其中添加每个服务。

    试试这个

    version: "2"
    services:
      app:
        container_name: golang
        restart: always
        build: .
        ports:
          - "49160:8800"
        networks:
         - my_network
        depends_on:
          - "mysql" 
    
      mysql:
        image: mysql
        container_name: mysql
        volumes:
          - dbdata:/var/lib/mysql
        restart: always
        networks:
         - my_network
        environment:
          - MYSQL_ROOT_PASSWORD=root
          - MYSQL_DATABASE=testDB
          - MYSQL_USER=root
          - MYSQL_PASSWORD=root
        ports:
          - "3307:3306"
    volumes:
      dbdata:
    networks:
      my_network:
        driver: bridge
    

    顺便说一句,如果你只从你的应用服务连接到 Mysql,你不需要暴露 mysql 端口。如果容器在同一个网络中运行,它们可以到达该网络内的所有端口。

    如果我的例子不起作用,试试这个 运行 docker compose,然后使用 docker container exec -it CONTAINER_NAME bash 安装ping以测试连接,然后运行ping mysql。

    【讨论】:

    • 我尝试了问题中的 compose 文件,只是 ping 了 mysql,它似乎工作正常并显示了正确的 IP。在它尝试连接到 `127.0.0.1:3306` localhost 的问题中,我想知道为什么。
    • 我无法连接到 mysql 服务器。告诉我我想在这里输入什么 root:root@tcp(mysql:3306)/testDB?charset=utf8&parseTime=True&loc=Local
    • 我做了一些研发,知道这个问题是由于docker启动命令引起的
    • 请就上述问题提出解决方案。
    猜你喜欢
    • 2021-04-04
    • 2019-04-01
    • 2017-08-10
    • 1970-01-01
    • 2016-04-26
    • 2016-07-22
    • 2020-12-30
    • 2020-07-29
    • 2019-09-04
    相关资源
    最近更新 更多