Docker Compose环境下Golang与PostgreSQL连接被拒绝问题求助
问题描述
我正尝试通过Docker Compose实现Golang CLI应用flow与PostgreSQL的连接,该应用用于预算信息的创建、查看、调整、查询与删除。执行docker-compose up -d后,成功生成flow-budget_planner与postgres两个镜像,但仅启动了PostgresContainer一个容器。
执行以下命令时的情况:
- 运行
docker exec -it <Container-ID> psql -U postgresql可正常进入PostgreSQL终端操作数据库; - 单独运行
docker run -it -e POSTGRES_PASSWORD=password postgres时,收到database system is ready to accept connections提示; - 在Postgres镜像运行时,另开终端执行
docker run -it flow-budget_planner ./flow budget create -c category -a 300,出现连接被拒绝的错误。
项目相关文件如下:
目录结构
├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── cmd │ ├── budget │ │ ├── budget.go │ │ └── handler │ │ ├── adjust.go │ │ ├── create.go │ │ ├── get.go │ │ ├── remove.go │ │ └── view.go │ └── other services │ └── service.go ├── db │ └── budget_db │ ├── connection.go │ └── migrations │ └── 001_create_budget_table.sql ├── docker-compose.yml ├── go.mod ├── go.sum └── internal ├── app │ └── budget │ └── budget.go └── middleware └── env.go
PostgreSQL连接代码
type Variables struct { Host string Port string User string Password string DBName string SSLMode string } func Connection() (*sql.DB, error) { v := Variables{ Host: middleware.LoadEnvVariable("host"), Port: middleware.LoadEnvVariable("port"), User: middleware.LoadEnvVariable("user"), Password: middleware.LoadEnvVariable("password"), DBName: middleware.LoadEnvVariable("dbname"), SSLMode: middleware.LoadEnvVariable("sslmode"), } connectionString := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s", v.Host, v.Port, v.User, v.Password, v.DBName, v.SSLMode) db, err := sql.Open("postgres", connectionString) if err != nil { return nil, err } err = db.Ping() if err != nil { return nil, err } return db, nil }
Dockerfile
FROM golang:1.22.0-alpine3.19 RUN mkdir -p /app WORKDIR /app COPY go.mod /app COPY go.sum /app COPY . /app RUN go mod download RUN go build -o flow . CMD ["./flow", "budget"]
Docker Compose文件
version: "3.8" services: postgres: image: postgres:latest hostname: localhost container_name: PostgresContainer restart: always environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: password POSTGRES_DB: flow ports: - '5432:5432' volumes: - postgres_data:/var/lib/postgresql/data budget_planner: build: context: . dockerfile: Dockerfile depends_on: - postgres environment: DATABASE_URL: postgresql://postgres:password@localhost:5432/flow volumes: postgres_data: driver: local
解决方案
1. 修复budget_planner容器未启动的问题
budget_planner容器未启动的核心原因是默认CMD ["./flow", "budget"]执行后直接退出(CLI应用无持续运行逻辑)。
- 先查看容器日志确认原因:
docker logs flow-budget_planner - 修改Dockerfile的CMD为持续运行命令,让容器保持存活:
之后重新构建镜像并启动:# 替换原CMD CMD ["tail", "-f", "/dev/null"]docker-compose up -d --build
2. 修复数据库连接被拒绝的问题
核心问题:容器间网络寻址错误
Docker Compose中,容器间需通过服务名而非localhost通信,你的Go代码读取的是单独的环境变量而非DATABASE_URL,需同步修正:
步骤1:修正Docker Compose环境变量
修改budget_planner的环境变量,将数据库主机改为PostgreSQL服务名postgres,同时补充Go代码所需的所有变量:
budget_planner: build: context: . dockerfile: Dockerfile depends_on: - postgres environment: host: postgres port: 5432 user: postgres password: password dbname: flow sslmode: disable
步骤2:添加数据库连接重试机制
depends_on仅保证容器启动顺序,不保证PostgreSQL服务就绪,需在Go代码中添加重试逻辑:
import "time" func Connection() (*sql.DB, error) { v := Variables{ Host: middleware.LoadEnvVariable("host"), Port: middleware.LoadEnvVariable("port"), User: middleware.LoadEnvVariable("user"), Password: middleware.LoadEnvVariable("password"), DBName: middleware.LoadEnvVariable("dbname"), SSLMode: middleware.LoadEnvVariable("sslmode"), } connectionString := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s", v.Host, v.Port, v.User, v.Password, v.DBName, v.SSLMode) var db *sql.DB var err error // 重试5次,每次间隔2秒 for i := 0; i < 5; i++ { db, err = sql.Open("postgres", connectionString) if err == nil { err = db.Ping() if err == nil { break } } time.Sleep(2 * time.Second) } if err != nil { return nil, err } return db, nil }
步骤3:正确执行CLI命令
通过Docker Compose进入存活的budget_planner容器执行命令:
docker-compose exec budget_planner ./flow budget create -c category -a 300
3. 其他细节修正
- 修正PostgreSQL登录命令的用户名错误:
psql -U posgresql改为psql -U postgres; - 确保数据库迁移脚本执行:可在CLI命令执行前,手动进入容器运行迁移,或在Go代码中添加自动迁移逻辑。
内容的提问来源于stack exchange,提问作者Kaifah Habibi
相关产品推荐
相关产品推荐

