使用 Zuul 改进 CI/CD

简述 Zuul 如何以及为何在 OpenStack 社区中取代 Jenkins 进行 CI 测试。
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 的安装和测试也很复杂,并且迅速流行起来。 不断增长的开发者贡献量加上不断增加的测试覆盖率意味着,在繁忙时期,根本没有足够的时间来测试每个通过审查的更改。 一些运行时间较长的作业需要将近一个小时才能完成,因此可以通过门控的上限大约为每天 20 几个更改。 由此产生的合并积压表明需要一种新的解决方案。

Zuul 登场

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

在此时间范围内,尝试为 Jenkins 的 XML 作业配置执行更好的修订控制所带来的挑战导致创建了基于人类可读的 YAML 的 Jenkins Job Builder 模板引擎。 使用 JClouds 插件进行 Jenkins 的成功有限,并且使用作业刷新单次使用的 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 提供了真正的多节点支持,因此相同的剧本既可以用于模拟和执行复杂的生产部署。 不断扩展的第三方模块生态系统填补了任何空白,就像之前的 Jenkins 社区插件一样。

一个新的 Zuul 执行器服务填补了 Jenkins 主节点的先前角色:它作用于调度程序的 geard 中的待处理请求,通过 Ansible 将它们调度到 Nodepool 管理的临时服务器,然后收集结果和工件以进行发布。 它还通过经典的 RFC 742 名称/指纹协议公开正在进行的构建输出,该协议从 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 的根管理员、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 分支合并到 master 后停止维护,因此最后一个 v2 版本是 2017 年 9 月的 2.6.0。

从理论上讲,仍然可以设置一个 Ansible playbook,在 Jenkins master 中触发预定义的任务,但我不知道是否有任何 Zuul 用户实际这样做。

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

Creative Commons License本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。
© . All rights reserved.