ci中go依赖缓存失效的主因是缓存被意外绕过或丢弃:如每次构建使用空$gomodcache、误配临时路径、执行go clean -modcache、go.sum损坏或缺失、未配置goproxy导致fallback直连等。

CI 中缓存 Go 依赖包,核心不是“怎么缓存”,而是“别让缓存失效”——go mod download 下载到 $GOMODCACHE 后,只要路径不变、go.sum 完整、不手动清空,后续构建就自动复用。
为什么 CI 构建总在重复下载依赖?
常见原因不是没缓存,而是缓存被意外绕过或丢弃:
- CI job 每次启动干净容器,
$GOMODCACHE(默认$HOME/go/pkg/mod)是空的 - 误配
GOMODCACHE指向临时目录(如/tmp/modcache),每次构建都重来 - 执行了
go clean -modcache—— 这会删光所有已下载模块,白忙一场 -
go.sum被修改、提交时漏掉、或校验失败,导致 go 工具链拒绝复用缓存并重下 - 没设
GOPROXY,部分请求 fallback 到 slowdirect模式,拖慢整体下载,让人误以为“缓存没起作用”
GitHub Actions 中正确复用 $GOMODCACHE
关键是把 $HOME/go/pkg/mod 目录作为缓存项,并用 go.sum 文件内容做缓存 key —— 这能保证依赖变更时自动失效,不变更时直接命中:
- 不用
actions/cache缓存整个$HOME/go,只缓存$HOME/go/pkg/mod - 缓存 key 推荐用
go-mod-${{ hashFiles('**/go.sum') }},比用go.mod更精确(go.sum包含所有间接依赖哈希) - 确保
go mod download在缓存 restore 之后、build 之前运行,避免因缓存未命中而跳过下载 - 如果项目用了私有模块,记得加
GOINSECURE或配置GOPRIVATE,否则代理 fallback 会导致部分模块下载失败
go mod download all 是唯一覆盖完整模块图的方式
只跑 go mod download 可能漏掉 // indirect 依赖,尤其当 go.mod 没及时 tidy 时。CI 中应统一用:
go mod download all
它等价于 go list -m all | xargs go mod download,会拉取所有出现在模块图中的版本(包括 replace 指向远程的、间接 require 的),但注意:
- 不处理
replace到本地路径的模块(它们本就不需要下载) - 不包含
vendor/里的模块(vendor 是另一套机制) - 若某模块在
go.sum中有记录但网络不可达,命令会失败,报错类似:verifying github.com/some/pkg@v1.2.3: checksum mismatch
缓存路径和环境变量容易被忽略的细节
GOMODCACHE 和 GOPATH 不是可有可无的配置项,它们直接影响缓存是否生效:
-
go env GOMODCACHE才是真实缓存路径,它优先于$GOPATH/pkg/mod;如果 CI 脚本里改过GOMODCACHE,要确认该路径被正确缓存 - 不要在 CI 中 unset
GOPATH或设为空 —— 多数 Go 版本仍依赖它推导默认GOMODCACHE -
go clean -modcache是“核按钮”,CI 脚本里出现就是事故源头,除非调试缓存异常,否则一律禁用 - 模块缓存中目录名含校验和(如
github.com/sirupsen/logrus@v1.9.3-0.20230516142758-4a9e2381ee3d),不是简单按@v1.9.3存的,所以手动清理必须带完整后缀
最常被低估的一点:模块缓存(GOMODCACHE)和构建缓存($GOCACHE)完全独立。即使你完美缓存了依赖,若没同时缓存 $GOCACHE,go build 仍要重新编译所有包 —— 两者得一起配,才真正提速。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











