无形的重写:现代化 Kubernetes 镜像推广工具

你从 registry.k8s.io 拉取的每个容器镜像都是通过 kpromo(Kubernetes 镜像推广工具)完成的。 它将镜像从临时仓库复制到生产环境,使用 cosign 进行签名, 在 20 多个区域镜像间复制签名,并生成 SLSA 来源证明。 如果这个工具出问题,Kubernetes 就无法发布。在过去几周里,我们从零开始重写了它的核心, 删除了 20% 的代码库,使其速度大幅提升,而没有人注意到。这正是我们的目的。

一点历史

镜像推广工具始于 2018 年末,是由 Linus Arver 发起的 Google 内部项目。 目标很简单:用社区拥有的、基于 GitOps 的工作流程取代手动的、由 Google 员工控制的容器镜像复制到 k8s.gcr.io 的过程。 推送到临时仓库,打开一个包含 YAML 清单的 PR,获得审查和合并,自动化处理其余部分。 KEP-1734 将此提议正式化。

2019 年初,代码迁移到 kubernetes-sigs/k8s-container-image-promoter 并快速发展。 在接下来的几年里,Stephen Augustus 将多个工具 (cipgh2gcskrel promote-imagespromobot-files)整合到一个名为 kpromo 的 CLI 中。 仓库更名为 promo-toolsAdolfo Garcia Veytia (Puerco) 添加了 cosign 签名和 SBOM 支持。 Tyler Ferrara 构建了漏洞扫描功能。 Carlos Panato 保持项目健康且可发布的状态。 42 位贡献者在 60 多个版本中做出了约 3,500 次提交。

它运行得很好。但到 2025 年,代码库承载了来自多个 SIG 和子项目七年增量添加的负担。 README 直白地说: 你会看到重复的代码、多种完成相同任务的技术以及多个 TODO。

我们需要解决的问题

Kubernetes 核心镜像的生产推广任务通常需要 30 分钟以上,并且经常因速率限制错误而失败。 核心推广逻辑已经变成一个难以扩展的单体, 难以扩展且难以测试, 使得添加来源证明或漏洞扫描等新功能变得非常困难。

SIG Release 路线图上, 有两个工作项已经搁置了一段时间:"重写制品推广工具"和"使制品验证更健壮"。 我们在 SIG Release 会议和 KubeCon 上讨论过这些问题, 项目看板 #171 上的开放研究问题记录了八个需要回答的问题,然后才能继续。

一个问题解决所有疑问

2026 年 2 月,我们打开了 Issue #1701 ("重写制品推广流水线"),并在一个跟踪 Issue 中回答了所有八个研究问题。 重写是分阶段进行的,以便每个步骤都可以独立审查、合并和验证。以下是我们所做的:

