介绍 Zuul 以改进 CI/CD

关于 Zuul 如何以及为何在 OpenStack 社区的 CI 测试中取代 Jenkins 的简要历史。
118 位读者喜欢这篇文章。
Plumbing tubes in many directions

Jenkins 是一款出色的软件。作为一个执行和自动化引擎,它是你能找到的最好的之一。Jenkins 是无数持续集成 (CI) 系统中的关键组件,这证明了其社区多年来构建的价值。但它只是一个组件。Jenkins 本身不是 CI 系统;它只是为你运行任务。它在这方面做得非常出色,并且有各种内置功能和一个充满活力的插件生态系统来帮助你告诉它要运行什么、何时运行以及在哪里运行。

CI 从根本上来说,是将多个软件开发流程的工作集成到一个连贯的整体中,并尽可能频繁和尽可能少摩擦。Jenkins 本身不了解你的源代码或如何将其合并在一起,也不知道如何为你和你的同事提供建设性的反馈。当然,你可以将其与其他可以执行这些活动的软件粘合在一起,这就是许多 CI 系统集成 Jenkins 的方式。

至少最初,我们对 OpenStack 也是这样做的。

未经测试,即为损坏

2010 年,一个名为 OpenStack 的开源项目社区正在形成。一些被引入以协助协作基础设施的开发人员也参与了一个名为 Drizzle 的免费数据库项目,该社区的一个关键理念是“未经测试,即为损坏”。因此,OpenStack 从一开始就要求对其软件的所有拟议变更进行审查和回归测试,然后才能批准合并到任何源代码存储库的主干中。为此,Hudson(后来分叉形成 Jenkins 项目)被配置为运行测试以执行每一项变更。

安装了一个插件,用于与 Gerrit 代码审查系统交互,在新变更被提议时自动触发作业,并使用审查评论报告反馈,指示它们是成功还是失败。这在今天的标准看来可能很简单,但在当时,对于开源协作来说,这是一项革命性的进步。在 CI 的眼中,OpenStack 上的任何开发人员都不是特殊的,每个人的变更都必须通过这一系列不断增长的测试才能合并——项目称之为“项目门控”的概念。

然而,这种门控思想存在一个新兴的缺陷:为了保证两个不相关的变更不会以功能不兼容的方式更改一块软件,它们必须在合并之前依次逐个测试。即使在当时,OpenStack 的安装和测试也很复杂,并且迅速流行起来。开发人员贡献量的增加以及测试覆盖率的提高意味着,在繁忙时期,根本没有足够的时间来测试通过审查的每一项变更。一些运行时间较长的作业需要将近一个小时才能完成,因此每天可以通过门控的上限大约是二十几个变更。由此产生的合并积压表明需要一种新的解决方案。

Zuul 登场

在 2012 年 5 月的 OpenStack CI 会议期间,CI 团队成员之一 James Blair 宣布,他“一直在研究 Jenkins 作业的推测执行”。推测执行是一种优化,在现代微处理器的流水线中最为常见。就像处理器硬件的类比一样,理论是通过乐观地预测最近批准但尚未完成测试的变更的积极门控结果,随后批准的变更可以并发测试,然后有条件地合并,只要其前任也通过了测试并合并。James 说他为这个智能调度器起了一个名字:Zuul

在此期间,尝试对 Jenkins 的 XML 作业配置执行更好的版本控制所带来的挑战导致了人类可读的基于 YAML 的 Jenkins Job Builder 模板引擎的创建。使用 Jenkins 的 JClouds 插件的有限成功以及使用作业刷新单次使用 Jenkins 从属设备的云映像的繁琐尝试最终导致了 Nodepool 服务的创建。有限的日志存储能力导致团队添加了单独的外部解决方案来组织、服务和索引作业日志,并承担了对已废弃的安全复制协议 (SCP) 插件的维护责任,该插件取代了 Jenkins 开箱即用的不太安全的 FTP 选项。OpenStack 基础设施团队正在慢慢围绕 Jenkins 构建一组服务和实用程序,但开始遇到性能限制。

倍增 Jenkins

到 2013 年中期,Nodepool 不断回收多达 100 台注册到 Jenkins 的虚拟机作为从属设备,但这已不足以跟上不断增长的工作负载。Jenkins 中全局锁的线程争用阻碍了所有突破此阈值的尝试,无论在主服务器上投入了多少处理器能力和内存。该项目有人提议捐赠 Jenkins 从属设备的额外容量,以帮助缓解频繁的作业积压,但这将需要额外的 Jenkins 主服务器。多个主服务器之间高效的工作分配需要一个新的通信通道来调度和协调作业。Zuul 的维护者将 Gearman 作业服务器协议确定为理想的选择,因此他们为 Zuul 配备了新的 geard 服务,并使用自定义 Gearman 客户端插件扩展了 Jenkins。

