go modules 缓存在ci/cd中常失效,因默认不复用本地缓存、容器环境干净、go.sum缺失或不匹配导致校验失败,且需显式设go111module=on、用go mod download预热、正确配置gomodcache路径及goprivate。

Go Modules 缓存为什么在 CI/CD 里经常失效
因为默认 go build 不会复用本地 $GOPATH/pkg/mod,而多数 CI 环境是干净容器或临时工作区,每次从零下载所有 module。更关键的是:即使你挂载了缓存目录,go 命令本身不会自动校验远程模块哈希一致性,它依赖 go.sum 和 GO111MODULE=on 的严格模式——一旦 go.sum 缺失或不匹配,就会重新 fetch 并报错 verifying github.com/xxx@v1.2.3: checksum mismatch。
- CI 环境必须显式设置
GO111MODULE=on(旧版 Go 1.11+ 默认开启,但某些镜像仍关着) -
go mod download是唯一安全的预热命令,go build或go test可能跳过部分模块,导致缓存不完整 - 缓存路径必须是
$GOMODCACHE(Go 1.14+ 推荐),不是$GOPATH/pkg/mod—— 后者在多版本 Go 下行为不一致 - Git 仓库若没提交
go.sum,CI 中go mod download会失败,因为无法验证签名
GitHub Actions 中正确配置 Go module 缓存
GitHub Actions 的 actions/cache 对 Go 模块有效,但必须和 go mod download 配合使用,不能只缓存构建产物。
- 缓存 key 要包含
go.sum的哈希,例如:go-mod-v2-${{ hashFiles('**/go.sum') }},否则依赖更新后缓存命中却内容过期 - 缓存路径写成
${{ env.GOMODCACHE }}(不是硬编码~/go/pkg/mod),并在 job 开头用env: { GOMODCACHE: /home/runner/go/pkg/mod }显式声明 - 务必在缓存 restore 后、运行测试前执行
go mod download -x(-x用于调试是否真从缓存读取),它会触发校验并填充缺失模块 - 避免在
cachestep 里用path: ${{ env.HOME }}/go/pkg/mod—— 不同 Go 版本下该路径可能不同,GOMODCACHE才是权威路径
GitLab CI 里用 vendor + cache 更稳?
不是“更稳”,而是权衡:vendor 把模块打进仓库,绕过网络和校验问题,适合审计强、网络受限场景;但它让 git diff 膨胀、go mod tidy 易出错,且 vendor/ 不包含 replace 指向本地路径的模块。
- 如果选 vendor,CI 里只需
go build -mod=vendor,无需额外缓存逻辑,但每次go mod vendor必须在本地运行并提交变更 - 不要在
.gitlab-ci.yml里自动执行go mod vendor再 commit —— 这会造成 pipeline 修改代码,违反不可变构建原则 - 若用 cache,GitLab 的
cache:key:files: [go.sum]是等效方案,但注意cache:paths必须写成[$GOMODCACHE],且 job 级别要设variables: { GOMODCACHE: "$CI_PROJECT_DIR/.cache/go/pkg/mod" } - vendor 无法解决
replace ../localpkg场景,此时仍需真实 module 缓存 + 正确的go mod edit -replace预处理
缓存失效的三个隐蔽信号
你以为缓存生效了,其实模块正在后台重下——这些现象说明缓存链路断了:
- 日志里出现
Fetching https://proxy.golang.org/xxx/@v/v1.2.3.info或GET https://sum.golang.org/lookup/...,说明go没命中本地缓存,正在走网络 -
go list -m all | wc -l在缓存 restore 前后数量不一致,尤其新增了大量indirect模块 - 构建时间波动大(比如从 30s 跳到 2min),且
go mod download -x输出中反复出现downloading而非verifying或using
最常被忽略的一点:私有模块(如 git.example.com/internal/lib)若没配 GOPRIVATE=git.example.com,Go 会强制走 proxy 和 sumdb,导致缓存完全无效——这个环境变量必须在所有 go 命令前生效,包括 go mod download。










