中文内容
隐形重写:Kubernetes 镜像推广器的现代化改造
你从 registry.k8s.io 拉取的每一个容器镜像,都是通过 kpromo(Kubernetes 镜像推广器)到达那里的。它将镜像从暂存注册表复制到生产环境,使用 cosign 对其签名,在 20 多个区域镜像站之间复制签名,并生成 SLSA 来源证明。如果这个工具出故障,就不会发布任何 Kubernetes 版本。在过去几周里,我们从零开始重写了它的核心,删除了 20% 的代码库,使其速度大幅提升,而且没有人注意到。这正是全部目的所在。
一点历史
镜像推广器始于 2018 年底,是 Linus Arver 在 Google 内部发起的一个项目。目标很简单:用一个由社区拥有、基于 GitOps 的工作流,取代将容器镜像复制到 k8s.gcr.io 的手动且由 Googler 把关的流程。推送到暂存注册表,提交一个带有 YAML 清单的 PR,让其经过审查并合并,自动化系统会处理其余工作。KEP-1734 正式确立了这一提案。
2019 年初,代码迁移到了 kubernetes-sigs/k8s-container-image-promoter,并迅速发展。在接下来的几年里,Stephen Augustus 将多个工具(cip、gh2gcs、krel promote-images、promobot-files)整合为一个名为 kpromo 的单一 CLI。该仓库更名为 promo-tools。Adolfo Garcia Veytia(Puerco)增加了 cosign 签名和 SBOM 支持。Tyler Ferrara 构建了漏洞扫描。Carlos Panato 使该项目保持健康且可发布的状态。42 位贡献者在 60 多个版本中提交了约 3,500 次 commit。
它奏效了。但到 2025 年,代码库承载着来自多个 SIG 和子项目七年来增量添加内容的负担。README 直白地写道:你会看到重复代码、实现同一件事的多种技术,以及若干 TODO。
我们需要解决的问题
Kubernetes 核心镜像的生产推广任务通常耗时超过 30 分钟,并且经常因速率限制错误而失败。核心推广逻辑已经发展成一个难以扩展且难以测试的单体,使得添加来源证明或漏洞扫描等新功能变得非常困难。
在 SIG Release 路线图上,有两个工作项已经搁置了一段时间:“重写制品提升器”和“让制品验证更加稳健”。我们曾在 SIG Release 会议和 KubeCon 上讨论过这些事项,而项目看板 #171 上开放的研究 spike 记录了在继续推进之前需要回答的八个问题。
一个用于回答所有问题的 issue
2026 年 2 月,我们创建了 issue #1701(“重写制品提升器流水线”),并在一个跟踪 issue 中回答了全部八个 spike。此次重写被有意分阶段进行,以便每一步都可以独立评审、合并和验证。以下是我们所做的工作:
阶段 1:速率限制(#1702)。重写了速率限制,以通过自适应退避正确地限制所有 registry 操作。
第 2 阶段:接口(#1704)。将注册表和认证操作置于清晰的接口之后,以便它们可以被替换并独立测试。
第 3 阶段:流水线引擎(#1705)。构建了一个流水线引擎,将推广作为一系列不同阶段运行,而不是一个大型函数。
第 4 阶段:来源(#1706)。为暂存镜像添加了 SLSA 来源验证。
第 5 阶段:扫描器和 SBOM(#1709)。添加了漏洞扫描和 SBOM 支持。将默认设置切换为新的流水线引擎。此时我们发布了 v4.2.0,并让其在生产环境中充分运行一段时间后再继续。
阶段 6:将签名与复制拆分(#1713)。将镜像签名与签名复制分离到各自的流水线阶段中,消除了导致大多数生产故障的速率限制争用。
阶段 7:移除旧版流水线(#1712)。彻底删除了旧的代码路径。
阶段 8:移除旧版依赖项(#1716)。删除了审计子系统、已弃用工具和端到端测试基础设施。
阶段 9:删除单体架构(#1718)。移除了旧的单体核心及其支持包。在第 7 到第 9 阶段中删除了数千行代码。
每个阶段都独立发布。v4.3.0 于次日发布,并已完全移除遗留代码。
在新架构就位后,一系列后续改进陆续落地:并行化的 registry 读取(#1736)、针对所有网络操作的重试逻辑(#1742)、防止流水线挂起的逐请求超时(#1763)、HTTP 连接复用(#1759)、本地 registry 集成测试(#1746)、移除已弃用的凭据文件支持(#1758)、重构 attestation 处理以使用 cosign 的 OCI API 并移除已弃用的 SBOM 支持(#1764),以及在 in-toto attestation 框架中注册的专用 promotion record predicate 类型(#1767)。如果没有这次重写所提供的清晰分离,这些改进本会困难得多。v4.4.0 发布了所有这些改进,并默认启用 provenance 生成与验证。
新的流水线
promotion 流水线现在有七个明确分离的阶段:
graph LR
Setup --> Plan --> Provenance --> Validate --> Promote --> Sign --> AttestPhaseWhat it doesSetupValidate options, prewarm TUF cache.PlanParse manifests, read registries, compute which images need promotion.ProvenanceVerify SLSA attestations on staging images.ValidateCheck cosign signatures, exit here for dry runs.PromoteCopy images server-side, preserving digests.SignSign promoted images with keyless cosign.AttestGenerate promotion provenance attestations using a dedicated in-toto predicate type.各阶段按顺序运行,因此每个阶段都能独占完整的速率限制额度。不再存在争用。将签名复制到镜像注册表不再是此流水线的一部分,而是改为作为专用的周期性 Prow 作业运行。
让它更快
架构就绪后,我们转向性能优化。
并行注册表读取(#1736):计划阶段会读取 1,350 个注册表。我们对此进行了并行化处理,计划阶段的耗时从约 20 分钟降至约 2 分钟。
两阶段标签列表(#1761):我们不再检查超过 20 个镜像中的全部 46,000 个镜像组,而是先只检查源仓库。约 57% 的镜像完全没有签名,因为它们是在启用签名之前提升的。我们会完全跳过这些镜像,将 API 调用量大约减少一半。
复制前的源检查(#1727):在遍历给定镜像的所有镜像站之前,我们会先检查签名是否存在于主注册表中。在大多数签名已经完成复制的稳定状态下,这将工作量从约 17 小时减少到约 15 分钟。
按请求设置超时(#1763):我们观察到间歇性挂起,停滞的连接会阻塞流水线超过 9 小时。现在每个网络操作都有自己的超时设置,并且会自动重试暂时性故障。
连接复用(#1759):我们开始在各项操作之间复用 HTTP 连接和认证状态,消除了冗余的令牌协商。这解决了 2023 年以来长期存在的一个请求。
用数字说话
以下是这次重写的总体情况。
- 已合并超过 40 个 PR,发布了 3 个版本(v4.2.0、v4.3.0、v4.4.0)
- 新增超过 10,000 行,删除超过 16,000 行,净减少约 5,000 行(代码库缩小 20%)
- 整体性能大幅提升
- 通过重试逻辑、按请求设置超时以及自适应速率限制,稳健性得到提升
- 关闭了 19 个长期存在的问题
代码库规模缩减了五分之一,同时增加了来源证明、流水线引擎、漏洞扫描集成、并行化操作、重试逻辑、针对本地注册表的集成测试,以及独立的签名复制模式。
没有面向用户的变更
这是一个硬性要求。kpromo cip 命令接受相同的标志,并读取相同的 YAML 清单。post-k8sio-image-promo Prow 作业始终持续正常运行。kubernetes/k8s.io 中的推广清单没有变化。没有人需要更新他们的工作流或配置。
我们在生产环境中及早发现了两个回归问题。一个(#1731)导致注册表键不匹配,使每个镜像都显示为“丢失”,因此没有任何镜像被推广。另一个(#1733)将默认线程数设置为零,阻塞了所有 goroutine。两者都在数小时内修复。分阶段发布策略(v4.2.0 使用新引擎,v4.3.0 移除旧代码)为我们提供了清晰的回滚路径,幸运的是我们从未需要使用它。
接下来是什么
在所有镜像注册表之间复制签名仍然是推广周期中成本最高的部分。Issue #1762 提议通过让 archeio(registry.k8s.io 重定向服务)将签名标签请求路由到单一的规范上游,而不是按区域的后端,从而完全消除这一环节。另一种选择是将签名移到更靠近注册表基础设施本身的位置。这两种方法都需要与 SIG Release 和基础设施团队进一步讨论,但无论哪一种,都将减少每个推广周期中的数千次 API 调用,并进一步简化代码库。
谢谢
这个项目是一项历时七年的社区工作。感谢 Linus、Stephen、Adolfo、Carlos、Ben、Marko、Lauri、Tyler、Arnaud 以及多年来贡献代码、审查和规划的许多其他人。SIG Release 和 Release Engineering 社区为这次重写提供了背景、讨论和耐心,而每个 Kubernetes 版本都依赖于这套基础设施。
如果你想参与进来,请在 Kubernetes Slack 上加入我们的 #release-management,或查看该仓库。
- ← 上一页
- 下一页 →