EulerMaker：构建 openEuler 全场景生态
2022 年末，openEuler Summit 2022 上吴峰光博士正式向开发者披露了 openEuler 统一构建服务 EulerMaker。
openEuler EulerMaker
2022 年末，openEuler Summit 2022 上吴峰光博士正式向开发者披露了 openEuler 统一构建服务 EulerMaker。
![](/data/attachment/album/202301/10/113845z924lascl8a9e339.jpg)


### 引言


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 最新技术特性](/article-15326-1.html)》。
> 
> 
> 


![](/data/attachment/album/202301/10/112113ebs4bsm550d7zamh.jpg)


“要做全场景 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 容器裁剪：



```
env.CC: /usr/bin/musl-gcc -static
env.CFLAGS: -I/usr/musl/include
env.LDFLAGS: -L/usr/musl/lib

buildRequires:
    - musl-gcc

subpackage.redis-server:
    meta.summary: redis-server
    files: %{_bindir}/%{name}-server
```

*redis.yaml 示例*


该示例使用十行 YAML，即可裁剪出 1MB 的 Redis 。


​以下视频演示了分层定制 Redis 的过程：






2、内核定制维护：



```
env.kconfig: CONFIG_BTRFS_FS=y
patchset.my-xxx-improve: xxx.patch
```

*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 已经上线，已经在欧拉社区发挥作用，但是作为一个开源社区的产物，笔者更希望看到它能开源出来，惠及更广大的开源社区，也接受开源社区的批评和贡献。对此，吴峰光博士表示，一定是会开源的，但是目前还需要进一步打磨成熟。对于这样的回应，笔者很认可，毕竟吴峰光博士对开源社区的贡献一向以精益求精而著称。非常期待能早日见到一个强大而完善的统一构建系统开源出来。

