Pungi 添加双内核集成实战
作者: 杨显钊
版本: 1.1
日期: 2021年8月20日
标签: Pungi、双内核、ISO构建、createrepo、Linux发行版
适合人群: Linux发行版维护者、系统集成工程师、DevOps
摘要
本文旨在解决 Pungi 构建 ISO 镜像时无法同时集成两个内核版本的问题。Pungi 默认在 Gather 阶段只保留最高版本的内核包,导致低版本内核被丢弃、无法放入 ISO 源中、无法完成 createrepo 生成仓库元数据。本文通过逐步排查,记录了从发现问题到最终解决的完整过程,最终采用 pkgset_koji_builds + uos-packages.json 的纯配置驱动方案,实现了 kernel-4.18 与 kernel-4.19 双内核共存的 ISO 镜像构建。
一、什么是 Pungi?
Pungi 是 Fedora 社区开发的一款 Linux 发行版 ISO 镜像构建工具,用于将大量 RPM 软件包按照预定义的配置组装成完整的操作系统安装镜像。
Pungi 解决的问题
在 Linux 发行版的制作过程中,面临以下挑战:
- 成千上万个 RPM 包,如何按需筛选和组合?
- 如何生成正确的软件包依赖关系?
- 如何生成
repodata(YUM/DNF 仓库元数据)? - 如何制作可启动的 ISO 镜像?
Pungi 通过 配置文件驱动 的方式,自动化完成上述全部流程。它的核心工作分为四个阶段:
| 阶段 | 英文名 | 作用 |
|---|---|---|
| 1. 收集 | Gather | 从 koji 构建系统或本地仓库拉取 RPM 包,解析依赖 |
| 2. 创建仓库 | Createrepo | 为收集到的 RPM 包生成 repodata 仓库元数据 |
| 3. 构建镜像 | Buildinstall | 构建安装程序(anaconda)、生成启动镜像 |
| 4. 打包 ISO | Createiso | 将所有内容打包成最终的 ISO 镜像文件 |
此外还有一个辅助阶段 ExtraFiles,用于从配置文件中收集额外文件复制到 compose 目录。
本文背景
在基于 CentOS 生态的 Linux 发行版维护过程中,需要制作同时包含 kernel-4.18 和 kernel-4.19 两个版本内核的 ISO 镜像,以便用户在安装时自由选择内核版本。但 Pungi 默认行为只保留最高版本,导致双内核集成遇到一系列问题。本文记录了完整的解决过程。
二、Pungi 无法集成两个版本内核的 ISO
2.1 问题描述
Pungi 无法拉取两个版本的内核,ISO 源中只存在 kernel-4.19 的包。
# 查看 ISO 源中 kernel 相关包,只有 4.19 版本
$ ls /mnt/koji/compose/latest-UnionTechOS-20/compose/BaseOS/x86_64/os/Packages/ | grep '^kernel-'
kernel-4.19.0-91.82.75.uelc20.x86_64.rpm
kernel-abi-whitelists-4.19.0-91.82.75.uelc20.noarch.rpm
kernel-core-4.19.0-91.82.75.uelc20.x86_64.rpm
kernel-cross-headers-4.19.0-91.82.75.uelc20.x86_64.rpm
kernel-debug-4.19.0-91.82.75.uelc20.x86_64.rpm
kernel-debug-core-4.19.0-91.82.75.uelc20.x86_64.rpm
kernel-debug-devel-4.19.0-91.82.75.uelc20.x86_64.rpm
kernel-debug-modules-4.19.0-91.82.75.uelc20.x86_64.rpm
kernel-debug-modules-extra-4.19.0-91.82.75.uelc20.x86_64.rpm
kernel-devel-4.19.0-91.82.75.uelc20.x86_64.rpm
kernel-headers-4.19.0-91.82.75.uelc20.x86_64.rpm
kernel-modules-4.19.0-91.82.75.uelc20.x86_64.rpm
kernel-modules-extra-4.19.0-91.82.75.uelc20.x86_64.rpm
kernel-tools-4.19.0-91.82.75.uelc20.x86_64.rpm
kernel-tools-libs-4.19.0-91.82.75.uelc20.x86_64.rpm
# 可以看到,完全没有 kernel-4.18 相关的包
$ ls /mnt/koji/compose/latest-UnionTechOS-20/compose/BaseOS/x86_64/os/Packages/ | grep 'kernel-4.18'
(无输出)
2.2 问题原因
通过查看 Pungi 源码和相关资料,发现其在 Gather(拉取包) 阶段默认会挑选最高版本的包,低版本内核被自动丢弃。
2.3 解决方法
参考 Pungi 说明文档,发现 Pungi 在拉取包时有一个 ExtraFiles 阶段,此阶段从配置文件中收集额外文件并将其复制到 compose 目录。我们可以通过配置文件的方式添加额外的内核到 ISO 中。
配置文件示例:
# extra_files 配置文件内容
$ cat extra_files.conf
[extra_files]
kernel-4.18/
dir = ./kernel-4.18-packages/
# dir 指定了 kernel 的存放路径(相对路径相对于配置文件本身)
# 上述配置表示:将 ./kernel-4.18-packages/ 目录下的所有文件
# 作为额外文件复制到 compose 目录中
2.4 参考
三、额外添加的 kernel 包没有放入到 ISO 的源里
3.1 问题描述
在 ExtraFiles 配置中添加的内核,并没有集成到 ISO 的源(即 YUM 仓库)中,而是存放在 ${TARGET_DIR}/compose/BaseOS/x86_64/os 目录下。这意味着安装程序无法从源中检索到这些内核包。
3.2 问题原因
Pungi 默认会将 ExtraFiles 中的文件拉取到 ${TARGET_DIR}/compose/BaseOS/${arch}/os。
查看源码 /usr/lib/python3.6/site-packages/pungi/phases/extra_files.py 可知,Pungi 通过 copy_all 函数将 ExtraFiles 中的文件统一拷贝到默认路径,官方并未提供指定目标子目录的配置选项。
3.3 解决方法
修改 ExtraFiles 中 kernel 的拷贝路径。核心思路:
- 判断是否存在 kernel 目录
- 如果有,在 Pungi 处理 ExtraFiles 之前 对 kernel 进行单独处理,将 kernel 放入 Packages 子目录(即 RPM 仓库的标准路径)
- 然后删除 kernel 相关的包,避免 Pungi 重复处理
- 如果没有 kernel 目录,则不做额外处理
这样既不会影响 Pungi 正常处理 ExtraFiles 中的其他文件,也能把额外 kernel 集成到 ISO 中。
具体实现:在 /usr/lib/python3.6/site-packages/pungi/paths.py 中定义了相关路径变量,修改 extra_files.py 中对 kernel 目录的处理逻辑。
# 查看修改前后的差异
$ diff -u /usr/lib/python3.6/site-packages/pungi/phases/extra_files.py.bak \
/usr/lib/python3.6/site-packages/pungi/phases/extra_files.py
--- extra_files.py.bak 2021-08-20 10:00:00.000000000 +0800
+++ extra_files.py 2021-08-20 14:00:00.000000000 +0800
@@ -45,8 +45,22 @@
self.copy_all(config, target_dir)
def copy_all(self, config, target_dir):
+ """拷贝 extra_files 中的所有文件到目标目录"""
for key in config:
src_dir = config[key]['dir']
+ # 判断是否为 kernel 目录,单独处理
+ if 'kernel' in key.lower():
+ # kernel 包需放入 Packages 子目录,才能被 YUM 仓库识别
+ pkg_target = os.path.join(target_dir, 'Packages')
+ if not os.path.exists(pkg_target):
+ os.makedirs(pkg_target)
+ self._copy_files(src_dir, pkg_target)
+ # 拷贝完成后删除 kernel 源目录,避免 Pungi 重复处理
+ shutil.rmtree(src_dir)
+ continue
+
+ # 非 kernel 文件走默认逻辑
self._copy_files(src_dir, target_dir)
def _copy_files(self, src_dir, target_dir):
shutil.copytree(src_dir, target_dir, dirs_exist_ok=True)
3.4 供参考的替代方案
在配置文件中可通过 additional_packages 配置添加额外的 kernel 包。Pungi 在 Gather 的过程中会一并获取 additional_packages 中的包。
局限性: 获取包时默认还是获取最新版本。例如,如果 ISO 里默认集成了 gcc-10.0,additional_packages 中也指定了 gcc,那么只会获取 gcc 的最新包,而不会获取两个版本。可尝试修改 Pungi 获取 additional_packages 包的逻辑来解决此问题。
3.5 参考
四、解决内核未进行 createrepo 的问题
4.1 问题描述
使用以上方法集成双内核时,额外加进去的内核没有进行 createrepo(即未生成 RPM 仓库元数据),导致 YUM/DNF 无法识别这些包。
4.2 问题原因
Pungi 默认没有对 ExtraFiles 部分加进来的文件执行 createrepo 操作。createrepo 是在 Gather 阶段之后、对 Gather 收集到的包统一执行的,ExtraFiles 中的文件不在这个流程中。
4.3 解决方法
最终方案:通过修改配置完成双内核集成,避免直接修改 Pungi 源码。以下以 x86_64 架构的配置为例。
4.3.1 uos.conf 配置
# 指定 koji 构建系统中需要拉取的内核包
pkgset_koji_builds = [
"kernel-4.18.0-305.10.2.uelc20.4",
"kernel-4.19.0-91.82.75.uelc20"
]
# 拉取策略配置
gather_method = {
"^(?!(AppStream|PowerTools|HighAvailability)).*$": {
"comps": "deps"
},
"^(AppStream|PowerTools|HighAvailability|BaseOS)$": "hybrid",
}
4.3.2 uos-packages.json 配置
{
"x86_64": {
"kernel": [
"kernel-4.18.0-305.10.2.uelc20.4.x86_64",
"kernel-4.19.0-91.82.75.uelc20.x86_64",
"kernel-abi-whitelists-4.19.0-91.82.75.uelc20.noarch",
"kernel-core-4.18.0-305.10.2.uelc20.4.x86_64",
"kernel-core-4.19.0-91.82.75.uelc20.x86_64",
"kernel-cross-headers-4.18.0-305.10.2.uelc20.4.x86_64",
"kernel-cross-headers-4.19.0-91.82.75.uelc20.x86_64",
"kernel-debug-4.19.0-91.82.75.uelc20.x86_64",
"kernel-debug-core-4.19.0-91.82.75.uelc20.x86_64",
"kernel-debug-devel-4.19.0-91.82.75.uelc20.x86_64",
"kernel-debug-modules-4.19.0-91.82.75.uelc20.x86_64",
"kernel-debug-modules-extra-4.19.0-91.82.75.uelc20.x86_64",
"kernel-devel-4.18.0-305.10.2.uelc20.4.x86_64",
"kernel-devel-4.19.0-91.82.75.uelc20.x86_64",
"kernel-headers-4.18.0-305.10.2.uelc20.4.x86_64",
"kernel-headers-4.19.0-91.82.75.uelc20.x86_64",
"kernel-ipaclones-internal-4.18.0-305.10.2.uelc20.4.x86_64",
"kernel-modules-4.18.0-305.10.2.uelc20.4.x86_64",
"kernel-modules-4.19.0-91.82.75.uelc20.x86_64",
"kernel-modules-extra-4.18.0-305.10.2.uelc20.4.x86_64",
"kernel-modules-extra-4.19.0-91.82.75.uelc20.x86_64",
"kernel-modules-internal-4.18.0-305.10.2.uelc20.4.x86_64",
"kernel-selftests-internal-4.18.0-305.10.2.uelc20.4.x86_64",
"kernel-tools-4.18.0-305.10.2.uelc20.4.x86_64",
"kernel-tools-4.19.0-91.82.75.uelc20.x86_64",
"kernel-tools-debuginfo-4.18.0-305.10.2.uelc20.4.x86_64",
"kernel-tools-libs-4.18.0-305.10.2.uelc20.4.x86_64",
"kernel-tools-libs-4.19.0-91.82.75.uelc20.x86_64",
"kernel-tools-libs-devel-4.18.0-305.10.2.uelc20.4.x86_64",
"bpftool-4.19.0-91.82.75.uelc20.x86_64",
"bpftool-debuginfo-4.19.0-91.82.75.uelc20.x86_64",
"perf-4.19.0-91.82.75.uelc20.x86_64",
"perf-debuginfo-4.19.0-91.82.75.uelc20.x86_64",
"python3-perf-4.19.0-91.82.75.uelc20.x86_64",
"python3-perf-debuginfo-4.19.0-91.82.75.uelc20.x86_64"
]
}
}
注意: 此处仅列出 x86_64 相关配置,如需集成 ARM 双内核,自行添加对应架构的配置。Pungi 版本请使用 pungi-4.1.38-1。
说明: 以上为最终推荐的纯配置方案。回顾第二节到第四节,我们经历了从 ExtraFiles 配置 → 修改 extra_files.py 源码 → 最终纯配置方案的演进过程。完整的端到端测试步骤见第五节。
4.4 参考
五、测试验证
以下为完整的端到端测试步骤,从配置文件编辑到集成执行再到结果验证。本节配置融合了第四节中的 pkgset_koji_builds + uos-packages.json(解决多版本拉取与 createrepo)以及第二节中的 extra_files(处理额外内核包),是生产环境实际使用的组合配置。
5.1 编辑配置文件
编辑 Pungi 配置文件,指定额外内核包的存放路径。其中 kernel-add-x86_64 目录中存放 kernel/*.rpm(即 kernel-add-x86_64/kernel/*.rpm)。kernel-add-x86_64 目录名可通过配置文件任意修改,但其内部的 kernel 子目录名不可改变。
# 查看 Pungi 配置文件
$ cat /path/to/pungi-custom.conf
# === 双内核集成配置 ===
# 1. 指定 koji 构建中的内核包版本
pkgset_koji_builds = [
"kernel-4.18.0-305.10.2.uelc20.4",
"kernel-4.19.0-91.82.75.uelc20"
]
# 2. extra_files 配置:额外内核包存放路径
[extra_files]
kernel-add-x86_64/
dir = ./kernel-add-x86_64/
# 3. 拉取策略
gather_method = {
"^(?!(AppStream|PowerTools|HighAvailability)).*$": {
"comps": "deps"
},
"^(AppStream|PowerTools|HighAvailability|BaseOS)$": "hybrid",
}
# 4. 自定义包列表见 uos-packages.json
说明: Pungi 配置文件混合了 Python 字典语法(
pkgset_koji_builds、gather_method)和 INI 段落语法([extra_files]),这是 Pungi 原生支持的格式,无需额外转换。
# 确认额外内核包目录结构
$ ls -R ./kernel-add-x86_64/
./kernel-add-x86_64/:
kernel/
./kernel-add-x86_64/kernel/:
kernel-4.18.0-305.10.2.uelc20.4.x86_64.rpm
kernel-core-4.18.0-305.10.2.uelc20.4.x86_64.rpm
kernel-devel-4.18.0-305.10.2.uelc20.4.x86_64.rpm
kernel-headers-4.18.0-305.10.2.uelc20.4.x86_64.rpm
kernel-modules-4.18.0-305.10.2.uelc20.4.x86_64.rpm
kernel-modules-extra-4.18.0-305.10.2.uelc20.4.x86_64.rpm
kernel-tools-4.18.0-305.10.2.uelc20.4.x86_64.rpm
kernel-tools-libs-4.18.0-305.10.2.uelc20.4.x86_64.rpm
5.2 运行 Pungi 开始集成
# 执行 pungi 集成命令
$ pungi-koji --config=/path/to/pungi-custom.conf \
--target-dir=/mnt/koji/compose/xzyang/latest-UnionTechOS-20
# Pungi 执行过程日志(示意)
[INFO] Phase: Init - 初始化完成
[INFO] Phase: Pkgset - 从 koji 拉取包集合
[INFO] Phase: Gather - 收集依赖包
[INFO] Phase: ExtraFiles - 处理额外文件
[INFO] Phase: Createrepo - 生成仓库元数据
[INFO] Phase: Buildinstall - 构建安装程序
[INFO] Phase: Createiso - 生成 ISO 镜像
[INFO] Compose completed successfully!
5.3 验证执行结果
集成完成后,查看 BaseOS/Packages 目录下是否已导入多个版本的内核包:
# 查看集成结果:BaseOS/Packages 下已经导入多个版本的内核包
$ ls /mnt/koji/compose/xzyang/latest-UnionTechOS-20/compose/BaseOS/x86_64/os/Packages/ \
| grep '^kernel-' | sort -V
kernel-4.18.0-305.10.2.uelc20.4.x86_64.rpm
kernel-4.19.0-91.82.75.uelc20.x86_64.rpm
kernel-abi-whitelists-4.19.0-91.82.75.uelc20.noarch.rpm
kernel-core-4.18.0-305.10.2.uelc20.4.x86_64.rpm
kernel-core-4.19.0-91.82.75.uelc20.x86_64.rpm
kernel-cross-headers-4.18.0-305.10.2.uelc20.4.x86_64.rpm
kernel-cross-headers-4.19.0-91.82.75.uelc20.x86_64.rpm
kernel-devel-4.18.0-305.10.2.uelc20.4.x86_64.rpm
kernel-devel-4.19.0-91.82.75.uelc20.x86_64.rpm
kernel-headers-4.18.0-305.10.2.uelc20.4.x86_64.rpm
kernel-headers-4.19.0-91.82.75.uelc20.x86_64.rpm
kernel-modules-4.18.0-305.10.2.uelc20.4.x86_64.rpm
kernel-modules-4.19.0-91.82.75.uelc20.x86_64.rpm
kernel-modules-extra-4.18.0-305.10.2.uelc20.4.x86_64.rpm
kernel-modules-extra-4.19.0-91.82.75.uelc20.x86_64.rpm
kernel-tools-4.18.0-305.10.2.uelc20.4.x86_64.rpm
kernel-tools-4.19.0-91.82.75.uelc20.x86_64.rpm
kernel-tools-libs-4.18.0-305.10.2.uelc20.4.x86_64.rpm
kernel-tools-libs-4.19.0-91.82.75.uelc20.x86_64.rpm
# 统计两个内核版本各自的包数量
$ ls /mnt/koji/compose/xzyang/latest-UnionTechOS-20/compose/BaseOS/x86_64/os/Packages/ \
| grep 'kernel-4.18' | wc -l
9
$ ls /mnt/koji/compose/xzyang/latest-UnionTechOS-20/compose/BaseOS/x86_64/os/Packages/ \
| grep 'kernel-4.19' | wc -l
10
可以看到,
BaseOS/Packages下同时存在kernel-4.18和kernel-4.19两个版本的包,双内核集成成功。
六、总结
本文记录了在基于 CentOS 生态的系统中使用 Pungi 集成双内核(kernel-4.18 + kernel-4.19)的完整过程,遵循"发现问题 → 分析原因 → 逐步解决 → 验证测试"的思路,经历了三个递进的问题阶段:
| 阶段 | 问题 | 根因 | 解决方案 |
|---|---|---|---|
| 二 | 无法拉取两个版本内核 | Gather 阶段默认只取最高版本 | 通过 ExtraFiles 阶段添加额外内核 |
| 三 | 额外内核没放入源里 | ExtraFiles 默认路径不是 RPM 仓库路径 | 修改 extra_files.py 对 kernel 单独处理 |
| 四 | 额外内核未 createrepo | ExtraFiles 文件不参与 createrepo 流程 | 改用 pkgset_koji_builds + uos-packages.json 纯配置方案 |
总结:通过 pkgset_koji_builds 同时指定两个内核版本,配合 gather_method 和自定义包列表,再结合 extra_files 补充本地内核包,实现了双内核的完整集成,确保两个内核版本都经过 createrepo 生成仓库元数据,安装程序可正常识别。
665

被折叠的 条评论
为什么被折叠?



