因为Docker每次构建都从干净环境开始,$GOPATH/pkg/mod不保留,go mod download无法复用缓存;需通过多阶段构建分离下载阶段并显式COPY /go/pkg/mod,配合GOPROXY、GOPRIVATE及精确缓存key确保模块路径一致。

为什么 go mod download 在 CI/CD 中总要重下一遍?
因为默认情况下,Docker 构建每轮都从干净的镜像层开始,GOBIN 和 GOPATH/pkg/mod 都不保留,go mod download 只能重新拉取所有依赖——哪怕代码没变、模块版本没变。
- 根本问题不是网络慢,而是模块缓存没被复用
-
go mod download本身不写缓存到构建上下文外,只写进当前容器的$GOPATH/pkg/mod - Docker 的 layer 缓存对
go mod download毫无帮助:只要go.mod或go.sum文件变动(哪怕只是空格),后续所有 RUN 就失效
用 go mod download + 构建阶段缓存目录最稳
核心思路是:把 $GOPATH/pkg/mod 提前下载好,并作为独立构建阶段的产物,再 COPY 进主构建阶段。这样既绕过 layer 失效问题,又避免反复下载。
- 在
Dockerfile中拆出一个downloader构建阶段,仅执行go mod download - 该阶段固定使用
golang:1.21-alpine(或你项目对应版本),并挂载/go/pkg/mod为tmpfs或直接 COPY 出来 - 主构建阶段用
COPY --from=downloader /go/pkg/mod /go/pkg/mod复用缓存 - 务必确保两个阶段 GOVERSION、GOOS、GOARCH 一致,否则缓存可能被 go runtime 拒绝加载
FROM golang:1.21-alpine AS downloader WORKDIR /app COPY go.mod go.sum ./ RUN go mod download FROM golang:1.21-alpine WORKDIR /app COPY --from=downloader /go/pkg/mod /go/pkg/mod COPY . . RUN go build -o myapp .
CI 环境中别依赖本地 ~/.cache/go-build
CI runner 是临时实例,~/.cache/go-build 不跨 job 存在,且它缓的是编译对象,不是模块源码——对 go mod download 无效。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
-
go build -i已废弃,-i不再影响模块下载行为 - 某些 CI 声称“缓存 GOPATH”实则只缓
$HOME/go,而 Go 1.14+ 默认使用$GOCACHE和$GOPATH/pkg/mod两处,后者才是模块缓存主路径 - GitHub Actions 推荐用
actions/cache缓存~/go/pkg/mod,但必须配合 key 精确匹配go.modhash,否则容易错用旧缓存
私有模块代理没配对,缓存就形同虚设
如果项目用了 git.company.com/internal/lib 这类私有模块,但没配置 GO_PROXY 或 GOPRIVATE,go mod download 会跳过代理直连,不仅慢,还无法被任何缓存机制命中。
立即学习“go语言免费学习笔记(深入)”;
- 必须设置
GOPRIVATE=git.company.com/*,否则 go 工具链默认认为它是公开模块,强制走 proxy -
GO_PROXY=https://proxy.golang.org,direct中的direct是 fallback,但私有域名若未列入GOPRIVATE,就会被 proxy 拦截并 404 - 企业级做法是在 CI 中部署
athens或goproxy.cn私有代理,并统一设GO_PROXY=http://athens:3000
缓存生效的前提,是所有模块请求都走同一套解析路径。路径一乱,缓存就断了——这不是速度问题,是路径一致性问题。

















