You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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-binary
    
    (需确保后端容器安装了postgresql-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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 03:27:39