Skip to content
Published on

閉域網でのモジュールとバージョン固定 — RHEL 8・9・10 はそれぞれ違う話をします

シェア
Authors

はじめに — リポジトリにあるのに「ない」と言われます

閉域網の持ち込み作業でもっとも面食らう瞬間がこれです。

# 確かに rpm ファイルはリポジトリディレクトリにある
ls /srv/repo/rhel9-appstream/ | grep nodejs
# nodejs-18.20.4-1.module+el9.4.0+21212+d9e3c1f2.x86_64.rpm

# ところが dnf は「ない」と言う
dnf list available nodejs
# Error: No matching Packages to list

ファイルはあります。メタデータも作りました。それなのに「ない」と言われます。

これはリポジトリが壊れたのではなく、モジュールフィルタリングが動いた結果 です。そしてこの現象は RHEL のバージョンによって出たり出なかったりします。今回はその構造と解決策を扱います。

まずバージョンを確認してください

このシリーズでバージョン差がもっとも大きいのが今回です。公式ドキュメント基準で整理するとこうなります。

項目RHEL 8RHEL 9RHEL 10
モジュールの提供AppStream の中心的な仕組み9.1 からライフサイクルの短い追加バージョンとして公式 DNF ドキュメントにモジュールの章がない
デフォルトストリームあり。ドキュメント: デフォルトストリームはメジャーリリースの間ずっと変わらないドキュメント: "no default module streams are predefined"該当なし
ストリームを指定せずにインストールデフォルトストリームが自動的に有効化されるストリームを指定する必要がある該当なし
ストリームの切り替えdistro-syncmodule resetmodule enabledistro-syncdnf module switch-to の 1 行該当なし
AppStream の提供形式RPM、モジュール、Software CollectionsRPM、モジュール、Software Collectionsドキュメント基準では RPM と Software Collections

RHEL 10 の公式ドキュメント "Managing software with the DNF tool" にはモジュール関連の章が存在せず、付録のコマンド一覧にも dnf module 系がありません。RHEL 10 だけを使う環境なら、今回の前半は飛ばして「バージョンを固定します」から読んでいただければ十分です

なぜモジュラーパッケージが消えるのか

RHEL 9 のドキュメントは、モジュールの依存関係を "an additional layer on top of regular RPM dependencies" であり "behave similarly to hypothetical dependencies between repositories" と説明しています。この追加レイヤーが動作するには モジュールメタデータ が必要です。

そして dnf modulesync のドキュメントが決定的な一文を含んでいます。DNF はモジュラーパッケージをインストールするときにモジュラーメタデータを要求する、というものです。

これで先ほどの現象が説明できます。

  1. dnf reposync で AppStream を取得したが --download-metadata を付けていなかった
  2. パッケージファイルはすべて来たが、モジュールメタデータは来なかった
  3. createrepo_c は rpm ファイルから通常のメタデータしか作りません。モジュールデータは rpm の中にないので作れません
  4. 内側の dnf はモジュラー RPM を見ても、どのストリームに属するのか分かりません
  5. フィルタリングの結果、それらのパッケージは検索に出てきません

解決策は 3 通りあり、上のほうがより王道です。

方法 1 — 取得時にモジュールメタデータも一緒に持ってくる

# --download-metadata を必ず付ける
sudo dnf reposync \
  --repoid=rhel-9-for-x86_64-appstream-rpms \
  --download-path=/var/tmp/airgap-bundle/repos \
  --download-metadata \
  --gpgcheck \
  --arch=x86_64 --arch=noarch

--download-metadata はリポジトリメタデータをそのまま取得し、すぐにリポジトリとして使える状態にします。この場合、持ち込み後に createrepo_c を再実行する必要はなく、実行するとかえってモジュールデータを失うおそれがあるので実行しないでください

方法 2 — dnf modulesync でモジュールを含むリポジトリを作る

特定のモジュールだけが必要なときにもっともきれいです。ドキュメントの説明は、このコマンドが "downloads packages from modules according to provided arguments and creates a repository with modular data in working directory" というものです。ダウンロードとリポジトリ生成を一度に行います。

