PostgreSQL容器连不上主因是端口映射与就绪检查未配对:需显式配置ExposedPorts、用wait.ForListeningPort校验就绪、连接串加sslmode=disable、动态取MappedPort、defer container.Terminate(ctx)清理。

PostgreSQL容器连不上?八成是端口和就绪检查没配对
直接用 localhost:5432 去连 testcontainers 启的 PostgreSQL,几乎必失败——不是 Docker 没起来,而是容器 IP 和端口映射、服务启动时序全没对上。
-
ExposedPorts必须显式写[]string{"5432/tcp"},否则 Docker 不做端口映射,MappedPort返回空 -
WaitingFor不能省,更不能只靠time.Sleep:PostgreSQL 日志里出现database system is ready to accept connections才算真就绪,推荐用wait.ForListeningPort("5432/tcp") - 连接字符串里漏掉
sslmode=disable会导致dial error或 context timeout,因为默认启用了 SSL 但容器没配证书 - 别硬写端口,用
container.MappedPort(ctx, "5432")动态取宿主机分配的真实端口
测试跑完容器还在跑?Terminate() 的调用时机很关键
CI 跑几次就提示端口被占用、本地 docker ps 一堆 testcontainers-go-xxx 僵尸容器,根本原因不是忘了清理,而是清理方式错了。
- 必须用
defer container.Terminate(ctx),不是Stop(),也不是手动Remove() -
Terminate()默认会等 10 秒 graceful shutdown,再强制 kill;而Stop()立即返回,不等进程退出,后续Terminate()实际无动作 - 多个容器时,别在
TestMain里统一Terminate所有——万一某个容器启动失败,container是 nil,Terminate()panic;每个测试内单独 defer 更稳 - Linux 下若看到
failed to dial docker endpoint,先确认用户是否在docker组:sudo usermod -aG docker $USER,然后重开终端
初始化 SQL 不执行?不是脚本有问题,是数据目录没清干净
你把 .sql 文件挂进 /docker-entrypoint-initdb.d/,但每次测试都跳过执行——PostgreSQL 只在空数据目录首次启动时才运行 init 脚本。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- testcontainers 默认创建匿名 volume,容器删了,volume 还在;下次同镜像再起,它发现
/var/lib/postgresql/data非空,直接跳过 init - 最简单解法:每次测试前确保
container.Terminate(ctx)成功执行,它会一并删掉关联的匿名 volume - 不想依赖 volume 生命周期?改用
ContainerRequest.Files把 SQL 内容直接写入容器路径,绕过挂载逻辑 - 验证是否生效:容器启动后
docker exec -it <id> ls -l /docker-entrypoint-initdb.d/</id>,再psql -U test -c '\dt'查表
Redis SET 了但 GET 不到?别急着查代码,先看 client 配置和时序
Go 测试里 client.Set() 返回 nil,但紧接着 client.Get() 就返回空,大概率不是 Redis 没存,而是 client 行为或连接状态干扰了结果。
立即学习“go语言免费学习笔记(深入)”;
-
redis.Client默认启用 pipeline 和连接复用,一次Set可能还没刷到服务端就被Get截获了旧连接 - 初始化 client 时加
DisableIndentity: true关 pipeline(注意拼写是DisableIndentity,非Identity),或显式等写入完成:client.Set(ctx, "k", "v", 0).Err() - 读操作前加
time.Sleep(10 * time.Millisecond)仅用于调试;生产级应改用client.Wait(ctx, 1, 100*time.Millisecond)等副本同步(如果启了 replica) - 容器内 Redis 默认只监听
127.0.0.1,宿主机连不上;启动时加Cmd: []string{"redis-server", "--bind", "0.0.0.0:6379", "--port", "6379"}
测试里最容易被忽略的,其实是「容器网络视角」和「Go client 行为」之间的错位——你以为在连 localhost,其实是在连一个动态 IP;你以为 Set 完了就落地了,其实还在 pipeline 缓冲区。这些细节不抠清楚,容器再“真实”,测试也总差一口气。

