现在,作业分布在不断增长的 Jenkins 主服务器集合中,不再有任何具有作业活动和结果完整视图的单一仪表板。为了促进这个新的多主服务器世界,Zuul 开发了自己的状态 API 和 WebUI,以及通过 StatsD 协议发出指标的功能。在接下来的几年里,Zuul 稳步地吸收了其用户依赖的更多 CI 功能,而 Jenkins 在系统中的地位也相应下降,并且它正在成为一种负担。OpenStack 早期选择标准化 Python 编程语言;这反映在 Zuul 的开发中,但 Jenkins 及其插件是用 Java 实现的。Zuul 的配置以与 OpenStack 用于模板化其自己的 Jenkins 作业相同的 YAML 序列化格式维护,而 Jenkins 将所有内容都保留在繁琐的 XML 中。这些差异使持续维护变得复杂,并导致来自相关社区的新管理员(他们已开始尝试运行 Zuul)的学习曲线不必要地陡峭。

又一次革命的时机成熟了。

Ansible 的崛起

2016 年初,Zuul 的维护者开始对其不断增长的服务群进行为期一年的雄心勃勃的全面改革,目标是从整体系统设计中消除 Jenkins。到那时,Jenkins 仅充当在从属节点上通过 SSH 运行主要由 shell 脚本组成的作业的管道,提供作业输出的实时流式传输以及将生成的工件复制到长期存储。发现 Ansible 非常适合第一个需求;它专门用于通过 SSH 远程运行命令,用 Python 编写,就像 Zuul 一样,并且还使用 YAML 定义其任务。它甚至具有内置模块,用于团队之前作为定制 Jenkins 插件实现的功能。Ansible 提供了真正的多节点支持,开箱即用,因此相同的 playbook 可以用于模拟和执行复杂的生产部署。不断扩展的第三方模块生态系统填补了所有空白,这与以前 Jenkins 社区的插件非常相似。

一个新的 Zuul 执行器服务填补了以前 Jenkins 主服务器的角色:它处理调度器的 geard 中的待处理请求,通过 Ansible 将它们分派到 Nodepool 管理的临时服务器,然后收集结果和工件以供发布。它还通过经典的 RFC 742 Name/Finger 协议公开了正在进行的构建输出,该协议从 Ansible 命令输出模块的扩展实时流式传输。一旦不再需要将作业限制为 Jenkins 解析器可以理解的内容,Zuul 就可以自由地发展新功能,例如分布式存储库内作业定义,可在项目之间共享,具有继承和安全处理秘密的功能,以及测试驱动作业本身提议的更改的能力。Jenkins 出色地完成了它的使命,但至少对于 Zuul 而言,它的用途终于走到了尽头。

测试未来

Zuul 社区喜欢说,它通过其推测执行的创新应用“测试未来”。 那些令人担忧的日子已经一去不复返了,你不再需要担心对现有作业进行的改进一旦应用于生产环境是否会使其无法正常工作。庞大的中央作业存储库的超负荷审查团队已成为过去。作业被视为软件的一部分,并与其余源代码一起发布,利用 Zuul 的其他功能,如跨存储库依赖项,以便你对一个项目中作业的一部分所做的更改可以在另一个项目中通过提议的作业更改来执行。它甚至会对你的作业更改进行评论,突出显示存在语法问题的特定行,就像另一个代码审查员给你建议一样。

这些是 Zuul 以前梦寐以求的功能,但这些功能需要摆脱 Jenkins 的束缚,以便它可以将作业解析掌握在自己手中。这就是 CI 的未来,而 Zuul 的用户正在体验它。

截至 2019 年初,OpenStack 基金会承认 Zuul 是一个独立的、开放治理的项目,拥有自己的身份和蓬勃发展的社区。如果你对开源 CI 感兴趣,请考虑了解一下。Zuul 下一代的开发一直在进行中,欢迎你参与其中。在 Zuul 网站上了解更多信息。

接下来阅读什么

什么是 CI/CD?

持续集成 (CI) 和持续交付 (CD) 是软件生产中非常常见的术语。但你知道它们真正的含义吗?

标签
Portrait of Jeremy Stanley
Jeremy Stanley 是一位长期的计算机爱好者和技术通才,从事 Unix 和 GNU/Linux 系统管理员工作近三十年,专注于信息安全、互联网服务和数据中心自动化。他是 OpenDev Collaboratory 的 root 管理员,Zuul 项目的维护者,并在 OpenStack 漏洞管理团队任职。

2 条评论

顺便说一句,Mediawiki 也在使用 Zuul CI。截至 2020 年 2 月,Zuul v2 正在使用中,并与 Jenkins 一起使用。

是的,在 Zuul 2.5 中的实验性“Ansible 启动器”(功能于 2016 年 1 月合并)之前,Jenkins 是 Zuul 的硬性依赖项。从 Zuul v3(2018 年 3 月发布)开始,基于 Ansible 的执行器完全取代了 Jenkins。请注意,Zuul v2 的维护与 feature/zuulv3 分支合并到主分支同时停止,因此 v2 的最后一个版本是 2017 年 9 月的 2.6.0。

理论上应该仍然可以设置一个 Ansible playbook,该 playbook 触发 Jenkins 主服务器中预定义的作业,但我不知道有任何 Zuul 用户实际上这样做。

回复 ,作者:Vitaliy (未验证)

© . All rights reserved.