# nodejs モジュール全体を取得してモジュールデータを含むリポジトリを作る
dnf modulesync nodejs

# 特定のストリームとプロファイル、依存関係まで指定した場所へ
dnf --destdir=/var/tmp/airgap-bundle/nodejs modulesync nodejs:18/minimal --resolve

# 最新のモジュールだけ
dnf modulesync --newest-only nodejs

--resolve は "Resolve and download needed dependencies"、-n--newest-only は "Download only packages from the newest modules" とドキュメント化されています。

ドキュメントが紹介する 2 段階の方式も閉域網によく合います。接続網の機材で dnf module install によって実際に必要なものを確定させてから dnf modulesync --destdir=... を実行すれば、そのシステムが実際に要求したパッケージだけを収めたリポジトリができあがります。

方法 3 — module_hotfixes でフィルタリングを止める

最後の手段です。dnf の設定ドキュメントは module_hotfixes を "Set this to True to disable module RPM filtering and make all RPMs from the repository available. The default is False" と定義しています。

# /etc/yum.repos.d/airgap-appstream.repo
[airgap-appstream]
name=RHEL 9 AppStream (airgap, no modular metadata)
baseurl=file:///srv/repo/rhel9-appstream
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
module_hotfixes=1
metadata_expire=-1

パッケージはただちに見えるようになります。その代わり、モジュールが提供していた保証は失われます。異なるストリームのパッケージが混ざってインストールされることがあり、その組み合わせは Red Hat が試験した組み合わせではありません。方法 1 か 2 が可能ならそちらを使ってください。

空の installroot でモジュールが丸ごと抜け落ちる罠

第 2 回で見た --installroot の手法には、モジュールに関わる罠がもう 1 つあります。dnf 公式ドキュメントの警告をそのまま引用します。

On a modular system you may also want to use the
--setopt=module_platform_id=<module_platform_name:stream> command-line option
when creating the installroot, otherwise the module_platform_id value will be
taken from the /etc/os-release file within the installroot (and thus it will be
empty at the time of creation, the modular dependency could be unsatisfied and
modules content could be excluded).

空のルートにはリリース情報ファイルがないのでプラットフォーム ID が空になり、その結果 モジュールコンテンツが除外されたまま ダウンロードが成功します。エラーも出さずに静かに抜け落ちるので、もっとも発見しにくい種類の事故です。

# RHEL 9 の場合 — プラットフォーム ID まで明示する
sudo dnf download \
  --installroot=/var/tmp/airgap-root \
  --releasever=9.4 \
  --setopt=module_platform_id=platform:el9 \
  --setopt=reposdir=/etc/yum.repos.d \
  --resolve --alldeps \
  --destdir=/var/tmp/airgap-bundle/rpms \
  nodejs

プラットフォーム ID の値はシステムから確認できます。

# 現在のシステムのプラットフォーム ID をそのまま読む
grep PLATFORM_ID /etc/os-release
# PLATFORM_ID="platform:el9"

バージョンを固定します

ここからはモジュールのない RHEL 10 にもそのまま当てはまります。

閉域網で再現が崩れるもっとも多い原因は、マイナーバージョンが動くことです。リリースバージョン変数は既定で rpmdb から導出されるため、放っておくとシステムが上がるのに合わせて一緒に上がります。固定する方法は 3 つあり、適用範囲がそれぞれ違います。

# 1. 1 回のコマンドにのみ適用 — 持ち込み用のダウンロードに使う
dnf download --releasever=9.4 --resolve --alldeps httpd

# 2. システム全体に固定 (サブスクリプション使用時) — Red Hat ドキュメントの表記
sudo subscription-manager release --set 9.4

# 現在の固定値を確認
subscription-manager release

# 3. dnf の変数ファイルで固定 — サブスクリプションを使わない環境
echo "9.4" | sudo tee /etc/dnf/vars/releasever

