go应用需cgo_enabled=0静态编译、多阶段构建(golang:alpine→scratch)、entrypoint用exec格式确保pid 1、监听地址设为":8080"而非"127.0.0.1:8080"。

Go 应用编译时必须指定 CGO_ENABLED=0
默认开启 CGO 会导致 Go 编译出的二进制依赖系统 libc,无法在精简镜像(如 alpine 或 scratch)中运行,直接报错 no such file or directory 或 not found。
正确做法是在构建阶段禁用 CGO:
CGO_ENABLED=0 go build -a -ldflags '-extldflags "-static"' -o app .- 如果用了 cgo 特性(比如调用 C 库、SQLite、某些 DNS 解析),不能关 CGO,就得用
golang:alpine基础镜像并安装musl-dev,但会增大镜像体积 - 推荐优先重构掉 cgo 依赖,保持静态编译能力
Dockerfile 要分阶段构建,避免泄露构建环境
把 go build 和最终运行分开在两个 stage,能大幅减小镜像体积,并防止源码、GOPATH、测试文件等被意外打包进去。
典型写法:
立即学习“go语言免费学习笔记(深入)”;
FROM golang:1.22-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 go build -o /bin/app . FROM scratch COPY --from=builder /bin/app /bin/app EXPOSE 8080 ENTRYPOINT ["/bin/app"]
- 第一阶段用
golang:alpine(比debian小很多),第二阶段用scratch(真正空镜像) - 别用
FROM golang直接跑应用,那会带完整 Go 环境,镜像动辄 900MB+ - 如果应用需要证书(如 HTTPS 调用外部 API),
scratch没有/etc/ssl/certs,得换alpine或手动 COPY ca-certificates
容器内进程必须是 PID 1,否则信号转发会失效
Go 程序启动后若被 shell 包裹(比如 ENTRYPOINT ["sh", "-c", "./app"]),它就不是 PID 1,docker stop 发的 SIGTERM 不会传给 Go 进程,导致无法优雅退出。
- 始终用 exec 格式:
ENTRYPOINT ["/bin/app"],不要加sh、bash层 - Go 代码里要监听
os.Interrupt和syscall.SIGTERM,做清理后os.Exit(0) - 验证方式:容器运行后执行
docker exec -it <id> ps aux</id>,看/bin/app是否 PID 1
健康检查和端口暴露要匹配实际行为
Go 默认不自动监听 0.0.0.0:8080,如果代码里只写了 http.ListenAndServe(":8080", nil),它其实等价于 127.0.0.1:8080,Docker 外部无法访问。
- 确保监听地址是
":8080"(Go 会自动绑定到所有接口),而不是"127.0.0.1:8080" -
EXPOSE 8080只是文档说明,不实际开放端口;运行时必须加-p 8080:8080 - 健康检查建议用
curl -f http://localhost:8080/health,避免用tcpSocket—— TCP 可连通不代表服务已就绪










