Go服务在Docker Compose中启动失败主因是构建路径错误、depends_on误用及go run热加载不当;需确保build.context含go.mod、DB加healthcheck并配合代码重试、用air热重载、CGO_ENABLED=0静态编译、main中处理SIGTERM。

Go 服务在 Docker Compose 中跑不起来,八成不是代码问题,而是构建路径、依赖加载或启动时机没对上——尤其本地开发阶段,go.mod 找不到、depends_on 误当健康检查、go run 直接塞进容器这三类错误占了九成。
docker-compose.yml 的 build.context 必须包含 go.mod
Docker 构建时以 context 为工作目录,go build 需要读取 go.mod 才能解析模块路径。如果 context 设成项目根目录(比如 .),但 Go 服务实际在 ./svc/auth 下,而 go.mod 又不在项目根目录——那构建就会退化到 GOPATH 模式,报错 module github.com/xxx/yyy is not active 或找不到包。
-
context应设为每个服务自己的代码根目录,例如./svc/auth,且该路径下必须存在go.mod - 不要用
build: .+dockerfile: ./svc/auth/Dockerfile,Docker 不会自动切换工作区去读上层的go.mod - 多服务项目中,
go mod download应在构建阶段执行,而非宿主机提前下载——否则跨平台或不同 Go 版本下容易缓存不一致
depends_on 不能替代数据库就绪检查
depends_on 只确保容器进程已启动,不保证端口可连、服务 ready。PostgreSQL 容器可能还在初始化用户、建库,Go 服务一上来就 dial tcp 172.20.0.3:5432,直接报 connection refused。
- 为 DB service 加
healthcheck,例如 PostgreSQL 用pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB} - Go service 的
depends_on改成condition: service_healthy - Go 代码里仍需实现连接重试逻辑(比如用
github.com/jackc/pgx/v5/pgxpool的MaxRetries或手动轮询),depends_on是 compose 层的弱约束,不是网络保障
本地开发别用 go run 进容器,改用 air + volume 挂载
把 go run main.go 写进 command 或 entrypoint,文件一改就触发容器重启:状态丢失、调试中断、go.sum 缓存无法复用,构建慢得反人类。
使用 Python 脚本下载 Docker 镜像并打包为 tar 文件。用户说“下载镜像”“拉取镜像”“pull xxx”“帮我下载 nginx 镜像”等时触发。支持 SOCKS5 代理直连(推荐)和国内镜像站加速两种模式。
立即学习“go语言免费学习笔记(深入)”;
- 用
air替代go run:在容器内运行air,监听源码变化并热重载二进制 -
Dockerfile第一阶段保留完整构建环境(含air),第二阶段用alpine+air二进制,再挂载源码目录:volumes: ["./svc/auth:/app:cached"] - 避免在容器里
go get或go mod download,这些操作应放在构建阶段完成,否则每次重启都重新拉依赖
多阶段构建中 CGO_ENABLED=0 和基础镜像选择很关键
Go 默认开启 CGO,编译出的二进制依赖 libc,在 scratch 或最小化 alpine 镜像里会 panic: standard_init_linux.go:228: exec user process caused: no such file or directory。
- 构建阶段务必加
CGO_ENABLED=0 GOOS=linux,生成静态链接二进制 - 运行阶段优先选
scratch(零依赖、极小体积),若需调试工具(如curl、sh)再退回到alpine:latest - 用
FROM golang:1.22-alpine AS builder而非golang:1.22,减小构建镜像体积,加速拉取
最常被忽略的一点:所有 Go 服务的 main() 函数里必须处理 SIGTERM 并调用 server.Shutdown(),否则 docker-compose down 会强制 kill,导致连接未关闭、事务中断、日志丢失——这个逻辑不在 Docker 或 Compose 配置里,只能写在 Go 代码里。

















