Docker Compose中Go服务的build.context必须指向含go.mod的目录,否则go build会失败;depends_on需配合healthcheck和代码层重试;开发应使用air热重载而非go run;GOPROXY等需通过args注入构建阶段。

docker-compose.yml 里 service 的 build 路径必须指向含 go.mod 的目录
本地开发时,Go 服务镜像不能靠 go build 命令临时编译再塞进基础镜像——那样会丢失模块依赖和版本信息,导致运行时报 module github.com/xxx/yyy is not active 或找不到包。
正确做法是让 Docker 构建过程自己执行 go mod download 和 go build,这就要求 Dockerfile 所在目录(即 build.context)下必须存在 go.mod。否则 go build 会退化为 GOPATH 模式,或直接失败。
-
build:下的context要设为服务代码根目录,不是项目根目录(除非两者重合) - 如果 Go 服务在
./svc/user,context就得是./svc/user,且该路径下要有go.mod - 别用
build: .然后靠dockerfile指定子路径——Docker 构建时仍以context为工作区,go mod读不到上层的go.mod
Go 服务容器启动前必须等数据库就绪,但 depends_on 不够用
depends_on 只检查容器是否 启动成功,不检查端口是否可连、数据库是否 ready。常见现象是 Go 服务启动时 panic:dial tcp 172.20.0.3:5432: connect: connection refused,因为 PostgreSQL 容器还在初始化。
解决方法不是加 restart: on-failure,而是让 Go 服务自己做连接重试,或者加一层健康检查兜底:
立即学习“go语言免费学习笔记(深入)”;
- 在
docker-compose.yml中为 DB service 加healthcheck,例如 PostgreSQL 的pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB} - Go 服务的
depends_on改成condition: service_healthy - Go 代码里仍需实现连接池初始化重试逻辑——
depends_on是 compose 层的约束,不是网络层保障
本地开发时 Go 服务热重载不能靠 go run 直接进容器
把 go run main.go 写进 command 或 entrypoint,看似能热重载,实则每次文件变更都会触发容器重启,丢失状态、打断调试、且无法复用已下载的 go.sum 缓存。
更稳的方式是:容器内只跑一个进程(如 air),由它监听文件变化并管理 go run 生命周期:
- 在
Dockerfile.dev中装air:RUN go install github.com/cosmtrek/air@latest -
command设为air -c .air.toml,配置文件里指定root和bin(避免每次重建二进制) -
volumes映射源码时,排除node_modules、vendor、go/pkg等非必要目录,否则 inotify 事件过多会导致air卡死
Go module proxy 和私有仓库在 compose 环境中容易失效
容器默认没继承宿主机的 GO111MODULE 和 GOPROXY 设置,尤其当使用私有 GitLab/GitHub Enterprise 时,go mod download 会因认证失败卡住或报 invalid version: unknown revision。
关键点不在 go env -w,而在构建阶段环境变量传递和凭证挂载:
- 构建时通过
args注入:GO111MODULE=on GOPROXY=https://proxy.golang.org,direct - 私有模块必须用
git config配置 credential helper,或把~/.netrc挂进构建上下文(注意权限) - 别在
ENV指令里硬编码GOPROXY——不同环境(dev/staging)可能需要不同代理策略
模块缓存路径 /go/pkg/mod 在反复构建时会不断膨胀,建议用 named volume 或 cache_from 优化,但别映射到宿主机——Windows/macOS 的文件系统性能差,反而拖慢 go build。


