3 番は、Red Hat のアップグレードドキュメントが RHUI 環境でリリースバージョンを手動指定する方法として案内している方式です。閉域網の社内ミラーのようにサブスクリプションツールが介在しない構成でも、同じ原理で動作します。CentOS Stream や Rocky、Alma を使っているなら、たいていこの方式を使うことになります。

パッケージ単位で縛るときは versionlock を使います。

sudo dnf install python3-dnf-plugin-versionlock

# 現在インストールされているバージョンで固定する
sudo dnf versionlock add httpd

# ロック一覧を確認
dnf versionlock list

# グロブで直接指定する (NEVRA 解決をせずそのまま使う)
sudo dnf versionlock add --raw 'httpd-2.4.57-*'

# 特定の項目を解除
sudo dnf versionlock delete httpd

# すべて解除
sudo dnf versionlock clear

ドキュメント基準で add は "Add a versionlock for all available packages matching the spec"、--raw は "Do not resolve <package-name-spec> to NEVRAs to find specific version to lock to" であり、設定ファイルは /etc/dnf/plugins/versionlock.conf です。

注意すべき点があります。Red Hat のアップグレードドキュメントは、メジャーアップグレードの前に dnf versionlock clear でロックを解除するよう案内しています。ロックが残っていると依存関係の解決に失敗するからです。閉域網でも、大規模な持ち込みの前にロック状態を確認する手順を入れておくのがよいです。

状態を記録して再現します

バンドルが同じでも、インストールの順序が違えば結果が変わることがあります。最初のサーバーで確定させた状態を記録しておき、残りのサーバーにそのまま適用するほうが安全です。

# 1. 有効化されたモジュールストリームとインストール済みプロファイル (RHEL 8 / 9)
dnf module list --installed > state-modules.txt

# 2. インストール済みパッケージ全体を NEVRA で記録
rpm -qa --queryformat '%{NAME}\t%|EPOCH?{%{EPOCH}}:{0}|\t%{VERSION}\t%{RELEASE}\t%{ARCH}\n' \
  | sort > state-packages.tsv

# 3. ユーザーが明示的にインストールしたものだけ (依存関係を除く)
dnf history userinstalled > state-userinstalled.txt

# 4. トランザクション履歴
dnf history list > state-history.txt

3 番がとくに有用です。dnf history userinstalled はユーザーが直接インストールしたパッケージだけを見せてくれるので、その一覧だけを別のサーバーでインストールすれば依存関係は自然についてきます。パッケージ一覧全体をそのまま流し込むよりはるかに安全です。

RHEL 8 でストリームを変更する必要があるなら、ドキュメントの順序をそのまま守ってください。ドキュメントはまず yum distro-sync が "Nothing to do. Complete!" で終わるかを確認し、そのあと yum module resetyum module enable を経て、もう一度 yum distro-sync を実行するよう案内しています。途中で依存関係の衝突が出た場合は --allowerasing が必要で、Perl モジュールについては、標準的な RHEL 8 インストールの一部パッケージが Perl 5.26 に依存しているため、このオプションが常に必要だと明記されています。

RHEL 9 なら 1 行です。

# RHEL 9 専用 — ストリームの切り替えを 1 コマンドで
sudo dnf module switch-to nodejs:20

サブスクリプションなしに Red Hat コンテンツを再配布することは契約違反になりうるので、組織のライセンス条件を先に確認してください。モジュールメタデータもまた Red Hat コンテンツの一部です。

おわりに — 先にバージョンを確認し、メタデータを失わないでください

今回の要点は 3 つです。

モジュラー RPM はメタデータなしでは見えませんreposync--download-metadata を付けるか、dnf modulesync を使ってください。module_hotfixes は最後の手段です。

空の installroot ではプラットフォーム ID を明示してください。抜かすとモジュールコンテンツがエラーなしで除外されます。

リリースバージョンを固定してください。これ 1 つで再現失敗の相当数が消え、モジュールのない RHEL 10 でも同じように有効です。

コマンドとオプションは 2026-08-15 に公式ドキュメントで確認しました。RHEL のバージョンによって異なるので、使用中のバージョンのドキュメントで再確認してください。

自分で試す

前回 / 次回

参考資料