题干与适用场景
请设计一个供团队发布和下载软件包、容器镜像或构建制品的仓库。回答应覆盖元数据、二进制 blob、版本标签、权限、可用性、缓存、撤回和审计。
可以先选择一种制品形态,再说明哪些抽象可复用。核心约束是同一内容只存一份、发布不可被部分读取、下载方能验证内容未被替换。
面试官考察点
元数据与内容分离
强回答会把包名、版本、标签、依赖和签名与不可变内容 blob 分开,避免每次下载都扫描大文件。
版本与一致性
候选人应说明语义版本、标签移动、并发发布和删除语义。版本解析错误会让构建不可复现。
分发与成本
要解释分块上传、断点续传、内容寻址、CDN、跨区域复制和垃圾回收,而不是只画一个对象存储。
安全与治理
权限、租户隔离、签名、SBOM、恶意制品扫描、审计和撤回流程必须形成闭环。
回答前需要澄清的问题
- 制品是 npm 类包、OCI 镜像,还是任意构建文件?
- 规模是每天多少发布、下载和总存储量?
- 版本是否允许移动标签,例如 latest?
- 发布后能否覆盖版本或只能追加新版本?
- 是否需要私有租户、代理上游仓库和跨区域容灾?
- 撤回是阻止新下载、删除内容,还是标记风险并保留审计?
30 秒回答框架
“我先设计一个多租户、内容寻址的制品仓库。发布端先上传并校验 blob,再提交不可变 manifest;标签只是在元数据中指向已存在版本,移动标签需要并发条件。下载端先读元数据,再按 digest 从对象存储或 CDN 取 blob,并校验摘要与签名。权限覆盖命名空间和动作,撤回标记风险而不立即删除审计证据。热点制品走 CDN,后台异步复制、扫描和垃圾回收。”
分步骤深入解答
第一步:定义资源模型
资源包括 namespace、package、version、tag、manifest、blob、signature 和 provenance。manifest 只引用 digest、媒体类型和大小,blob 以 digest 做不可变键。
第二步:设计发布流程
客户端先申请上传会话,分块上传到临时空间;服务端校验每块和最终 digest,完成后原子提交 manifest。失败会话过期清理,重复 digest 直接复用已有 blob。
第三步:处理版本与标签
不可变版本一旦发布不能覆盖;tag 可以移动但必须记录操作者、前后指向和条件版本。解析依赖时优先锁定精确版本和 digest,避免 latest 漂移。
第四步:设计下载路径
元数据服务返回 manifest、依赖和签名;blob 服务支持范围请求、ETag 和 CDN。下载器校验 digest,缓存键包含 digest,标签解析结果设置短 TTL。
第五步:扩展与容灾
对象存储承载大文件,元数据存储按 namespace 和 package 分片;跨区域复制 manifest 后再复制 blob,并以复制状态决定新区域是否可读。
第六步:安全与运营
权限采用最小化的读、写、发布、改 tag 和删除动作;扫描恶意内容、生成 SBOM、验证签名和来源证明。撤回把版本置为 blocked,阻止新下载但保留证据,所有动作写审计日志。
高质量示范回答
“我把仓库拆成元数据服务、blob 存储和异步治理任务。客户端先创建分块上传会话,blob 完成摘要校验后,manifest 以事务方式引用这些不可变 digest。版本不可覆盖,tag 移动需要条件版本并记录历史。下载先解析精确版本,再从 CDN 或对象存储按 digest 取内容并校验签名。
热点 blob 用内容寻址缓存和范围请求降低成本;后台做跨区复制、恶意扫描、SBOM 与 provenance 处理。私有 namespace 使用租户级权限和配额。撤回标记 blocked、停止新下载并触发构建系统告警,不删除审计证据。关键指标包括发布成功率、p95 下载延迟、缓存命中率、复制滞后和未授权访问率。”
常见错误
- 让客户端直接改对象存储 → 绕过权限和完整性校验 → 用短期上传会话并由服务端提交 manifest。
- 允许覆盖已发布版本 → 构建不可复现 → 版本不可变,使用新版本修复。
- 把 latest 当永久缓存键 → 内容悄悄漂移 → 按 digest 缓存,标签只做短 TTL 解析。
- 只存二进制不存 manifest → 无法表达依赖和签名 → 保存结构化 manifest 与 provenance。
- 发布后立刻同步所有区域 → 跨区半成品可读 → 先复制 blob 与 manifest,再更新可读状态。
- 删除撤回制品 → 审计和事故复盘丢失 → 标记 blocked,按保留策略异步清理。
- 只做登录鉴权 → 租户间可能越权 → 对 namespace、动作和制品逐层授权。
- 扫描阻塞发布请求 → 上传高峰时延失控 → 先存 quarantine,异步扫描后改变可用状态。
追问及应对
追问一:两个发布者同时移动同一个 tag 怎么办?
使用条件版本或 compare-and-swap;冲突返回当前版本,让客户端重试并显示历史,禁止最后写入静默覆盖。
追问二:如何保证下载到完整内容?
manifest 声明 digest、大小和媒体类型;客户端校验摘要,失败从其他副本重试,服务端记录校验失败率。
追问三:跨区域复制落后时能否发布?
主区域可以接受发布,但将版本标为复制中;目标区域只有 manifest、依赖 blob 和策略达到一致后才宣告可读。
追问四:如何回收重复 blob?
从所有活跃 manifest、签名和保留策略建立引用集合,标记无引用 blob,经过宽限期和并发检查后删除。
追问五:供应链证明如何进入下载决策?
把签名、SBOM 和 provenance 与 manifest 关联;策略引擎按租户、环境和制品风险决定允许下载、隔离或仅告警。
来源一:OCI Distribution Specification
OCI 规范把 manifest、descriptor 和 blob 作为分发核心,并定义 push、pull、digest 与错误语义,为内容寻址仓库的资源模型提供依据。
来源二:npm Registry 元数据
npm Registry 的元数据示例展示版本、dist-tags 与包信息分离,说明标签解析和不可变版本需要不同的一致性与缓存策略。
来源三:SLSA 供应链完整性
Google 对 SLSA 的介绍强调制品来源、可追溯性和防篡改证明;这些信号可与仓库的签名、SBOM、扫描和下载策略组合。