首页/开发/CI/CD 与发布速查/封闭构建(Hermetic Build)
流水线设计54 条

封闭构建(Hermetic Build)

构建过程不访问网络、依赖全部预先声明并冻结:可复现性、可缓存性与供应链安全的共同基础。

CI/CD 与发布速查开发更新于 2026-10-13

封闭构建是什么

封闭构建要求构建过程不访问外部网络、不依赖未在描述中声明的文件与隐式环境,全部输入在构建前显式声明并冻结。Google 内部的大规模构建系统(Bazel 的前身 Blaze)即以此为核心设计:只有输入完整可列举,缓存键才能覆盖全部输入,构建结果才既稳定又可精确复用;同时它收窄了供应链攻击面——构建期动态拉取依赖是最常见的投毒通道。

基本信息

适用平台Bazel / Nix 等原生支持;其他生态用容器与预取近似
约束构建期间不访问网络,不读取未声明的宿主文件或环境
收益缓存精确、结果稳定可复现、攻击面收窄
降级路径准封闭:先预取依赖(允许联网),再断网离线构建

典型使用场景

  • 可审计发布:所有输入有据可查,产物可被第三方重建
  • 大型单仓:多团队共享构建缓存,输入不封闭则缓存污染风险高
  • 隔离网交付:金融、政企生产网不直连公网源,构建必须离线完成
  • 定位「换台机器就失败」:把隐式的系统依赖逼出来显式声明
  • 供应链治理:阻断构建期直连公网拉包,改走受控镜像源

实践方向

实践方向
1. 依赖以内容摘要引用(而非浮动版本)
2. 构建环境与外部网络隔离
3. 缓存键覆盖全部输入(含工具链版本)
4. 构建日志保留输入清单

预取与离线构建分离

预取与离线构建分离
pip download -r requirements.txt -d vendor/  # 预取阶段:允许联网
pip install --no-index --find-links=vendor/ -r requirements.txt  # 构建阶段:完全离线
npm ci --offline  # 依赖来自本地缓存,构建期不出网
bazel build --nofetch //app:image  # 缺失输入直接报错,而不是联网拉取

输入类别与封闭要求

输入类别封闭要求常见违例
依赖包摘要固定 + 预取到本地构建中直连镜像源下载
工具链版本与哈希固定依赖 runner 预装的 apt 包,版本随镜像漂移
环境变量清单显式声明隐式继承 shell 或宿主环境
宿主文件显式声明为输入读取 HOME 下的缓存或配置
从准封闭入手:先把依赖下载与构建分离(预取允许联网、构建阶段断网),大多数团队在这一步就能拿到大部分收益。

易错点

  • 构建中途动态下载:缓存失效与结果漂移的常见来源
  • 未声明隐式依赖(系统库、环境变量):换台机器即失败,且难以定位
  • 预取与构建阶段环境不一致:预取到 A 版依赖、构建在 B 环境链接,结果仍然漂移
  • 把封闭当一次性工作:新增依赖时忘记同步声明,断网构建时才暴露

数据来源与参考

  • Bazel Docs《Hermeticity》
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