Docker化PostgreSQL连接失败:Golang服务主机名解析错误排查
问题描述
使用以下docker-compose.yaml部署PostgreSQL和Golang服务:
services: db: image: postgres ports: - "5432:5432" environment: POSTGRES_USER: user POSTGRES_PASSWORD: password POSTGRES_DB: test networks: [ "test_network" ] app: build: . ports: - "8080:8080" environment: DB_HOST: db DB_USER: user DB_PASSWORD: password DB_NAME: test depends_on: - db networks: [ "test_network" ] networks: test_network: driver: bridge
Golang应用中通过GORM连接数据库的代码:
db, err := gorm.Open(postgres.New(postgres.Config{ DSN: "host=db user=user password=password dbname=test port=5432", PreferSimpleProtocol: true, // disables implicit prepared statement usage }), &gorm.Config{})
执行docker-compose up时出现错误:
[error] failed to initialize database, got error failed to connect to `host=postgres user=user database=test`: hostname resolving error (lookup postgres on 127.0.0.11:53: read udp 127.0.0.1:36942->127.0.0.11:53: i/o timeout) news-api-app-1 | 2023/04/08 07:16:38 Cannot run server, encountered an error whilst initializing - failed to connect to `host=postgres user=user database=test`: hostname resolving error (lookup postgres on 127.0.0.11:53: read udp 127.0.0.1:36942->127.0.0.11:53: i/o timeout)
已知本地通过psql可正常连接数据库,存在两个疑问:
- 代码明确指定
host=db,为何错误显示连接host=postgres? - 如何解决该连接失败问题?
疑问解答
1. 为什么错误显示连接host=postgres?
最可能的原因是Golang服务使用了旧镜像:你修改了代码中的DSN为host=db,但未重新构建镜像,docker-compose up时复用了之前构建的、仍使用host=postgres的旧镜像,导致错误中的目标host与当前代码不符。
此外也需排查代码逻辑:是否存在其他数据库初始化逻辑误用了默认值(部分postgres驱动配置中默认host可能设为postgres),或者环境变量覆盖了硬编码的DSN。
2. 连接失败问题的解决方法
方法1:重新构建并启动服务
确保使用最新代码构建镜像,执行:
docker-compose up --build
方法2:确保数据库服务就绪后再连接
depends_on仅保证db容器启动,不代表PostgreSQL服务已完全就绪(比如还在初始化数据库)。可通过两种方式处理:
- 在docker-compose中添加健康检查:修改
db服务配置,等待PostgreSQL就绪后再启动app:db: image: postgres ports: - "5432:5432" environment: POSTGRES_USER: user POSTGRES_PASSWORD: password POSTGRES_DB: test networks: [ "test_network" ] healthcheck: test: ["CMD-SHELL", "pg_isready -U user -d test"] interval: 5s timeout: 5s retries: 5 app: # ... 其他配置 depends_on: db: condition: service_healthy - 在Golang代码中添加重试逻辑:初始化数据库时循环尝试连接,直到成功或达到重试次数:
import "time" import "log" var db *gorm.DB var err error maxRetries := 5 retryDelay := 2 * time.Second for i := 0; i < maxRetries; i++ { db, err = gorm.Open(postgres.New(postgres.Config{ DSN: "host=db user=user password=password dbname=test port=5432", PreferSimpleProtocol: true, }), &gorm.Config{}) if err == nil { break } log.Printf("Failed to connect to database, retrying in %v... (%d/%d)", retryDelay, i+1, maxRetries) time.Sleep(retryDelay) } if err != nil { log.Fatalf("Cannot run server, encountered an error whilst initializing - %v", err) }
方法3:验证容器网络连通性
如果以上方法无效,进入app容器验证能否解析并连通db服务:
# 进入app容器 docker exec -it news-api-app-1 sh # 尝试ping db服务 ping db # 尝试用psql连接数据库 psql -h db -U user -d test
如果ping不通db,需检查docker-compose中的网络配置,确保两个服务都加入了test_network网络。
内容的提问来源于stack exchange,提问作者cert_perf
相关产品推荐
相关产品推荐

