使用Docker连接Golang与MySQL的实践指南

使用Docker连接Golang与MySQL的实践指南 我正在尝试使用Docker将Go与MySQL连接,对于本地的MySQL实例,它可以完美工作,但当我尝试访问Docker容器中的MySQL实例时,总是收到连接被拒绝的错误。

有人能帮我解决这个问题吗?

.env

MYSQL_IDS_USERNAME=root
MYSQL_IDS_PASSWORD=12345678
MYSQL_IDS_HOST=localhost
MYSQL_IDS_PORT=3306
MYSQL_IDS_SCHEMA=ids_db
MYSQL_ROOT_PASSWORD=12345678

docker-compose.yml

version: '3.7'

services:
  mysql:
    image: mysql:latest
    volumes:
      - "./db_data:/var/lib/mysql"
    env_file:
      - ".env"
    ports:
      - "3307:3306"
    networks:
      - appmysql

  id_service:
    container_name: id_service
    build:
      context: .
      dockerfile: Dockerfile
    image: rezwanulhaque/id-service:latest
    volumes:
      - "./logs:/app/lib/logs"
    ports:
      - "7001:7001"
    depends_on:
      - mysql
    networks:
      - appmysql
networks:
  appmysql:
    driver: bridge

Dockerfile

# Start from golang base image
FROM golang:1.14-alpine3.11 AS builder

#ENV GO111MODULE=on

# Add Maintainer info
LABEL maintainer="Rezwanul Haque <rezwanul.cse@gmail.com>"

# Install git.
# Git is required for fetching the dependencies.
RUN apk update && apk add --no-cache git

# Configure the repo url
ENV REPO_URL=github.com/Rezwanul-Haque/ID-Service

# Setup our $GOPATH
ENV GOPATH=/app

# Set the current working directory inside the container
ENV APP_PATH=$GOPATH/src/$REPO_URL
ENV WORKPATH=$APP_PATH/src/
COPY src $WORKPATH
WORKDIR $WORKPATH

# Copy go mod and sum files
#COPY go.mod .
#COPY go.sum .

# Download all dependencies. Dependencies will be cached if the go.mod and the go.sum files are not changed
RUN go mod download

# Build the Go app
RUN CGO_ENABLED=0 GOOS=linux go build -o id-service .

# Start a new stage from scratch
#FROM alpine:latest
#
#RUN ls bin/
## Copy the Pre-built binary file from the previous stage. Observe we also copied the .env file
#COPY --from=builder /app/id-service .
#COPY --from=builder /app/.env .
#
# Expose port 7001 to the outside world
EXPOSE 7001

# Command to run the executable
ENTRYPOINT ["./id-service"]

db.go

var (
	Client *sql.DB

	username = os.Getenv("MYSQL_IDS_USERNAME")
	password = os.Getenv("MYSQL_IDS_PASSWORD")
	host     = os.Getenv("MYSQL_IDS_HOST")
	port     = os.Getenv("MYSQL_IDS_PORT")
	schema   = os.Getenv("MYSQL_IDS_SCHEMA")
)

func init() {
	dataSourceName := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8",
		username, password, host, port, schema,
	)
	var err error
	Client, err = sql.Open("mysql", dataSourceName)
	if err != nil {
		logger.Error("connecting to database failed: ", err)
		panic(err)
	}

	if err = Client.Ping(); err != nil {
		panic(err)
	}
	logger.Info("database successfully configured")
}

更多关于使用Docker连接Golang与MySQL的实践指南的实战教程也可以访问 https://www.itying.com/category-94-b0.html

9 回复

你是在宿主机上运行,还是在应用程序容器内运行?

更多关于使用Docker连接Golang与MySQL的实践指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Docker Compose 使你的 mysql 在 DNS 中可通过该名称访问,而从应用程序容器的视角来看,本地主机上并没有数据库在运行。

但是使用以下命令,我可以访问我的Docker容器中的MySQL实例。

mysql -h localhost -P 3307 --protocol=tcp -u root --password='12345678

