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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 15:23:16