红帽在宣布将工作重心从 CentOS 切换到 CentOS Stream 后,CentOS 的定位发生了变化。按照 CentOS 官方的说法,它从原来的 RHEL 下游项目变成了一个 Linux 开发平台,社区开发者可以与红帽的开发人员一起为 RHEL 做贡献。如果单纯的从字面上的意思来看,很容易让人理解为 CentOS Stream 就是红帽的开发测试版本,既然是开发和测试的版本,所以不稳定就是必然的。
那么实际情况是什么?
把一个大家可以免费使用的发行版变成了不稳定的开发版本,红帽真的这么不厚道么?我一直想写点东西来以正视听,直到我看到了 Stef Walter(他是红帽雇员,高级总监,软件工程(全球)。负责 RHEL 和 Satellite 的大部分工作:服务、应用堆栈、安装、升级、镜像构建等等)的博文《CentOS Stream 是持续交付的》,我知道我不需要写任何东西了,只需将它翻译成中文就已足够。为了方便大家理解,在原文译文的基础上,我加了很多注释。
通过这篇译文,我们可以找到下面问题的答案:
- 红帽为什么要做这个转变?
- 红帽的开发模式发生了哪些变化?
- Stream和RHEL的异同在哪里?
- Stream稳定么?
持续交付 101:持续做苦难的事情,它们就会变得容易!
从外界看来,我们构建 RHEL(包括 CentOS Linux)的方式十多年来似乎没有什么变化。但在幕后,我们正在进行一场巨大的改革,即如何在不影响客户的情况下开发 RHEL。
我在很多的会议上讲述过这个故事,但有关 CentOS Linux 8 和 CentOS Stream 的公告推动了我在这里讲述这个故事。
三年前(原文发布于 2020 年,因此这里是 2017 年),我们几个在 RHEL 工程部工作的人有了一个想法:如果我们将现代化的开发实践应用到 RHEL 上,比如:持续集成、持续交付、可预测的版本发布节奏……,再加上开源开放实践,比如:早发布、勤发布、拉取请求、分支和代码审查,会怎样?
很明显,不是吗?……不。
Linux 发行版是持续集成和交付的巨大挑战
吸引我进入开源领域的一直是这种集成挑战,有无数不协调的项目,这真的是一个进化的惊人的例子。眯起你的双眼,你几乎可以看到奇怪的生物体,突变、微观世界和自然选择都发生在你的面前。
在过去的 20 年里,我为 100 多个不同的开源项目做了贡献。我的贡献集中在让各个项目无缝地在一起运行,这样用户就可以有一个连贯的使用体验。
Cockpit 项目 就是最明显的例子。我们将大约 95 个独立开发的 Linux API 和组件集成在一起,在 10 多个不同的发行版中以不同的时间表发布,在六年内每隔一周发布一次稳定的版本。
如果说 Linux 是持续集成和交付的巨大挑战,那么我认为 RHEL 绝对是挑战之最:包含上万个未经协调的项目、数千名贡献者、添加额外的架构(比如 kABI)和额外的保障(诸如 10+3 的周期),不断地集成它们,每天交付一个稳定的版本。
我们用梦幻般的(嗯,水汪汪的)眼睛,称这种努力为“随时就绪的 RHEL”。
这项工作开始艰苦地将数千个软件包整合到持续集成中。让很多人震惊的是,在 2017 年的时候我们并没有针对所有的 RHEL 组件进行持续集成。如果这很容易的话,就应该更早做到。
目前,任何进入到 RHEL 中的更新都必须先通过 “持续集成闸门” [注释 1],然后才能进入我们的每夜组合,它会为这些组件运行自动测试。在每夜构建之前,每一个变更都需要经过 RHEL 质量(主要由红帽的 QE 团队)的明确验证。
“随时就绪的 RHEL”的工作现在持续进行并持续交付,你现在可以把它称为 CentOS Stream:RHEL 每夜组合已经在 CentOS Stream 中交付。持续交付的全部目的是为了每一次版本的发布都像之前一样稳定。我们每天都在交付。
我们结束了么?……并没有。
注释 1这里需要简单介绍一下红帽 RHEL 的开发过程的变化,在 CentOS Stream 之前,RHEL 用的还是传统的瀑布式开发模式,当一个合并请求进来,审核通过以后,需要把这个补丁提交到发行版的代码仓库,进入到组合阶段再进行测试,如果失败需要重新进行测试和验证,如下图所示。
在以前这个周期会很长,原因在于 Linux 操作系统会包含数千个软件包,传统模式下,都是每隔固定的时间,将积累下来多次提交的代码集中进行组合。如果顺利通过还好,如果失败就需要将失败的部分返工,而其他的相关的组件需要等失败的组件提交新的补丁以后重新再次进行组合。
因此,其他测试通过的项目的开发者有可能会在很长一段时间后,为了配合测试失败的项目的代码更改,不得不从正在进行的其他工作中抽身出来,重新返工修改自己那部分的代码,开发人员通常都需要并行处理很多工作,打断正在进行的工作是效率非常低下的行为;此外,为了配合测试,有可能还需要重新搭建测试环境;并且,操作系统最终发布之前,多个项目之间也需要相互等待,彼此之间的协调性很差。所有这些都会对发行版的发布进度造成延误,在过去这样一个测试周期往往需要耗时 2~3 个月。
缩短这个周期最好的办法就是在补丁进入发行版代码库之前就完成补丁的测试工作,测试通过以后再提交到发行版代码仓库,完成发行版整体的组合工作和确认。在这种模式下,测试范围缩小到具体的某个软件包,也只会涉及到与之有关的开发人员,这就大大的提升了工作效率。
在对操作系统进行持续集成的关键在于,需要有一种控制机制,能够鉴别每个项目提交的代码在运行了 CI 系统的测试以后,能够根据测试结果判断这些代码是否可以进入到最终的发行版代码树中。这就是闸门的作用,当测试失败的时候,CI 系统可以防止这个中断影响到其他的包。它就像是机场或者火车站的验票口一样,只有持有有效票据的人才可以通过,对于代码来说,通过闸门的唯一途径就是拿到通过 CI 测试的结果。之后,就可以入库并进入到镜像打包的阶段。
基于测试结果,闸门可以根据开发者事先的定义,引导通过测试的代码进入到不同的代码仓库,比如测试版代码仓库或者是稳定版代码仓库。
以 RHEL 8 为例,已经实现了大部分操作系统的构建打包过程的 CI/CD。如下图所示,在图中实线是还需要人手工参与的部分,而虚线是已经实现了自动化的部分。因此以 CI/CD 的方式开发操作系统是“随时就绪的 RHEL”的基础。
对多数人来说,CentOS Stream 已经和 RHEL 一样稳定!
但是这里的挑战是空前的,RHEL 的工程师们已经意识到了这一点。不同团队在整合 RHEL 的工作方式上与上游社区本身一样多样化,然而因为有这么多人在一起迭代来实现这个目标的不同方面,我相信我们是可以实现持续交付的。
图片由 CC-SA 授权
这是版本 8 和 9 的交付流程:可以看到左侧是 Fedora 的发布。这张图说明了 CentOS Stream 是如何等同于 RHEL X.Y 版本的[注释 2]。从技术上来讲,CentOS Stream 和 RHEL 的更新是编译自同一套源代码的两套二进制软件包。因此,当且仅当一个更新被发布到 RHEL 的每夜构建时,该更新才会被发布到 CentOS Stream 中。因此,RHEL 的每夜构建也是你获得的 CentOS Stream 的更新。一旦我们从 Fedora 构建了分支,我们的开发就会按照 RHEL 的产品开发节奏来进行,即:每一个变更都会经历完整的 RHEL 质量保证的测试流程,每一次变更都要在之前的变更基础之上叠加。当且仅当更新被发布到未发布的 RHEL 的小版本更新中以后,这个更新才会推送到 CentOS Stream 中。这些更新就是红帽客户在未来看到的 RHEL 的勘误更新。
这些变更中的每一个,无论是 Bug 修复还是新增的功能特性,在 CentOS Stream 中落地之前,都会通过自动化测试进行测试,并通过 QE 流程进行验证。
CentOS Stream 中唯一不能直接和立即看到的是我们在已经发布的 RHEL 的小版本扩展升级(EUS)[注释 3]中做的工作(即上图中显示的 “RHEL X.Y Errata”)。这项工作通常是在 NDA 下完成的,是禁止公开的[注释 4],或者是已经在 CentOS Stream 中被向后移植的代码工作[注释 5]。
注释 2下图是 CentOS Stream 和 RHEL 的开发流程,两个发行版的源代码都是来自同一个源代码库。当有拉取请求进来,被项目负责人批准以后,会分别进到 CentOS Stream 和 RHEL 的发行版代码库;触发构建操作,根据不同的版本打上不同的标签,CentOS Stream 会被打上
koji
标签,而 RHEL 会打上brew
标签;同样的代码只有同时通过了 CentOS Stream 和 RHEL 的测试流程,闸门才会开启并让代码进入到下一个候选环节;在候选环节会做代码的验证,同样的,CentOS Stream 和 RHEL 中的代码都必须通过验证,才可以进入到发行版的组合阶段。因此,从开发流程上看 CentOS Stream 和 RHEL,虽然可能在测试用例和规则上不同,但是考核的标准是“与”的关系,因此稳定性是一致的。
注释 3在 RHEL 的每一个大版本中会有很多小的升级版本,比如 RHEL 7、RHEL 8 是大版本,而对应的像 RHEL 7.6、RHEL 8.4 这样的是 7 系列和 8 系列里对应的小版本。红帽内部称这种小版本为 RHEL X.Y。
在默认情况下,如果我们通过红帽官网在线升级以后,永远只能是获得大版本里最新的版本。比如,你现在有台系统的版本是 RHEL 8.2,而假设现在 8 系列最新的版本是 8.5,那么通过
dnf update
升级以后,你被升级的系统版本将是 8.5。这通常不会有什么问题,然而对于一些有特殊要求的客户,比如有版本基线管理要求的,或者是出于特殊的兼容性稳定性考虑的客户,是不希望看到操作系统版本因为打补丁发生变化的。针对这种需求,红帽有 EUS Addon 服务(即文中提到的 RHEL X.Y Errata,在红帽也被称为 RHEL X.Y.Z),如果有了 EUS 订阅,就可以把系统注册到某个小的升级版本,比如 RHEL8.2,因为 EUS 的生命周期是小版本发布之后维持 2 年,从下图可以看到,在 2022 年年中以前,RHEL 8.2 EUS 的生命周期结束之前,系统通过网络在线升级是可以一直停留在 RHEL8.2 这个版本的,之后你可以选择跳跃到下一个 EUS 版本,比如 8.6。
针对 EUS,红帽会向后移植主线版本中的红帽定义的级别为关键和重要的安全勘误公告(RHSA),以及红帽选择的优先级为“紧急”的程序漏洞修复公告(RHBA)。其他勘误公告可根据具体情况发布。
因为 CentOS Stream 只有一个主线版本,没有小的升级版本,也没有 EUS 的服务,因此作者在其博文中说:“Stream 中唯一不能直接和立即看到的是我们在已经发布的 RHEL 的小版本扩展升级(EUS)中做的工作(即上图中显示 RHEL X.Y Errata)”。
注释 4关于上文中提到的“不能被立即查看需要签署 NDA 以及禁止公开”的原因在于:修复安全相关等问题时,如果这些漏洞是社区未知的漏洞,出于对最终用户的保护,在修复补丁正式发布之前,红帽不会公开相关信息,以避免被别有用心的人利用。这些信息在红帽正式发布补丁之前,只能在和红帽签署了 NDA 的合作伙伴内部共享,在补丁发布之后红帽会公开补丁的详细内容。
注释 5对于 RHEL 的小版本,在下一个小的升级版本发布之前会作为主线与 CentOS Stream 保持一致,但是当新的小版本发布以后,如果上一个小版本属于有 EUS 服务支持的,那么从这个时间点开始,上一个小版本将进入到 EUS 代码分支中。
以 RHEL 8.2 为例,它在 2020 年年底进入到 EUS 阶段。假设 2021 年在 CentOS Stream 8 和 RHEL 8 中向后移植了一个上游社区的一个重要更新,红帽也会将这个更新向后移植到 8.2,但是 8.2 的这个向后移植的操作在 CentOS Stream 中就体现不出来了,或者说从 CI 角度来看,这个向后移植操作与 CentOS Stream 的 CI 没有直接关系。
CentOS Stream 旨在和 RHEL 一样稳定,这是持续交付的基础!
但是,即使是 RHEL 发布的产品也不是完全稳定的。早在 2020 年 7 月,RHEL(和 CentOS)修复了“boot hole”漏洞,这个修复的结果比这个 CVE 本身 糟糕的多:它导致了许多系统无法启动。
因此,我们不仅在再加工上游组件上投入了时间,还调整了流程(译者注:即指 CI/CD 流程),以确保这种情况不会再次发生。并不断梳理,迭代。
虽然我没有参与结束 CentOS Linux 8 生命周期结束的决定,但我致力于努力推动 CentOS Stream 的发展。更重要的是,它使得 RHEL 开发过程开放给公众:在这里,我们可以与整个生态系统一起应对这一令人兴奋的持续集成和交付的挑战。
将一个产品开源是很难的,但是我们取得了惊人的进步。截止目前为止,我们已经将 RHEL 的开发过程与 Fedora 保持一致,将 RHEL 的所有实际的源代码放在一个 可读取的位置,让贡献者能够针对 RHEL 的任何部分打开一个 拉取请求,并且可以“早发布”和“勤发布”。
这只是个开始,数以百计的人都在为 CentOS Stream 的改变而努力,同时不遗余力的交付您所期待的 RHEL 版本。
CentOS Stream 是 RHEL 稳定可靠的持续交付产品。