正确顺序是先单独copy go.mod和go.sum,立即run go mod download,再copy源码;这样依赖未变时该层缓存有效,避免每次重下依赖,尤其提升ci构建效率。

go mod download 必须在 COPY go.mod 之后立即执行
很多 Dockerfile 把 go.mod 和源码一起 COPY,再 RUN go mod download,结果每次构建都重下所有依赖——因为 go.mod 时间戳变了,Docker 缓存失效。正确顺序是:先单独 COPY go.mod go.sum .,立刻 RUN go mod download,再 COPY . .。这样只要依赖没变,这层缓存就一直有效。
- 不这么做,
go mod download会成为每次构建的瓶颈,尤其在 CI 环境下拉取慢、失败率高 -
go.sum必须一并COPY,否则go mod download会拒绝执行(校验失败) - 如果项目用了私有模块,需提前配置
GO111MODULE=on和GOPROXY,否则仍会 fallback 到 git clone
避免 vendor/ 目录污染最终镜像
即使你本地 go mod vendor 了,也别把 vendor/ 复制进最终镜像——它只会增加几 MB 到几十 MB 体积,且毫无运行时价值。Go 1.14+ 默认忽略 vendor/(除非显式启用 -mod=vendor),而多阶段构建中,构建阶段用的是完整依赖树,运行阶段根本不需要它。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 常见错误:
COPY vendor/ .或COPY . .没配.dockerignore,导致vendor/被带上 - 更稳妥的做法是在
.dockerignore中明确写入vendor/、go.mod、go.sum、**/*.go等非运行必需项 - 如果你真要用
vendor/(比如离线环境),必须在构建阶段用go build -mod=vendor,且确保最终镜像里完全不包含该目录
CGO_ENABLED=0 不只是开关,它决定是否带 libc 依赖
哪怕你用了 scratch 镜像,如果没关 CGO,二进制仍会尝试动态链接 libc(或 musl),在 scratch 里直接报 no such file or directory——这不是找不到可执行文件,而是找不到动态链接器。
- 必须在构建阶段设置
CGO_ENABLED=0,且搭配-ldflags="-s -w"剥离符号和调试信息 -
-a参数不能省:它强制重新编译所有标准库,防止某些包(如net)悄悄绕过静态链接 - 某些标准库行为仍隐式依赖 libc,比如
os/exec启动子进程、net/http的系统 DNS 解析;遇到 panic 时优先查这些调用点
alpine vs scratch:选哪个取决于调试需求
scratch 镜像体积接近 0MB,但连 sh 都没有,出问题只能靠日志或远程调试;alpine:latest 约 5MB,带 apk 和 sh,适合需要临时诊断的场景。
- 生产环境首选
scratch,前提是确认二进制完全静态、无 libc 依赖 - 用
alpine时,务必加RUN apk --no-cache add ca-certificates,否则 HTTPS 请求会因证书缺失失败 - 别用
golang:alpine当最终镜像——它含编译器、git、pkg-config 等,体积超 300MB,纯属浪费
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










