Go模块缓存机制本身支持离线构建,但默认行为不自动保障离线可用性;必须执行go mod download all、设GOPROXY=direct和GOSUMDB=off,并验证缓存覆盖所有go.sum条目。

Go模块缓存机制是否足够支撑离线构建
够,但默认行为不自动保障离线可用性。Go 1.13+ 的 go mod download 会把依赖拉到本地 $GOPATH/pkg/mod/cache(或 $GOCACHE 下的模块缓存区),但只缓存你实际用到的版本——如果 go.sum 里有多个校验条目、或 go.mod 中指定了间接依赖(// indirect),而你没显式触发过 go build 或 go test,那些模块可能根本没进缓存。
常见错误现象:go build 在无网时失败,报错 module github.com/some/pkg: reading https://proxy.golang.org/...: failed to fetch,即使之前跑过 go mod download。
- 必须执行
go mod download -x(加-x查看真实下载路径和命令)确认所有require条目都已落地 -
go mod download不递归拉取replace或exclude影响范围外的间接依赖,需配合go list -deps -f '{{.ImportPath}}' ./...验证覆盖度 - 若项目含
replace指向本地路径(如replace example.com/a => ../a),离线时该路径必须存在且可读,否则go build直接报no required module provides package
如何一次性拉全依赖并验证离线可用
核心是让 Go 工具链“看见”所有可能被解析的模块路径,并强制缓存它们。不能只信 go.mod 的静态声明——Go 的模块加载器会在构建时动态解析 import 路径,而某些 vendor 或测试用的临时导入可能未被 go mod graph 捕获。
- 运行
go mod download all(不是.),它会加载all模式下的全部包,比go mod download更彻底 - 紧接着执行
go list -m -f '{{.Path}} {{.Version}}' all 2>/dev/null | sort -u,输出实际缓存的模块列表,人工核对是否包含go.sum中所有条目 - 断网后运行
go build -a -v -work(-work会打印临时工作目录),观察是否卡在 fetch 阶段;若成功,说明缓存完整 - 注意:
go build -a会强制重编译所有依赖,比默认增量构建更严苛,更适合离线验证
离线构建时 GOPROXY 和 GOSUMDB 必须关闭
即使缓存齐全,Go 默认仍会尝试连接代理和校验服务器。这不是“优化选项”,而是默认开启的网络检查行为。
- 必须设置
export GOPROXY=direct(或GOENV=off后设环境变量),否则go命令仍会尝试访问proxy.golang.org - 必须设置
export GOSUMDB=off,否则go build在校验go.sum时会连sum.golang.org,报错failed to fetch sum.golang.org/lookup/... - 这两个变量建议写入构建脚本头部,不要依赖用户本地配置;CI 或离线机器上容易漏设
- 若使用
go env -w持久化,注意GOENV若为auto,则go env -w写入的是$HOME/.config/go/env,离线环境可能无 HOME 权限,直接 export 更可靠
vendor 目录能否替代模块缓存实现离线构建
能,但代价高、维护难,且 Go 官方已弱化支持。自 Go 1.14 起,go build -mod=vendor 仅在存在 vendor 目录时生效,且不会自动更新 vendor 内容。
-
go mod vendor只复制go list -deps结果中的包,不包含test依赖或构建 tag 排除的代码,离线运行go test时可能缺包 - vendor 目录体积通常比模块缓存大 3–5 倍(含重复嵌套的
.git、文档等),同步和清理成本高 - 若项目用了
replace指向非模块路径(如replace example.com/a => /tmp/a),go mod vendor会跳过它,导致 vendor 不完整 - 真正可靠的离线方案是:用
go mod download all+GOPROXY=direct GOSUMDB=off,而非依赖 vendor
缓存路径本身不可移植,但 $GOPATH/pkg/mod/cache 打包带走后,在另一台同架构机器上设好环境变量就能用——这点常被忽略,以为必须重新下载。

