由于我的Docker容器包含mysql和Id_service两个服务,并且有一个名为“appmysql”的桥接网络,因此为了让id_service访问mysql,我需要使用appmysql网络的IP从app服务访问mysql,这样对吗?

每个Docker容器都是隔离的,你可以将其视为独立的计算机以简化理解。因此,你的应用程序容器中的“localhost”与主机计算机是不同的。

你的主机计算机已配置了将其自身端口映射到MySQL容器端口的设置。

正如我之前提到的,在Compose网络内部,数据库可以通过DNS以名称mysql访问。

你有两个容器。每个服务一个。

每个服务都可以通过其服务名称在DNS下访问。只需在你的Go应用程序中使用 mysql 作为数据库的主机。

必须在 my.cnf 文件中允许远程连接

这已经完成了,否则从主机到容器MySQL的连接将无法实现。

我是Docker的新手,所以我运行了 docker-compose up -d 命令,然后检查Docker的应用程序服务(在我的例子中是ID_service)时,它显示连接被拒绝。接着我检查了MySQL服务,发现它正在运行,于是我尝试使用以下命令来检查MySQL是否正常运行:

mysql -h localhost -P 3307 --protocol=tcp -u root --password='12345678

我可以通过主机电脑访问MySQL的3307(公共)端口,该端口已从容器中公开为公共端口。

您必须在 my.cnf 文件中允许远程连接(参见 bind-address 选项),并在您的情况下授予远程用户 ‘root’@’%’ 的访问权限。

nixCraft – 31 Mar 06

如何启用对 MySQL 数据库服务器的远程访问? - nixCraft

出于安全原因,默认情况下禁止远程访问 MySQL 数据库服务器。但是,有时您需要从家庭或 Web 服务器提供对数据库服务器的远程访问。本文将解释如何设置用户帐户并访问…

问题出在Go应用连接MySQL时使用了localhost作为主机地址。在Docker容器网络中,localhost指向容器自身,而不是MySQL容器。需要将主机地址改为Docker Compose网络中的服务名称。

修改.env文件中的MYSQL_IDS_HOST

MYSQL_IDS_HOST=mysql
MYSQL_IDS_PORT=3306

在Docker Compose网络中,服务名称mysql会自动解析为MySQL容器的内部IP地址。同时确保端口使用容器内部的3306而不是映射的3307

另外,确保Go应用的Dockerfile正确复制了.env文件。当前Dockerfile中注释掉了复制.env的步骤,需要取消注释:

FROM alpine:latest

RUN apk add --no-cache ca-certificates

COPY --from=builder /app/src/id-service .
COPY --from=builder /app/src/.env .

EXPOSE 7001

ENTRYPOINT ["./id-service"]

还需要确认MySQL容器允许root用户从网络连接。在MySQL初始化时可能需要设置环境变量:

services:
  mysql:
    image: mysql:latest
    environment:
      MYSQL_ROOT_PASSWORD: 12345678
      MYSQL_DATABASE: ids_db
    volumes:
      - "./db_data:/var/lib/mysql"
    ports:
      - "3307:3306"
    networks:
      - appmysql

在Go代码中,可以添加连接重试逻辑来处理MySQL容器启动延迟:

func init() {
    dataSourceName := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8",
        username, password, host, port, schema,
    )
    
    var err error
    for i := 0; i < 5; i++ {
        Client, err = sql.Open("mysql", dataSourceName)
        if err != nil {
            time.Sleep(2 * time.Second)
            continue
        }
        
        err = Client.Ping()
        if err == nil {
            break
        }
        
        time.Sleep(2 * time.Second)
    }
    
    if err != nil {
        logger.Error("connecting to database failed: ", err)
        panic(err)
    }
    
    logger.Info("database successfully configured")
}

确保Go应用使用了正确的MySQL驱动。在go.mod中需要包含:

require github.com/go-sql-driver/mysql v1.6.0

连接字符串也可以添加额外的参数:

dataSourceName := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&parseTime=True&loc=Local&timeout=30s",
    username, password, host, port, schema,
)
回到顶部