- はじめに — 3 つのツールはそれぞれ別の問題を解きます
- dnf download — 必要なものだけ選んで受け取る
- --alldeps だけでは足りません
- dnf reposync — リポジトリを丸ごと複製する
- yumdownloader はいつ使うのか
- 受け取ったものが完全か確認する
- RHEL 10 と dnf5 で変わること
- おわりに — 空のルートで解き、持ち込む前に閉包を確認します
- 自分で試す
- 前回 / 次回
- 参考資料
はじめに — 3 つのツールはそれぞれ別の問題を解きます
第 1 回では、閉域網インストールを「計算できる側で計算を終わらせる問題」だと整理しました。今回はその計算を実際に終わらせる方法です。
ツールが 3 つあり、名前が似ているのでよく混同されますが、3 つは別々の問いに答えます。
dnf download— 「このパッケージと、それが必要とするものをファイルでください」dnf reposync— 「このリポジトリ全体をローカルに複製してください」yumdownloader— RHEL 7 の頃から使われている名前。RHEL 8 以降は互換パッケージとして提供されます
何を選ぶかは規模ではなく、これから何をインストールするのか分かっているかどうかで決まります。
dnf download — 必要なものだけ選んで受け取る
もっとも単純な形から見ていきます。
# パッケージを 1 つだけカレントディレクトリにダウンロードする (依存関係は付いてこない)
dnf download httpd
# 依存関係まで解決して受け取る
dnf download --resolve httpd
# すでにインストール済みのものもスキップせず全部受け取る
dnf download --resolve --alldeps --destdir ./bundle httpd
公式の dnf-plugins-core ドキュメントは 2 つのオプションをこう定義しています。--resolve は "Resolves dependencies of specified packages and downloads missing dependencies in the system"、--alldeps は "When used with --resolve, download all dependencies (do not skip already installed ones)" です。
この 2 文の違いを見落としてはいけません。--resolve だけを使うと、この機材にないものだけを受け取ります。閉域網へ持ち込むバンドルを作る目的なら、これはほぼ常に誤った結果です。ダウンロードする機材とインストールする機材が別のシステムだからです。
--destdir は dnf のグローバルオプションで、"Redirect downloaded packages to provided directory" と文書化されています。download プラグインのドキュメントには --downloaddir も出てきますが、例では --destdir を使っています。どちらも同じグローバルオプションの表記です。
--alldeps だけでは足りません
--alldeps を付けたのに閉域網で詰まる場合があります。理由はこうです。
--alldeps は「すでにインストール済みのものをスキップするな」と指示しますが、依存関係の解決そのものは依然としてこのシステムの状態を前提に行われます。接続網の機材が RHEL 9.4 でインストール対象が RHEL 9.2 なら解決結果が変わり得ますし、接続網の機材で有効になっているリポジトリが閉域網側と違えば、そこでもまた変わります。
確実な方法は、何もインストールされていない空のルートで計算することです。dnf の公式ドキュメントは --installroot をこう説明します。"Think of this like doing chroot <root> dnf, except using --installroot allows dnf to work before the chroot is created. It requires absolute path."
そしてすぐ次の段落に、必ず読むべき警告があります。
Note: You may also want to use the command-line option --releasever=<release>
when creating the installroot, otherwise the $releasever value is taken from
the rpmdb within the installroot (and thus it is empty at the time of creation
and the transaction will fail).
空のルートには rpmdb がなく、リリースバージョンは rpmdb から導出されるため、明示しないと値が空になりトランザクションが失敗するという意味です。実際のコマンドはこうなります。
# 持ち込み用の空のルートを作る
sudo mkdir -p /var/tmp/airgap-root
# 空のルートを基準に依存関係をすべて解いて受け取る
sudo dnf download \
--installroot=/var/tmp/airgap-root \
--releasever=9.4 \
--setopt=reposdir=/etc/yum.repos.d \
--resolve --alldeps \
--destdir=/var/tmp/airgap-bundle/rpms \
httpd mod_ssl
--setopt=reposdir= を付けた理由もドキュメントにあります。"configuration file and reposdir are searched inside the installroot first. If they are not present, they are taken from the host system." 空のルートにはリポジトリ設定がないのでホストのものを使うことになりますが、ドキュメントはコマンドラインでパスを指定した場合について "this path is always relative to the host with no exceptions" と明言しています。つまり明示的に書いておくほうが予測可能です。
モジュラーシステム、つまり RHEL 8 やモジュールを使う RHEL 9 なら、もう 1 行必要です。ドキュメントは --setopt=module_platform_id= を指定しないと "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)" と警告しています。モジュールのコンテンツが静かに抜け落ちるという意味なので、特に危険です。この部分は第 5 回で続けて扱います。
dnf reposync — リポジトリを丸ごと複製する
これから何をインストールするか分からないなら、リポジトリ自体を移すほうが得策です。公式ドキュメントの説明は "reposync makes local copies of remote repositories. Packages that are already present in the local directory are not downloaded again" です。2 文目のおかげで、再実行が増分で動作します。
# BaseOS リポジトリをメタデータごとローカルへ複製する
sudo dnf reposync \
--repoid=rhel-9-for-x86_64-baseos-rpms \
--download-path=/var/tmp/airgap-bundle/repos \
--download-metadata \
--gpgcheck \
--newest-only \
--arch=x86_64 --arch=noarch \
--remote-time
オプションごとにドキュメントが述べている内容はこうです。
| オプション | ドキュメント上の動作 |
|---|---|
-p, --download-path | "Root path under which the downloaded repositories are stored, relative to the current working directory. Defaults to the current working directory." |
--download-metadata | リポジトリメタデータも一緒に受け取り、そのままリポジトリとして使えるようにします |
-g, --gpgcheck | "Remove packages that fail GPG signature checking after downloading. Exit code is 1 if at least one package was removed." |
-n, --newest-only | "Download only newest packages per-repo." |
-a, --arch | "Download only packages of given architectures (default is all architectures). Can be used multiple times." |
--delete | "Delete local packages no longer present in repository." |
--norepopath | "Don't add the reponame to the download path. Can only be used when syncing a single repository." |
--remote-time | "Try to set the timestamps of the downloaded files to those on the remote side." |
-u, --urls | "Just print urls of what would be downloaded, don't download." |
--source | "Download only source packages." |
--gpgcheck の終了コードの挙動は自動化で役に立ちます。署名検証に失敗したパッケージが 1 つでも削除されると終了コードが 1 になるので、持ち込みスクリプトでそのままゲートとして使えます。
--newest-only と --download-metadata を一緒に使うときは、ドキュメントの但し書きを覚えておく必要があります。最新パッケージだけをダウンロードしますが、メタデータには以前のパッケージの情報が残ります。内側で特定の古いバージョンを要求すると、メタデータにはあるのにファイルがない状態になります。特定バージョンの固定が必要な環境なら、--newest-only を外すほうが安全です。
持ち込み前に複製がきちんとリポジトリとして認識されるかは、ドキュメントの例の形で確認できます。
# 複製したディレクトリを一時リポジトリとして接続し、一覧が出るか見る
dnf --repofrompath=synced,/var/tmp/airgap-bundle/repos/rhel-9-for-x86_64-baseos-rpms \
--repoid=synced list --available | head
--repofrompath は "Specify a repository to add to the repositories for this query"、--repoid は "Enable just specific repositories by an id or a glob" と文書化されたグローバルオプションです。
yumdownloader はいつ使うのか
yumdownloader は RHEL 7 の yum-utils 時代の名前です。RHEL 8 以降でも yum-utils パッケージをインストールすれば同じ名前のコマンドが使え、既存の運用スクリプトをそのまま維持しなければならないときに意味があります。
ただし正直に書くと、yumdownloader は dnf-plugins-core の公式ドキュメントに独立した項目としては存在しません。 文書化されているプラグイン一覧には download、reposync、repoclosure などがあり、yumdownloader はありません。そのためこの記事では yumdownloader の個別オプションを断定しません。使用中のシステムで次のように直接確認してください。
# このコマンドがどのパッケージから来るのか確認
dnf provides '*/yumdownloader'
# 実際にサポートしているオプションを確認
man yumdownloader
yumdownloader --help
新しく手順書を書くなら dnf download を使うほうがよいでしょう。ドキュメントがあり、オプションの意味が明確で、dnf5 にも対応するコマンドがあります。
受け取ったものが完全か確認する
ここがこの記事でもっとも重要な部分です。持ち込み媒体を作る前に、この束だけで依存関係がすべて解けるかを確認しなければなりません。閉域網に入ってから気づくと、次の審査まで待つことになるからです。
dnf repoclosure がまさにこの仕事をします。ドキュメントの定義は "repoclosure is a program that reads package metadata from one or more repositories, checks all dependencies, and displays a list of packages with unresolved dependencies" です。
# 持ち込み予定のディレクトリをリポジトリにして (第 3 回で詳しく扱います)
createrepo_c /var/tmp/airgap-bundle/rpms
# そのリポジトリだけですべての依存関係が解けるか検査する
dnf repoclosure \
--repofrompath=bundle,/var/tmp/airgap-bundle/rpms \
--repo=bundle
出力が空なら合格です。解決されない依存関係があれば、パッケージごとに列挙されます。オプションには --check <repoid>("Specify repo ids to check, can be specified multiple times")、--pkg <pkg-spec>("Check closure for this package only")、--best("Check only the newest packages per arch")があります。
この 1 ステップを手順に入れるだけで、持ち込みのやり直しの相当数が消えます。
RHEL 10 と dnf5 で変わること
RHEL 10 は dnf5 ベースです。download コマンドのオプションの一部は名前が変わっているので、手順書をそのまま移してはいけません。
| 機能 | dnf4 (RHEL 8 / 9) | dnf5 (RHEL 10) |
|---|---|---|
| ソース RPM を受け取る | --source | --srpm |
| 依存関係の解決 | --resolve | --resolve |
| インストール済みも含める | --alldeps | --alldeps |
| 受け取る場所 | --destdir / --downloaddir | --destdir |
| 特定リポジトリに限定 | --repo | --from-repo |
| 存在しないパッケージをスキップ | ドキュメントに該当オプションなし | --skip-unavailable |
dnf5 のドキュメントには --allmirrors("To be used together with --url. It prints out space-separated URLs from all available mirrors")のように dnf4 になかったオプションもあります。いずれにせよ、実行前に使用中のバージョンの man dnf または dnf download --help で確認する習慣が安全です。
サブスクリプションなしに Red Hat のコンテンツを再配布することは契約違反になり得るので、組織のライセンス条件をまず確認してください。この記事の手順は、エンタイトルメントが付いた接続網の機材で実行することを前提にしています。
おわりに — 空のルートで解き、持ち込む前に閉包を確認します
今回の結論は 2 行です。
受け取る機材の状態が結果を汚染します。--installroot で空のルートを作り --releasever を明示すれば、その汚染は消えます。モジュールを使うシステムなら module_platform_id まで指定してはじめて、モジュールのコンテンツが抜け落ちません。
持ち込む前に dnf repoclosure を回します。この検査は数秒で終わり、通らないバンドルを持って入ると数日かかります。
コマンドとオプションは 2026-08-15 に公式ドキュメントで確認しました。RHEL のバージョンによって異なるので、使用中のバージョンのドキュメントで再確認してください。
自分で試す
- Linux ターミナル — 長い dnf コマンドを改行つきで組み立ててみる
- Linux コマンドクイズ — パッケージ管理オプションの復習
- ハッシュ生成器 — 受け取った rpm のチェックサムを作り、第 4 回のマニフェストを先に練習する
前回 / 次回
参考資料
현재 단락 (1/100)
第 1 回では、閉域網インストールを「計算できる側で計算を終わらせる問題」だと整理しました。今回はその計算を実際に終わらせる方法です。