因为Docker缓存依赖指令及输入完全一致才能命中,先COPY go.mod/go.sum再RUN go mod download可使依赖层长期复用;若先COPY全部源码,代码任一改动都会导致依赖下载层缓存失效,每次重建。

为什么 go mod download 要放在 COPY 源码之前
因为 Docker 层级缓存只在指令及其上下文完全相同时才命中。如果先 COPY . . 再 go mod download,只要任意一行代码变动,整个构建上下文就变了,go mod download 这层必然失效——每次都要重下依赖。
正确做法是:只复制 go.mod 和 go.sum,然后立即执行 go mod download。这两个文件变更频率极低,缓存复用率高。
- 必须按顺序写:
COPY go.mod go.sum ./→RUN go mod download - 不能合并成一步,比如
COPY . . && go mod download,这会破坏缓存粒度 - 确保
.dockerignore里没误删go.mod或go.sum,否则 COPY 会失败
GOPROXY 设置不当会导致下载慢或失败
默认 GOPROXY 是 https://proxy.golang.org,direct,但在国内常被阻断或超时。不显式设置代理,go mod download 可能卡住几十秒甚至失败。
推荐在构建阶段开头就设环境变量:
-
ENV GOPROXY=https://goproxy.cn,direct(国内稳定) - 或
ENV GOPROXY=https://proxy.golang.org,direct(海外 CI 推荐) - 避免用
export GOPROXY=...写在RUN里——它只对当前 shell 生效,下一条指令就丢了
BuildKit 的 --mount=type=cache 更适合 CI 场景
本地开发用分层缓存足够,但 CI 环境中每次都是全新构建上下文,Docker 默认层缓存失效。这时要用 BuildKit 的挂载缓存机制,跨构建任务复用 /go/pkg/mod。
启用方式(需 DOCKER_BUILDKIT=1):
- 在
RUN指令中加--mount=type=cache,target=/go/pkg/mod - 例如:
RUN --mount=type=cache,target=/go/pkg/mod go mod download - 注意:不能和
COPY go.mod go.sum合并在同一 RUN 中,否则缓存路径可能被覆盖
go build 阶段误带 CGO_ENABLED=0 会破坏本地包引用
如果你的项目 import 了本地路径(如 ./internal/db),但构建时用了 CGO_ENABLED=0,通常不影响;但如果依赖里有 cgo 包(如 net 在某些系统上),CGO_ENABLED=0 可能导致 go mod download 阶段就报错:“no required module provides package”。
这不是缓存问题,而是模块解析失败。解决方法:
- 确认是否真需要静态链接:若只是本地开发或调试镜像,可暂时去掉
CGO_ENABLED=0 - 若必须禁用 CGO,确保所有依赖都支持纯 Go 实现(比如用
github.com/mattn/go-sqlite3就不行,得换github.com/glebarez/sqlite) -
go mod download阶段不要加任何CGO_*环境变量,它只管下载,不编译
go.sum 文件校验失败、私有仓库认证缺失、或 replace 指向本地路径却没同步进构建上下文,都会让 go mod download 直接退出。这些错误往往不报“缓存失效”,而是直接中断,得盯紧构建日志里第一行失败提示。


















