使用go webservice在docker中连接到mysql时连接被拒绝

Posted

技术标签:

【中文标题】使用go webservice在docker中连接到mysql时连接被拒绝【英文标题】:Connection refused when connecting to mysql in docker, using a go webservice 【发布时间】:2021-12-18 21:43:15 【问题描述】:

我无法在 docker 容器中运行我的 mysql 数据库。我用 go 构建了一个非常简单的 web 服务来尝试解决错误,但是无论我尝试什么,在启动 docker build 时,我都会不断收到“连接被拒绝错误”

所以我做了一个最简单的最小例子,目录树看起来像这样:

Example/
| ---- Dockerfile
| ---- main.go
| ---- docker-compose.yml
database/
        | ---- Dockerfile
        | ---- example.sql

我的 Example/Dockerfile 看起来像这样:

FROM golang:alpine
ENV GO111MODULE=on \
    CGO_ENABLED=0 \
    GOOS=linux \
    GOARCH=amd64

WORKDIR /build

COPY go.mod .
COPY go.sum .
RUN go mod download

COPY . .
RUN go build -o main .

EXPOSE 8080

CMD ["/build/main"]

数据库dockerfile只有三行:

FROM mysql:8.0.24
COPY example.sql /docker-entrypoint-initdb.d/example.sql
EXPOSE 3306

main.go 小而简单:

package main
import "fmt"
import "net/http"
import _ "github.com/go-sql-driver/mysql"
import "database/sql"
import "log"

func main() 
    http.HandleFunc("/", handler)

    db, err := sql.Open("mysql", "user:password@tcp(db:3307)/example") // -- this is the troublemaker!
    if err != nil 
        log.Fatal(err)
    

    pingErr := db.Ping()
    if pingErr !=  nil 
        log.Fatal(pingErr)
    

    fmt.Println("Connected to DB")

    http.ListenAndServe(":8080", nil)

func handler(w http.ResponseWriter, r *http.Request)
    fmt.Println("Calling handler")
    fmt.Println(r.URL.Path)

docker-compose.yml 也没什么特别的

version: '3'
services:
  db:
    build:
      context: ./database
    volumes: 
      - database:/database/data
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: example
      MYSQL_USER: user
      MYSQL_PASSWORD: password
    container_name: container_db
    ports:
      - "3307:3306"
    networks:
      - default
api:
    build:
      context: .
    restart: on-failure
    container_name: "container_api"
        volumes:
            - api:/usr/api
    ports:
      - "8080:8080"
    networks:
      - default

volumes:
    database:
    api:

而且数据库文件example.sql也很简单

CREATE DATABASE IF NOT EXISTS example;
CREATE USER IF NOT EXISTS 'user'@'%' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'user'@'%' WITH GRANT OPTION;

CREATE TABLE IF NOT EXISTS exampleTable (
    id           INT             NOT NULL,
    name         VARCHAR(2048)   NOT NULL,
    lastName     VARCHAR(2048)
    PRIMARY KEY (`id`)
);

我尝试在连接中使用服务名称so db,以及容器名称container_db,但错误仍然存​​在。我还发现了一个帖子,建议配置mysql绑定值0.0.0.0,但这也没有帮助我。

有时我也只得到一个错误输出:“container_api exited with code 1”

【问题讨论】:

【参考方案1】:

您应该从您的服务中删除networks 部分。这将导致两个容器在新网络中启动。同一网络中的容器无需暴露端口即可相互通信,因此您也可以从 db 服务中删除 ports 部分。现在将 DSN 更改为使用端口 3307 而不是 3307。

这里的主要问题是当你公开一个端口时,你将主机上的一个端口映射到容器上的一个端口。使用您当前的配置,您告诉主机将端口 3307 转发到 3306,但是您的 api 服务是容器,不能使用主机上的端口(3307),它需要使用 3306 端口。而默认网络中的容器只有拥有自己的网络才能相互通信

【讨论】:

我认为ports: 3307: 3306 表示该服务可以通过端口3307 提供给外界?在东部,这就是我在其他与此类问题相关的帖子中发现的。所以我从配置中删除了网络部分,从 db 服务中删除了端口,我更新了我的连接,比如db, err := sql.Open("mysql", "user:password@tcp(container_db:3306)/example"),这样我得到了错误Error 1044: Access denied for user 'user'@'%' to database 'example' @malajedala 是的,但是您的其他 docker 容器不是外部世界。您应该查看docs.docker.com/compose/networking/#specify-custom-networks 以获得更详细的说明。 Access denied for user是进度,表示可以连接,只是你的用户名和密码错误。 container_api | 2021/11/04 17:27:43 Error 1044: Access denied for user 'user'@'%' to database 'example' container_api | 2021/11/04 17:28:42 dial tcp 172.19.0.2:3306: connect: connection refused 我不确定,我仍然收到此错误消息。我刚刚删除并读取了带有“密码”的“用户”到 mysql,就像在我最初的帖子中一样,我检查了前面评论中所写的连接,密码中没有拼写错误,也在撰写 docker 文件中的所有内容看起来很完美 我认为这是由于startup order。如果您查看日志,您首先会收到连接被拒绝错误。在api 重新启动后的一秒钟后,您会收到拒绝访问错误。您应该将depends_on 配置添加到您的api 服务中,这样它才会在db 服务已经运行时启动。至于身份验证错误,如果您尝试使用root 凭据会发生什么?因为该用户应该没有权限问题

以上是关于使用go webservice在docker中连接到mysql时连接被拒绝的主要内容,如果未能解决你的问题,请参考以下文章

Go 连接docker 数据库

GOGO更新1.18版本并在Docker下运行实际案例

在 Docker 中使用私有 gitlab 模块构建 Go 应用程序

Docker入门实战-SSH连接docker容器

如何使用Docker容器建立TCP传出连接?

如何发布https的webservice