- Published on
RHEL 閉域網インストールが本当に難しい理由 — 依存関係はグラフであり、内側からは解けません
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- はじめに — 「rpm ファイルを 1 つ取ってきてください」
- 依存関係はリストではなくグラフです
- グラフを解く側とインストールする側が別のネットワークにあります
- Red Hat コンテンツはどう配信されるのか
- リポジトリは動く標的です
- モジュラリティが層をもう 1 つ重ねます
- このシリーズが扱うこと
- おわりに — 問題を定義し直せば手順が出てきます
- 自分で試す
- 次回
- 参考資料
はじめに — 「rpm ファイルを 1 つ取ってきてください」
閉域網を運用したことがある人なら、この依頼を受けたことがあるはずです。内側のサーバーにツールを 1 つ入れたいがネットワークがないので、外側の機材で rpm ファイルを 1 つダウンロードして USB に入れてほしい、という頼みです。
そしてそのファイルを持ち込んでインストールすると、こういう画面を見ます。
# 持ち込んだ rpm 1 つだけでインストールを試みると起きること
sudo dnf install ./htop-3.2.2-1.el9.x86_64.rpm
# Error:
# Problem: conflicting requests
# - nothing provides libnl-3.so.200()(64bit) needed by htop-3.2.2-1.el9.x86_64
足りないと言われたライブラリを取ってくると、今度はそのライブラリが要求する別のものがないと言われます。3、4 往復しているうちに半日が過ぎ、持ち込み審査は 1 日 1 回しかありません。
このシリーズはその往復をなくす方法を扱います。第 1 回はコマンドではなく 構造 を見ます。これがファイルコピーの問題ではないと理解できれば、残り 6 回のコマンドはすべて当然のものになります。
依存関係はリストではなくグラフです
もっともよくある誤解は「パッケージ A は B と C を必要とする」という平坦なリストを想像することです。RPM の依存関係はそういう形をしていません。
RPM における依存の単位はパッケージ名ではなく ケーパビリティ です。パッケージは自分が提供するものを Provides で宣言し、必要なものを Requires で宣言します。そのケーパビリティにはパッケージ名だけでなく共有ライブラリの soname、ファイルパス、さらにはバージョン付きシンボルまで含まれます。先ほどのエラーに出ていた文字列が、まさにそれです。
実際の大きさを目で確かめると感覚がつかめます。インターネットに接続された機材でこう問い合わせます。
# httpd が直接要求するケーパビリティ一覧 — パッケージ名ではなくシンボルとファイルが混ざって出ます
dnf repoquery --requires httpd
# そのケーパビリティを実際に提供するパッケージに置き換え、再帰的に最後まで追います
dnf repoquery --requires --resolve --recursive httpd | sort -u | wc -l
--requires は依存ケーパビリティを表示し、--resolve はそれを提供するパッケージに置き換え、--recursive はそこで止まらず下まで降りていきます。この 3 つのオプションはいずれも DNF 公式コマンドリファレンスにあります。行数はシステムの状態によって変わりますが、手で追える数ではないという点はどの環境でも同じです。
ここにもう 1 つ落とし穴があります。dnf repoquery --tree も depsolver の判断も、すでにインストールされているものを除いて 計算します。接続網の機材にすでに入っているライブラリは「不要」として扱われ、結果から消えます。その結果をそのまま閉域網へ持ち込むと、内側のサーバーにはそれがないので再び止まります。第 2 回で --installroot を使ってこの落とし穴に正面から取り組みます。
グラフを解く側とインストールする側が別のネットワークにあります
この問題の本質はここです。
依存関係を解くのは depsolver であり、depsolver の入力は リポジトリメタデータ全体 です。どのパッケージがどのケーパビリティを提供するかを知って初めて組み合わせを探せるからです。ところが閉域網の内側にはそのメタデータがありません。だから内側では計算自体が成立しません。
外側にはメタデータがありますが、インストール対象システムの状態がわかりません。計算できる側と答えが必要な側が分離していること、これが閉域網パッケージインストールのすべてです。
したがって解法は 2 通りしかありません。
- 解決済みの結果を移す。 外側で依存関係をすべて解いて rpm ファイルの束を作り、その束だけを持ち込みます。必要なものが明確で規模が小さいときに使います。
- リポジトリをまるごと移す。 リポジトリをミラーリングして持ち込み、内側で depsolver を正常に動かします。これから何を入れるかわからないとき、そして複数サーバーを長く運用するときに使います。
第 2 回が前者、第 3 回が後者です。ほとんどの組織は結局後者に行き着きます。
Red Hat コンテンツはどう配信されるのか
RHEL 9 公式ドキュメントはコンテンツリポジトリをこう区分します。BaseOS はすべてのインストールの基盤となる中核 OS 機能を、AppStream はユーザー空間アプリケーションとランタイム言語、データベースを収めます。ドキュメントはこの両方が RHEL に必須であり、すべての RHEL サブスクリプションに含まれると明記しています。CodeReady Linux Builder はすべてのサブスクリプションに付属しますが開発者向けであり、Red Hat がサポートしないパッケージが入っています。
このコンテンツにアクセスするにはシステムが登録され、リポジトリが有効になっている必要があります。接続網の機材で状態を確認する流れはこうです。
# システム登録およびサブスクリプション状態の確認
sudo subscription-manager status
# このシステムで有効になっているリポジトリ ID の一覧
sudo subscription-manager repos --list-enabled | grep "^Repo ID"
# 必要なリポジトリを有効にする (アーキテクチャ・バージョンによって ID が異なります)
sudo subscription-manager repos \
--enable rhel-9-for-x86_64-baseos-rpms \
--enable rhel-9-for-x86_64-appstream-rpms
ここで必ず触れておくべきことがあります。サブスクリプションなしに Red Hat コンテンツを再配布することは契約違反になり得るため、組織のライセンス条件を先に確認してください。 このシリーズはエンタイトルメントを迂回する方法を扱いません。あるステップにエンタイトルメントが必要なら、必要だと書いてそこで止めます。社内ミラーを作って社内システムに配布することが組織の契約で許されるかは法務や Red Hat の担当者に確認する問題であり、技術で決める問題ではありません。
サブスクリプションがなくて実習できない読者には CentOS Stream、Rocky Linux、AlmaLinux、Fedora が現実的な代替になります。dnf・createrepo_c・rpm の挙動はほぼそのまま再現します。ただしリポジトリ ID と GPG 鍵、そしてマイナーバージョンの固定方法はディストリビューションごとに異なるため、手順を書き写すときはその部分を各自の環境基準で確認し直す必要があります。
リポジトリは動く標的です
同じコマンドを今日と 2 週間後に実行すると違う結果が出ます。セキュリティ更新が入り、マイナーリリースが上がり、パッケージが obsolete 扱いになるからです。
これが問題になるのは、閉域網では持ち込みが イベント だからです。審査を経て一度入れた束で複数サーバーをインストールし、数か月後にサーバーを 1 台増やすときに同じ束をまた使います。そのとき「また取ってくればいい」は通用しません。取り直すと違うものが入り、既存サーバーとバージョンがずれます。
だから持ち込み単位には必ず 3 つが一緒に入っていなければなりません。リポジトリスナップショットを取った日付、使用したリリースバージョン、そしてパッケージごとの正確な NEVRA とチェックサムです。第 4 回でこのマニフェストを具体的に作ります。
モジュラリティが層をもう 1 つ重ねます
RHEL 8 は AppStream の中に モジュール という概念を導入しました。同じコンポーネントの複数バージョンをストリームに分けて 1 つのリポジトリに収め、そのうち 1 つだけを有効にして使う方式です。問題は、こうなると依存解決が RPM レベルとモジュールレベルで 同時に 起きる点です。RHEL 9 ドキュメントの表現を借りれば、モジュール依存は通常の RPM 依存の上に載る追加レイヤーであり、リポジトリ間の仮想的な依存のように振る舞います。
閉域網でこれがなぜ痛いのかは第 5 回で詳しく扱いますが、いま知っておくべきなのは バージョンごとに状況がまったく違う という事実です。公式ドキュメント基準で整理するとこうなります。
| 項目 | RHEL 8 | RHEL 9 | RHEL 10 |
|---|---|---|---|
| ドキュメント上の主コマンド | yum (dnf の別名) | dnf | dnf (dnf5 ベース) |
| モジュールの既定ストリーム | あり、既定ストリームが自動的に有効化される | 事前定義された既定ストリームなし | 該当なし |
| モジュールの提供 | AppStream の中核方式 | 9.1 からライフサイクルの短い追加バージョンとして提供 | 公式ドキュメントにモジュールの章自体がない |
| ストリーム切り替え | distro-sync → module reset → module enable → distro-sync | dnf module switch-to の 1 行 | 該当なし |
| リポジトリ追加 | yum-config-manager --add-repo | dnf config-manager --add-repo | dnf config-manager --add-repo |
RHEL 10 の公式「Managing software with the DNF tool」にはモジュール関連の章がまったく存在せず、Application Stream の提供形式も RPM と Software Collections の 2 つだけが明記されています。RHEL 8 の手順書をそのまま RHEL 10 に持って行くと、存在しないコマンドを実行することになるという意味です。
このシリーズが扱うこと
閉域網の持ち込みを最初から最後まで、一度作れば 6 か月後にも同じ結果が出る手順にすることが目標です。
- なぜ難しいのか (この記事)
- 外側で受け取る —
dnf download、reposync、yumdownloaderの違いと--installroot - ローカルリポジトリを作る —
createrepo_c、repodata、.repoファイルと GPG 鍵 - 持ち込みと完全性 — チェックサム、署名検証、再現可能なバンドルマニフェスト
- モジュールとバージョン固定 — マイナーバージョンのロックと状態の再現
- コンテナイメージ —
podman saveとskopeo、社内レジストリ - 運用 — セキュリティパッチ周期、ロールバック、CVE 対応遅延の管理
おわりに — 問題を定義し直せば手順が出てきます
閉域網インストールはファイルを移す問題ではありません。計算できる側で計算を終わらせ、その結果を再現可能な形で移す問題 です。
こう定義すれば残りは付いてきます。計算を終わらせるにはクリーンなルートで解く必要があり、結果を再現するにはスナップショット時点とリリースバージョンを打ち込んでおく必要があり、移したものを信じるには署名を確認する必要があります。それがこのシリーズの残り 6 回です。
コマンドとオプションは 2026-08-15 に公式ドキュメントで確認しました。RHEL のバージョンによって異なるため、使用中のバージョンのドキュメントで再確認してください。
自分で試す
- Linux ターミナル —
dnfとrpmコマンドの形を手に馴染ませる - Linux コマンドクイズ — パッケージ管理コマンドの復習
- Linux エミュレーター — ファイルシステム構造とパスの感覚をつかむ