Phase 1: 速率限制 (#1702)。 重写了速率限制,使用自适应退避正确限制所有仓库操作。

Phase 2: 接口 (#1704)。 将仓库和认证操作放在清晰的接口后面,以便可以独立替换和测试。

Phase 3: 流水线引擎 (#1705)。 构建了一个流水线引擎,将推广作为一系列不同阶段运行,而不是一个大函数。

Phase 4: 来源证明 (#1706)。 为临时镜像添加了 SLSA 来源验证。

Phase 5: 扫描器和 SBOM (#1709)。 添加了漏洞扫描和 SBOM 支持。将默认值切换到新的流水线引擎。此时我们发布了 v4.2.0,让它在生产环境中试运行后再继续。

Phase 6: 分离签名和复制 (#1713)。 将镜像签名与签名复制分离到各自的流水线阶段,消除了导致大多数生产失败的速率限制争用。

Phase 7: 移除旧流水线 (#1712)。 完全删除了旧的代码路径。

Phase 8: 移除旧依赖 (#1716)。 删除了审计子系统、已弃用的工具和端到端测试基础设施。

Phase 9: 删除单体 (#1718)。 移除了旧的单体核心及其支持包。在第 7 到第 9 阶段删除了数千行代码。

每个阶段独立发布。第二天发布了 v4.3.0,完全移除了旧代码。

新架构就位后,一系列后续改进得以实现:并行化仓库读取 (#1736)、 所有网络操作的重试逻辑 (#1742)、 防止流水线挂起的每请求超时 (#1763)、 HTTP 连接复用 (#1759)、 本地仓库集成测试 (#1746)、 移除已弃用的凭证文件支持 (#1758)、 重新设计证明处理以使用 cosign 的 OCI API 并移除已弃用的 SBOM 支持 (#1764), 以及在 in-toto 证明框架 中注册的专用推广记录谓词类型 (#1767)。 如果没有重写提供的清晰分离,这些改进会难上加难。 v4.4.0 发布了所有这些改进,并默认启用了来源证明生成和验证。

新的流水线

推广流水线现在有七个清晰分离的阶段:

graph LR
    Setup --> Plan --> Provenance --> Validate --> Promote --> Sign --> Attest
阶段功能
Setup验证选项,预热 TUF 缓存。
Plan解析清单,读取仓库,计算需要推广的镜像。
Provenance验证临时镜像上的 SLSA 证明。
Validate检查 cosign 签名,模拟运行在此退出。
Promote服务端复制镜像,保留摘要。
Sign使用无密钥 cosign 签名推广的镜像。
Attest使用专用的 in-toto 谓词类型生成推广来源证明。

阶段按顺序运行,因此每个阶段都获得对完整速率限制预算的独占访问权。不再有争用。 镜像仓库的签名复制不再是此流水线的一部分,而是作为一个 专门的定期 Prow job 运行。

使其更快

架构就位后,我们转向性能优化。

并行仓库读取 (#1736): 计划阶段读取 1,350 个仓库。我们将其并行化,计划阶段从大约 20 分钟降至约 2 分钟。

两阶段标签列出 (#1761): 不是检查 20 多个镜像上的所有 46,000 个镜像组,我们首先只检查源仓库。 大约 57% 的镜像根本没有签名,因为它们是在签名启用之前推广的。 我们完全跳过这些,将 API 调用减少大约一半。

复制前的源检查 (#1727): 在遍历给定镜像的所有镜像仓库之前,我们首先检查签名是否存在于主仓库上。 在大多数签名已经复制的稳定状态下,这将工作从大约 17 小时减少到约 15 分钟。

每请求超时 (#1763): 我们观察到间歇性挂起,停滞的连接阻塞流水线超过 9 小时。 现在每个网络操作都有自己的超时,临时失败会自动重试。

连接复用 (#1759): 我们开始在操作间复用 HTTP 连接和认证状态,消除了冗余的令牌协商。 这解决了一个长期存在的请求(始于 2023 年)。

数字说明

以下是重写的总体情况。

  • 合并了 40 多个 PR,发布了 3 个版本 (v4.2.0v4.3.0v4.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 提议通过让 archeioregistry.k8s.io 重定向服务)将签名标签请求路由到单个规范上游,而不是每个区域的后端,来完全消除它。 另一个选择是将签名移到更接近仓库基础设施本身的位置。 这两种方法都需要与 SIG Release 和基础设施团队进一步讨论, 但任何一种都会在每个推广周期中减少数千次 API 调用,并进一步简化代码库。

致谢

这个项目是一个跨越七年的社区努力。感谢 LinusStephenAdolfoCarlosBenMarkoLauriTylerArnaud,以及多年来贡献代码、审查和规划的许多其他人。 SIG Release 和发布工程社区为重写每个 Kubernetes 版本都依赖的基础设施提供了背景、讨论和耐心。

如果你想参与,请加入 Kubernetes Slack 上的 #release-management 频道, 或查看 仓库

最后修改 May 26, 2026 at 2:44 PM PST: [zh-cn]Add blog: image-promoter-rewrite (6175861a5c)