EulerMaker:构建 openEuler 全场景生态
引言
2022 年末,openEuler Summit 2022 上,中国 Linux 内核圈最有影响力的开发者之一——吴峰光博士做了名为《面向全场景操作系统的构建服务发布》的主题演讲,正式向开发者披露了 openEuler 统一构建服务 EulerMaker。
Linux 中国开源社区就此采访了吴峰光博士,为读者挖掘到一些在峰会上亮相的 EulerMaker 背后的有趣细节。
? 吴峰光博士是著名的 Linux 内核贡献者、华为计算操作系统首席专家、openEuler 社区技术委员会委员。他在 Linux 内核领域上拥有卓越贡献,在 I/O 和内存管理、内核测试服务方面做出了重要的贡献。有关他的故事,可以参阅《新程序员》杂志的专访《吴峰光杀进 Linux 内核》。
openEuler 的全场景
2021 年 9 月,openEuler 宣布将支持全场景,在服务器、云计算之外,还支持编译计算和嵌入式场景,这就引来了很多人的关注,但也有一些怀疑的眼光。因为,服务器和嵌入式分处两端,中间有着巨大的鸿沟。
“30 年前,服务器 OS、嵌入式 OS,界限非常清晰,像是两个村,中间全是农田。但后来 IT 越来越深入千行百业,云、边缘、IoT 兴起,各种场景涌现,发生了交叠。所以我们认为这是一个新的历史机会,来做一个全场景 OS。”吴峰光说。
? 关于多场景的支持和融合,请参考我们之前对欧拉技术委员熊伟的采访:《操作系统专家解读 openEuler 22.09 最新技术特性》。
“要做全场景 OS,就引入了一个需求:原来的构建系统,如何转化为全场景统一构建系统?”
什么是 OS 构建系统?
OS 构建系统以 OS 源代码作为输入,以用户可安装使用的软件仓或OS镜像作为输出。
? 服务器领域的 OBS、嵌入式领域的 Bitbake,是各自领域的代表性构建系统,历史悠久。
为什么需要新的构建系统?
“openEuler 社区一开始是用 OBS 来构建的,”吴峰光介绍说,“OBS 最初由 SUSE 贡献开源,功能强大。但随着使用的深入,我们发现一些复杂的新需求很难在它上面改进,这就对 openEuler 的演进造成了困难。”
就一般的 OS 来说,OBS 构建可能够用;但是当一个操作系统要支持全场景,复杂度就大大增加,对构建系统提出了更严苛的要求。
吴峰光博士对 OBS、Bitbake 做了深入调研。他解释了这些老牌构建系统,为什么满足不了openEuler的需求:
“服务器领域的 OBS 主打能力是什么?几大主流的 Linux 发行版它都支持,比如可以给 Redhat 打包,也可以给 Debian 打包。兼容并包是它的核心设计目标,适应了 Linux 多样化的现状。但我们认为,多样化在早期对 Linux 发展有利,但长期而言,Linux 生态的碎片化是一个需要被解决的问题。”
“嵌入式领域的 Bitbake 采用了面向任务和过程的 DSL 描述语言,这使得它非常灵活强大,但自由度和复杂性过高,以学习曲线陡峭知名。现在流行的理念是如 YAML、JSON 等通用、声明式的配置语言,和函数式编程,以实现低门槛、易理解、可控可重复的构建过程。”
在吴峰光看来,在 30 年后的今天,构建系统有着新的时代目标。
2022 年 3 月,openEuler 团队开始设计新的构建系统 EulerMaker。
EulerMaker 构建系统
OS 构建系统的核心流程是,用户给定一组软件包后,按照包依赖关系,对它们发起并行构建任务。“那么搭一个 Kubernetes 集群,上面叠加一个包构建调度模块,是不是就可以了呢?”
“这里的包依赖管理和调度,的确非常复杂:既有源包的依赖,又有二进制包的依赖,还有构建环境的依赖;既有构建依赖,又有运行依赖,还有传递依赖;成千上万的依赖关系,形成一个巨大的图,要考虑怎么破环,把它变成一个有向无环图(DAG),用于最大化并发调度。随着包构建的推进,新输出的 RPM 包需要成为之后 RPM 包构建的环境,还会提供新的依赖信息,动态更新这个 DAG 图。还要考虑各种包构建的失败情况,多用户并发任务之间的干扰,或者任意机器、模块随时崩溃重启后如何接力,避免单点故障,等等,这需要一整套精巧的架构设计。”
看到这样的难题,可能有工程师大牛们要摩拳擦掌,跃跃欲试了。但是在难题面前,吴峰光不慌不忙,踩了一脚刹车——
“我们先把发动机放一边,追根溯源,回到最初的那一个问题:用户到底需要一辆什么车?”
做架构设计,首先要考虑用户场景,然后推导出功能,最后才能确定数据和流程。设计时全盘考虑了所有的用户需求,数据和流程在未来才会稳定,才不会变来变去。
“我们对用户需求的考虑,真的全面了吗?”
吴峰光继续展开分析:“在 Linux 发展的最初阶段,需求是简单的:只要功能实现了,跑一下 gcc / make 能构建出来,用户能用,构建系统的工作就完成了。那时侯 Linux 社区对测试不重视,也还没有 CI / CD 的概念,测试基本全靠用户踩坑。现在情况就不一样了,时代的要求在提高:开发者期望有质量把控,要做测试,要有一整套的构建测试 CI / CD,要覆盖一整个开发流程,一站式全部搞定,出来的 RPM 包已经是经过测试的、用户能放心用的。这已经被开发者认为是标配,是开发者的正常预期。”
所以,新的构建系统要集成测试流程。
那么,是不是直接集成现在流行的通用 CI / CD 测试工具就可以了呢?
“市面上的 CI / CD 通用测试服务,适合测试上层的应用;而操作系统需要测试的,既有上层软件,又有基础软件;既面向应用开发者,又面向内核开发者,还有软件厂商、硬件厂商、OS 厂商,他们都有独特的测试需求;既要做功能测试,还要做性能测试。这些不是市面上通用 CI / CD 能做的。”
“所以,我们需要一套全栈系统,既能构建,又满足上述所有测试需求。”
早在 2020 年,吴峰光就综合分析上述需求,设计了 Compass-CI。Compass-CI 被设计为一个通用的任务执行系统,可以执行构建、功能测试、性能测试等各类任务。Compass-CI 也被设计为一个异构调度系统,可以调度物理机、虚拟机、容器等各种资源。
“Compass-CI 已经在 2020 年上线服务,在这个基础上补足构建相关的模块,就可以作为 EulerMaker 的后端,服务 openEuler 的构建。”
“当我们可以用一套系统,来服务好业界各方的需求,就会有硬件厂商问我们,能不能远程接入他们的硬件?然后,我们就需要支持工作机远程接入,分布式调度,数据共享,立足云端,连接各类线下机房,x86、ARM 等各类硬件,方便开发者之间、厂商之间、开发者与厂商之间的分布式协作。”
“资源有了,功能有了,开发者来了,也会有很多需求:我打包了,能不能看见进展?出问题了能不能复现当时的环境?能不能登录调试修复?能不能 DIY 验证?这些需求都需要一一满足大家。现在 EulerMaker 的可视化界面可以显示构建的进展,每个包会显示其依赖关系图以及其构建的进度,哪些包已经构建了,哪些包还没有构建,预计还有多少分钟,它前面还有哪几个依赖都一目了然。而且每个开发者都可以有专属队列,可以大大缩短等待时间。”
EulerMaker 怎么解决全场景
一个 OS 怎么支持全场景?
“首先这个 OS 要足够通用。但这还不够。”吴峰光继续说到:“一个通用的 OS,所带的软件往往要求大而全,把常见的功能都编进去。但很多场合有着不同的需求,比如启用一个不常用的功能,或者在嵌入式设备上,因为资源受限,需要把不必要的功能裁减掉。”
一个通用软件,往往会提供一组编译期的定制功能,方便不同场景的开发者和用户针对自己的需求,构建出不同功能组合的二进制程序。
“类似的,当我们说一个 OS 支持全场景,是只需维护一套 OS 源代码,通过源码级 + 镜像级定制,即可构建生成各类场景化的二进制 OS 发布。强大的定制能力,是赋能一套 OS 源码支持全场景的‘究极魔法’。”
吴峰光进一步介绍什么是定制能力:
“最简单的定制能力,体现在软件打包上,就是对用户提供定制选项。一般是把上游软件的可选功能做一个封装,让用户在做包构建的时候可以打开或者关闭。比如 RPM SPEC 文件中通过宏定义了 %{with xxx}
选项,用户就可以通过 rpmbuild --with xxx
来打开 xxx
选项对应的软件功能。”
“然而 SPEC 文件中往往只封装了少量选项,对于没有被 SPEC 维护者封装的上游软件功能,用户就只好自行修改 SPEC 文件来实现定制,这样就很杂乱了。”
“事实上 OS 的用户和场景多种多样,他们需要的定制项,往往远超包维护者所能提供。比如有人想升级版本号,有人想加个补丁,有人想加个编译选项,或者修改编译器。我们需要一种开放式的定制规范,即允许用户在不修改打包文件的情况下,实现对打包文件的定制,且允许定制任意字段,不限于包维护者事先提供的一个封闭选项集合。”
“这时候就会发现 SPEC 文件的定制能力不够用了。”
EulerMaker 怎么解决这个问题呢?
“我们引入了开发者们都很熟悉的 YAML 配置语言,用它来声明式的描述一个软件包。然后允许用户再定义一个 YAML 文件,来选择性覆盖或者修改 OS 软件包 YAML 文件里的任意字段。这样不但实现了开放式定制,而且用户定制选项都可以以 YAML 配置文件的形式,集中存储管理,或者代码化 Git 管理。”
不过,事情并没有这么简单。
“当用户可以非常方便的定制任意字段,随之而来一个风险:很多包字段之间有条件依赖和约束,用户一不小心,就容易在不知情的情况下,破坏一些关联约束。在过去,很多这种约束在 Git 日志里隐式维护的。比如开发者首先修改一个 SPEC 文件里的版本号,同时去掉一个只适用于老版本的补丁,完成对软件包的一次升级。当暴露在定制环境下,这就是一大脆弱性,会造成事实上难以自由定制。”
而 EulerMaker 的解决办法,是把该补丁适用的版本范围,用条件语句显式的写在软件包 YAML 里。“这样用户随便改版本号,都不会出错,其它关联字段会自适应的变化,从而实现定制自由。”
如此一来,定制能力是强大了,那么易用性又如何?
“一般用户要的不是一堆零件,而是套装。成千上万的定制项,小白用户眼花缭乱,不知道拿它们怎么办,他可能只知道我用的是 OrangePI,那有没有对应的一组定制项可以拿来就用的?”
针对这个问题,EulerMaker 的解决思路如下:
“这组定制项,要由这个专业领域的开发者或者厂商,在社区里提供,我们称之为一个定制层。每一种硬件、场景都可以有这样一个个的定制层。这样场景化 OS 的开发任务就简化为,菜单式选择所需的层,像搭积木一样组合,轻松完成 OS 的场景化分层定制。”
以上,就是 EulerMaker 解决全场景的整体思路。最后,吴峰光总结说:
“一个好用的全场景 OS,一定会是一种生态协作的组织形态。首先把各个场景的公共知识下沉到 BaseOS,统一描述,汇聚复杂枯燥的字段间条件依赖,方便在各个场景中复用。然后创建一个个薄薄的场景化定制层,简单描述各场景下“我要什么”的问题。BaseOS + 多样化场景层,成为 openEuler 社区共同维护和提供的公共组件,通过搭积木的方式,让开发者 DIY 菜单式定制,成就一个轻松愉悦的 OS 创造体验。”
为了让读者们对分层定制有一个直观的概念,吴峰光举了两个例子:
1、Redis 容器裁剪:
1 |
|
redis.yaml 示例
该示例使用十行 YAML,即可裁剪出 1MB 的 Redis 。
以下视频演示了分层定制 Redis 的过程:
2、内核定制维护:
1 |
|
kernel.yaml 示例
该示例使用一个 YAML 文件,两行搞定 Linux 内核的定制维护。
“比如您在一家大公司的基础设施团队,需要在 openEuler 基础上定制 Linux 内核,改一下 kconfig,加一个补丁。在过去,您可能需要拷贝一份 kernel.spec
,然后直接在上面修改。这意味着维护上百行的 SPEC 文件,且时不时要从上游回合新的改进,这一过程枯燥而繁琐。现在从 Fork 模式转为搭积木模式,只需维护好一个小小的 kernel.yaml
文件。然后每次拉新的 BaseOS 重新构建,都会自动拿到上游欧拉内核的最新错误修复。这样就很好的降低了开发维护成本,提高了安全性。是一种更加可持续的上下游协同演进方式。”
统一的包格式
在吴峰光的介绍中,我发现了一件令我很感兴趣的事情,就是 openEuler 在探索自己独有的软件包规范。
我们知道,openEuler 现在采用的是业界主流的软件包格式之一:RPM 。这种软件包格式不仅仅被 Redhat Linux 使用,也被 openSUSE、OpenMandriva、Oracle Linux、Tizen 等使用。
而在 openEuler 中,RPM 软件包不仅仅用在我们熟知的服务器、云计算领域,还应用在其它场景中,比如嵌入式。
为了一统软件包的定义,openEuler 采用了新的 YAML 配置语言进行包描述,并接管 RPM 的 SPEC 文件,成为新的开发者界面。吴峰光说,SPEC 文件中采用了大量复杂的宏定义,而 EulerMaker 将这些复杂性隐藏到YAML后面。换言之,openEuler 新的统一构建系统采用的 YAML 配置语言制定了一种更通用、更灵活的包定义。
这是不是代表着 openEuler 会逐渐发展自己的软件包格式?吴峰光表示,在保持兼容性的同时,openEuler 会走出一条自己的路。
别出心裁创建一种新的包格式容易,但是能在兼容既有架构的基础上,又能开拓新的特性,乃至于支持更广泛的场景,这应该很值得期待。
迈向开源开放
经过半年的努力,统一构建系统服务 EulerMaker 已经上线,已经在欧拉社区发挥作用,但是作为一个开源社区的产物,笔者更希望看到它能开源出来,惠及更广大的开源社区,也接受开源社区的批评和贡献。对此,吴峰光博士表示,一定是会开源的,但是目前还需要进一步打磨成熟。对于这样的回应,笔者很认可,毕竟吴峰光博士对开源社区的贡献一向以精益求精而著称。非常期待能早日见到一个强大而完善的统一构建系统开源出来。