LabHub
学习 学习路径 课程

RHEL 系管理

模块化与 module_hotfixes

在 LabHub 中继续学习

一句话总结

模块化 RPM 在普通 RPM 依赖关系之上又增加了一层。如果缺少这一层的信息,软件包即使以文件形式存在,也无法被查询到

概念图: 普通 RPM 依赖关系之上又增加了一层 · 无法被查询到 · 而且不会报错。 · 模块元数据却没有下载

为什么需要理解模块元数据

隔离网络中经常收到下面这样的报障。

ls /srv/repo/appstream/ | grep nodejs
# nodejs-18.20.4-1.module+el9.4.0+21212+d9e3c1f2.x86_64.rpm

dnf list available nodejs
# No matching Packages to list

文件确实在那里,dnf 却说不存在,而且不会报错。

它是如何工作的

为什么会发生这种情况

整个因果链有五个阶段。

  1. 使用 dnf reposync 下载 AppStream 时,没有添加 --download-metadata
  2. 软件包文件都下载了下来,模块元数据却没有下载
  3. createrepo_c 只能从 rpm 文件生成普通元数据。模块数据不在 rpm 内,因此无法由它生成
  4. 内网 dnf 即使看到模块化 RPM,也不知道它属于哪个流
  5. 过滤之后,这些软件包便无法被查询到

RHEL 9 文档将模块依赖描述为“普通 RPM 依赖关系之上的额外一层”,并说明它“像仓库之间的虚拟依赖关系一样运作”。

不同版本之间的差异

项目 RHEL 8 RHEL 9 RHEL 10
模块的定位 AppStream 的核心机制 从 9.1 起用于提供生命周期较短的附加版本 官方文档中已没有模块章节
默认流 没有预先定义的默认流 不适用
不指定流进行安装 自动启用默认流 必须指定流 不适用
切换流 distro-syncmodule resetmodule enabledistro-sync 一条 dnf module switch-to 命令 不适用

从 RHEL 8 发展到 RHEL 9 后,模块的重要性大幅降低,到 RHEL 10 时实际上已经消失。但是,RHEL 8/9 系统还会长期存在。

命令

dnf module list
dnf module list nodejs
dnf module info nodejs:20
dnf module enable nodejs:20
dnf module install nodejs:20/common
dnf module reset nodejs
dnf module switch-to nodejs:20      # RHEL 9
dnf module list --installed > state-modules.txt

reset 用于重置启用状态。切换流之前通常要先执行 reset。

三种解决办法

方法 1——下载时一并获取(标准做法)

dnf reposync --repoid=...appstream-rpms --download-path=/srv/sync \
  --download-metadata --gpgcheck --arch=x86_64 --arch=noarch

这种情况下,导入后不能再次运行 createrepo_c。重新运行反而会丢失模块数据。

方法 2——dnf modulesync

它会在下载模块所属软件包的同时,创建包含模块数据的仓库。

dnf --destdir=/srv/bundle/nodejs modulesync nodejs:20/minimal --resolve

方法 3——module_hotfixes=1(最后手段)

[airgap-appstream]
name=RHEL 9 AppStream (airgap, no modular metadata)
baseurl=file:///srv/repo/appstream
enabled=1
gpgcheck=1
module_hotfixes=1
metadata_expire=-1

文档定义是:“禁用模块 RPM 过滤,让仓库中的所有 RPM 都可用。默认值为 False。”

这样做需要付出代价。 不同流的软件包可能混合安装,而这种组合并未经过 Red Hat 测试,因此它只能作为“最后手段”。

使用 installroot 时

以空根目录为基准进行计算时,还必须指定模块平台 ID。

dnf download --installroot=/var/tmp/airgap-root --releasever=9.4 \
  --setopt=module_platform_id=platform:el9 \
  --resolve --alldeps --destdir=/srv/bundle nodejs

如果没有显式指定,该值会从空根目录中的 /etc/os-release 读取;但创建时这个文件并不存在,所以值为空,所有模块内容都会在没有任何报错的情况下被漏掉。 正因为它会悄无声息地消失,这也是最难发现的一类事故。

grep PLATFORM_ID /etc/os-release
# PLATFORM_ID="platform:el9"

实际工作中会遇到的情况

记录四类状态。 在导入前后保留下面四份记录,后续调查会容易得多。

dnf module list --installed > state-modules.txt
rpm -qa --qf '%{NAME}\t%|EPOCH?{%{EPOCH}}:{0}|\t%{VERSION}\t%{RELEASE}\t%{ARCH}\n' | sort > state-packages.tsv
dnf history userinstalled > state-userinstalled.txt
dnf history list > state-history.txt

第三份记录尤其有用。它只列出用户显式安装的软件包,所以只要在另一台服务器上安装这份列表中的内容,依赖项就会自动跟随安装。这比原样强行安装完整软件包列表安全得多。

下一步检查什么

本模块以判断概念的测验结束。先区分模块元数据缺失时出现的 症状与 module_hotfixes 的风险范围,再到下一个模块的 rh-createreporh-airgap 实验中选择仓库复制方式。