モジュラリティとmodule_hotfixes
一言でいうと
モジュラー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は、ないと言います。エラーも出ません。
どう動くのか
なぜこうなるのか
因果は5段階です。
dnf reposyncでAppStreamを取得したのに、--download-metadataを付けていません- パッケージファイルはすべて届きましたが、モジュールメタデータは届きません
createrepo_cは、rpmファイルから通常のメタデータだけを作ります。モジュールデータはrpmの中にないので、作れません- 内側のdnfはモジュラーRPMを見ても、どのストリームに属するのかわかりません
- フィルタリングの結果、そのパッケージが照会されません
RHEL 9のドキュメントは、モジュールの依存関係を「通常のRPM依存関係の上にある追加の階層」と表現し、「リポジトリ間の仮想的な依存関係のように動作する」と説明しています。
バージョンごとの違い
| 項目 | RHEL 8 | RHEL 9 | RHEL 10 |
|---|---|---|---|
| モジュールの提供 | AppStreamの中核的な方式 | 9.1からライフサイクルの短い追加バージョンとして提供 | 公式ドキュメントにモジュールの章がない |
| デフォルトストリーム | あり | あらかじめ定義されたデフォルトストリームなし | 該当なし |
| ストリームを指定せずにインストール | デフォルトストリームが自動で有効化 | ストリームを指定する必要がある | 該当なし |
| ストリームの切り替え | distro-sync → module reset → module enable → distro-sync |
dnf module switch-toの1行 |
該当なし |
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が先です。
解決策の3つの方向
方法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"
現場での姿
状態記録の4種類。持ち込みの前後に、この4つを残しておくと、調査がずっと楽になります。
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
3つ目が特に役に立ちます。ユーザーが明示的にインストールしたものだけが出力されるので、その一覧だけを別のサーバーにインストールすれば、依存関係は自動でついてきます。パッケージ一覧全体をそのまま流し込むより、ずっと安全です。
次の確認で見ること
このモジュールは、概念を判断するクイズで締めくくります。モジュールメタデータが抜けたときに現れる症状と、module_hotfixesの危険範囲を区別したうえで、次のモジュールのrh-createrepoとrh-airgapのラボで、リポジトリの複製方式を選択します。