胡萝卜烤地瓜
胡萝卜烤地瓜
发布于 2026-07-25 / 4 阅读
0
0

NO.A8 仓库是根,缓存只是影-制品真源与加速层

⚠️ 法律与用途声明:本文讨论制品仓库(真源)与构建缓存(加速层)的分离设计,基于开源工具(Conan 2 / Artifactory CE),属于经验分享。所有企业名、项目名、内部脚本与路径命名均已脱敏,代码示例中的包名、仓库名、版本号均为占位。读者须遵守所在地区法律法规。


一、先分清:哪个是原件,哪个是复印件

前几篇聊了不少缓存的事——模板缓存、即时构建目录、hardlink 快照。聊多了容易产生一个错觉:缓存好像挺重要的,是不是得供着?

这一篇先把正反面摆清楚:在整套体系里,制品仓库 Artifactory 是原件,所有缓存都只是它的复印件

这个区分听着像废话,但真到了线上,很多团队的毛病就出在分不清原件复印件——缓存和真源搅在一起,删也不敢删、丢也不知道能不能恢复、到底以谁为准谁也说不清。这篇就讲怎么把它们彻底分开,分开之后能得到什么,以及那些"敢删缓存"的操作具体长什么样。

二、原件复印件不分的乱

如果缓存和真源没有清晰边界,几种典型症状:

  • 不敢删缓存。分不清某份包是只存在缓存里、还是真源里也有,删了可能真没了。缓存越攒越大,谁也不敢动。
  • 不知道以谁为准。同一个包,缓存里一份、真源里一份,内容还不一样(缓存可能是旧的或本地改过的)。开发者拉到哪个,全凭路径。
  • 丢了不敢补救。某份缓存坏了,没人敢确定能不能从别处恢复,迟迟不敢动手。

共同根因是:没有一个明确的真源,所有副本都自称真源。没有唯一原件,复印件失去参照,管理必然乱。

三、真源归档了什么:不止是二进制

我们把 Artifactory 这个私有仓立为唯一真源。它归档的不是随便一份二进制,而是一套可追溯的东西——recipe、二进制、源码,三件套。

最关键的是"源码随包归档"。在 Conan 2 里,这通过 recipe 的 exports_sources 实现(脱敏示意):

class PackageRecipe(ConanFile):
    name = "<包名>"
    version = "<版本>"

    # 源码随 recipe 一起归档,上传时打成 conan_sources.tgz
    exports_sources = "src/*", "include/*", "CMakeLists.txt"

这么写之后,conan upload 会在传二进制的同时,把源码也打包成 conan_sources.tgz 一起归档到仓库。效果是:任何一份正式发布的包,都能追到产生它的源码——这个包是从哪份代码编出来的,仓库里有据可查。

真源还带一个利器叫 recipe revision(配方修订号)。同一个包名加版本,每改一次 recipe,revision 就变一次。所以真源里能精确区分"<包名>/<版本> 的第几版配方",不会出现"同名同版本但内容变了,到底以哪个为准"的糊涂账。查真源里有什么,一条命令:

# 列出真源里某包的所有配方与二进制(含 revision)
conan list "<包名>/*:*" -r=<私有仓>

输出里每个条目都挂着 recipe revision 和 package_id,谁是谁、哪一版,一目了然。这就是"唯一原件"该有的样子——不只是有,还能精确说出来有几份、每份长什么样。

四、加速层是什么:为快而生的影子

加速层(各种缓存)的存在理由只有一个:

真源在仓库里,每次构建都从仓库全量拉,慢;所以把常用的包提前放到离构建更近的地方,让构建少等。一个典型的加速层缓存目录长这样(脱敏):

加速层缓存目录/
└── <target>/                 # 按构建目标分目录
    ├── conan2/                # Conan 包缓存(工具链、已固化的依赖)
    ├── ccache/                # C/C++ 编译缓存
    ├── profiles/              # Conan profile
    └── metadata/              # 固化审计日志(谁、什么时候合并进来的)

但加速层天生有个属性:它是派生物,不是源头

派生的意思很直白:缓存里的包,要么是从真源复制来的,要么是某次构建产物合并来的(而那次产物最终也该回流真源)。所以缓存丢了,永远能从真源重新派生。这个"可重建"的属性,是后面所有治理动作的底气。

