Docker-Compose环境下Golang容器无法连接Postgres容器问题
问题排查与解决
报错信息
panic: dial tcp 192.168.0.2:5432: connect: connection refused
后端连接代码
package database import ( "database/sql" "fmt" _ "github.com/lib/pq" "WebApp/config" ) func Connect() (*sql.DB, error) { psqlInfo := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", config.DB_HOST, config.DB_PORT, config.DB_USER, config.DB_PASSWORD, config.DB_NAME) db, err := sql.Open("postgres", psqlInfo) if err != nil { panic(err) } defer db.Close() err = db.Ping() if err != nil { panic(err) } fmt.Printf("\nSuccessfully connected to database!\n") return db, nil }
Docker Compose 配置
version: '3' services: backend: build: context: ../RestAPI-Golang dockerfile: Dockerfile.dev environment: - DB_USER=username - DB_PASSWORD=password - DB_NAME=default_database - DB_PORT=5432 - DB_HOST=database ports: - "3000:3000" volumes: - ../RestAPI-Golang:/app depends_on: - database database: image: postgres restart: always volumes: - ./db-data/:/var/lib/postgresql/data/ environment: - POSTGRES_USER=username - POSTGRES_PASSWORD=password - POSTGRES_DB=default_database ports: - "5432:5432" db-admin: image: adminer ports: - 8080:8080
核心问题与解决步骤
1. 修复数据库连接函数的致命错误
你的Connect函数里的defer db.Close()会在函数返回时立即关闭数据库连接池,导致返回的*sql.DB实例完全无效。必须移除这行代码,sql.DB是连接池,应该在应用生命周期结束时再关闭,而非创建后立刻关闭:
修改后的代码:
func Connect() (*sql.DB, error) { psqlInfo := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", config.DB_HOST, config.DB_PORT, config.DB_USER, config.DB_PASSWORD, config.DB_NAME) db, err := sql.Open("postgres", psqlInfo) if err != nil { return nil, err } err = db.Ping() if err != nil { db.Close() // 仅连接失败时关闭 return nil, err } fmt.Printf("\nSuccessfully connected to database!\n") return db, nil }
2. 处理数据库启动延迟问题
depends_on仅保证容器启动顺序,不会等待Postgres完成初始化(比如加载数据、准备接受连接)。后端容器启动过快时,Postgres还未就绪,就会触发连接拒绝错误。解决方式:
- 在后端启动脚本中加等待逻辑:比如用
pg_isready工具循环检查,在Dockerfile.dev的启动命令前添加:
(需确保后端容器安装了until pg_isready -h database -p 5432 -U username; do sleep 1; done && ./your-app-binarypostgresql-client包,或用nc替代:until nc -z database 5432; do sleep 1; done) - 在Go代码中加重连逻辑:当Ping失败时重试几次:
import "time" func Connect() (*sql.DB, error) { psqlInfo := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", config.DB_HOST, config.DB_PORT, config.DB_USER, config.DB_PASSWORD, config.DB_NAME) db, err := sql.Open("postgres", psqlInfo) if err != nil { return nil, err } // 重试3次,每次间隔2秒 for i := 0; i < 3; i++ { err = db.Ping() if err == nil { break } time.Sleep(2 * time.Second) } if err != nil { db.Close() return nil, err } fmt.Printf("\nSuccessfully connected to database!\n") return db, nil }
3. 验证容器网络连通性
虽然Docker Compose默认会为所有服务创建统一网络,但可手动验证:
- 进入后端容器:
docker exec -it <你的后端容器名> sh - 执行
ping database,能ping通则DNS解析正常 - 执行
telnet database 5432,能连通则端口开放正常
4. 确认环境变量匹配
你的后端与Postgres的环境变量(用户名、密码、数据库名)完全匹配,这部分无需调整。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