五、入库这件仪式:从候选到真源

一个包不是构建出来就自动变成真源的。构建产物一开始只是"候选",放在即时构建目录里,谁也不认。要经过一道入库仪式(promote),它才正式进入 Artifactory 这个真源。

这道仪式大体两步(脱敏后的关键动作):

  • 上传到真源:conan upload "<包名>/<版本>" -r=<私有仓> --confirm
  • 把晋升的包合并进加速层缓存(让后续构建能命中):
# 把这次晋升的包从本地缓存导出成归档
conan cache save "<包名>/<版本>:*" -f=<归档文件>
# 再恢复进公共加速层(增量合并,不覆盖已有)
conan cache restore <归档文件>

注意第二步是增量合并,不是整体覆盖——只把这次晋升的包加进缓存,不删已有包、不抹掉别人固化的东西。并发入库时靠一把写锁串行(NO.A3 讲过的读写锁),避免两个 promote 互相踩。合并完还会往 metadata/ 里追加一条审计,记下谁、什么时候、把哪个包合并进来了。

这道仪式的意义是:真源只收被确认过的东西,不收构建出来却没人看过的东西。候选区可以乱(反正用完即弃),但真源必须干净、必须可信。

六、为什么敢大胆清理缓存

把原件和复印件分清之后,一个直接的好处:缓存敢删了

NO.A3 聊过构建缓存的定时治理——每天清、按价值分级清、删得挺狠。凭什么敢?就凭这一篇立的规矩:缓存只是复印件,原件在真源里好好的。

具体的删留边界是这样划的(脱敏清单):

  • 会删(可重建):conan2/(包缓存,能从真源重拉)、ccache/(编译缓存,能重新生成)、各种临时工作区。
  • 会留(不可重建):构建结果记录、产物包归档、源码归档、metadata/ 审计。这些是历史档案,删了找不回。
  • 绝不碰:真源(Artifactory 里的制品)、公共加速层的 metadata/

清理脚本默认只预演(列出要删的候选),必须显式确认才真删;删完还会把对应构建记录的状态更新成"已清理",让登记表如实反映"这份缓存还能不能直接复现"。

反过来,如果缓存和真源不分、或缓存里存着真源没有的唯一副本,谁敢删?磁盘爆了也只能干瞪眼。分清原件复印件,是为了让复印件能被当作可消耗品——这是缓存能被自动化治理的根本前提。

七、几条攒下来的判断

这篇核心就一句话:仓库是根,缓存只是影。展开成几条:

真源必须唯一。一个包只有一个正式归档地,别的都是副本。没有唯一原件,管理必乱。

真源要完整。归档的不只是二进制,还有 recipe 和源码(exports_sources + conan_sources.tgz),加 recipe revision 精确到每一版配方。能追溯到"从哪份代码来",才算真源。

加速层是派生的、可重建的。缓存从真源来,丢了能恢复,所以敢被激进治理。

入库是仪式,不是自动。候选不等于真源,经确认才进真源;真源必须干净可信。

分清原件复印件,是为了让复印件可消耗。缓存敢删,是因为原件在。分不清,就谁都不敢动。

这套分法落地后,最踏实的感觉是:哪天某台机器的缓存全坏了,你不会慌——因为你知道真源还在,重建缓存是几分钟的事。让人不慌的,不是缓存有多可靠,而是知道原件在哪

下一篇,我们专门聊入库那道仪式里的"人工确认":人工晋升门禁——为什么构建成功不等于可以发布,中间那道人工确认为什么省不掉。


参考链接: [1] Conan 2:上传包与制品归档 https://docs.conan.io/2/reference/commands/upload.html

[2] Conan 2:exports_sources 与源码归档(conan_sources.tgz) https://docs.conan.io/2/reference/conanfile/attributes.html

[3] Conan 2:recipe revision 与可追溯性 https://docs.conan.io/2/reference/commands/list.html

[4] Conan 2:cache save / restore(缓存导出合并) https://docs.conan.io/2/reference/commands/cache.html

[5] JFrog Artifactory CE(制品真源仓库) https://jfrog.com/open-source/

[6] Single Source of Truth 概念(Martin Fowler) https://martinfowler.com/bliki/SingleSourceOfTruth.html


评论