Skip to content

필사 모드: 閉域網へのコンテナイメージ持ち込み — podman save と skopeo は別の道具です

日本語
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

はじめに — RPM の次には必ずイメージが来ます

パッケージの持ち込み手順が固まると、すぐ次の要求が来ます。コンテナイメージも同じ媒体で内側に入れてほしい、という話です。

道具には 2 つの系統があり、よく混同されます。

  • podman save / podman loadローカルのイメージストアを経由します。まず pull してローカルに置き、それをファイルに書き出し、内側で再びローカルへ読み込みます
  • skopeo copy / skopeo syncローカルストアを経由しません。出発地から目的地へ直接運びます

イメージが 1 つ 2 つならどちらでも構いません。数十個になり、それが定期作業になると差がはっきりします。

まずトランスポートを理解します

skopeo を使うにはトランスポート表記を知る必要があります。containers-transports ドキュメントを基準にすると、閉域網で使うのはこのあたりです。

表記ドキュメント上の意味
docker://"An image in a registry implementing the 'Docker Registry HTTP API V2'."
dir:"An existing local directory path storing the manifest, layer tarballs and signatures as individual files."
docker-archive:"An image is stored in the docker-save(1) formatted file."
oci:"An image in a directory structure compliant with the 'Open Container Image Layout Specification' at path."
oci-archive:"a tar(1) archive with contents compliant with the 'Open Container Image Layout Specification'."
containers-storage:"An image located in a local containers storage."

docker-archive: にはドキュメントが明記する罠があります。"a write to a docker-archive: destination completely overwrites path, replacing it with the single provided image." 同じ tar ファイルにイメージを 1 つずつ追加しようとして、前のものを消してしまう事故はここから出ます。複数のイメージを 1 つのファイルに入れるなら dir:oci: を使うか、podman save の複数イメージ用オプションを使う必要があります。

名前解決の規則も覚えておくとよいです。ドキュメントは "If name does not contain a slash, it is treated as docker.io/library/name" と定義しています。閉域網で短い名前を使うと、存在しない外部ホストを向いてしまうという意味です。

podman save と load — 単純で確実な経路

イメージを数個運ぶだけなら、こちらが楽です。

# 接続網の機材: イメージを取得する (対象アーキテクチャを明示)
podman pull --arch amd64 registry.access.redhat.com/ubi9/ubi:9.4

# 単一イメージをアーカイブに書き出す
podman save --quiet -o ubi9.tar registry.access.redhat.com/ubi9/ubi:9.4

# 複数のイメージを 1 つのアーカイブにまとめる (docker-archive 形式のみ対応)
podman save --multi-image-archive -o bundle.tar \
  registry.access.redhat.com/ubi9/ubi:9.4 \
  registry.access.redhat.com/ubi9/nginx-124:latest

# OCI 形式で書き出す
podman save -o ubi9-oci.tar --format oci-archive registry.access.redhat.com/ubi9/ubi:9.4

--format が受け取る値はドキュメントに 4 種類として明記されています。docker-archive は "A tar archive interoperable with docker load(1)"、oci-archive は "A tar archive using the OCI Image Format"、oci-dir は "A directory using the OCI Image Format"、docker-dir は "dir transport with v2s2 manifest type" です。

--multi-image-archive-m は "Allow for creating archives with more than one image. Additional names are interpreted as images instead of tags. Only supported for --format=docker-archive" です。OCI 形式とは併用できません。

内側で読み込む側はもっと単純です。

# 閉域網: アーカイブからローカルストアへ読み込む
podman load -i bundle.tar

# 読み込んだイメージを確認
podman images

podman load のオプションは --input-i--quiet-q、そしてヘルプだけです。ドキュメントはこのコマンドを "restores an archive created by podman save as the same image, preserving its layers, history and tags" と説明しています。

ドキュメントが付け加えている運用上の注意が 1 つあります。"Use the environment variable TMPDIR to change the temporary storage location of container images. Podman defaults to use /var/tmp." 大容量イメージを読み込むときに一時領域が足りずに失敗することがあるので、閉域網サーバーのパーティション構成によってはこの変数を指定する必要があります。

注意: podman save には署名関連のオプションがありません。公式ドキュメントのオプション一覧には --compress--format--help--multi-image-archive--output--quiet--uncompressed の 7 つしかありません。イメージに署名を付けるには、あとで出てくる skopeo 側を使う必要があります。

skopeo copy — ローカルを経由せずに運びます

skopeo copy のドキュメント説明には、閉域網で重要な一文があります。"Uses the system's trust policy to validate images, rejects images not trusted by the policy." コピーそのものがポリシー検査を通るという意味なので、ポリシーをきちんと設定しておけば、持ち込み経路がそのまま検証経路になります。

# レジストリから持ち込み媒体のディレクトリへ
skopeo copy \
  docker://registry.access.redhat.com/ubi9/ubi:9.4 \
  dir:/media/transfer/images/ubi9

# 持ち込み媒体から社内レジストリへ
skopeo copy \
  dir:/media/transfer/images/ubi9 \
  docker://registry.internal.example.com/base/ubi9:9.4

# レジストリからレジストリへ直接 (両方に到達できる場合)
skopeo copy \
  docker://quay.io/skopeo/stable:latest \
  docker://registry.internal.example.com/skopeo:latest

閉域網で見落としやすいオプションを 2 つ挙げます。

# マルチアーキテクチャイメージをすべてコピーする
skopeo copy --all \
  docker://registry.access.redhat.com/ubi9/ubi:9.4 \
  dir:/media/transfer/images/ubi9

# 必要なアーキテクチャだけ選んでコピーする
skopeo copy --multi-arch=linux/amd64,linux/arm64 \
  docker://quay.io/skopeo/stable:latest \
  docker://registry.internal.example.com/skopeo:latest

# リトライを有効にする (デフォルトはリトライなし)
skopeo copy --retry-times 3 \
  docker://registry.access.redhat.com/ubi9/ubi:9.4 \
  dir:/media/transfer/images/ubi9

--all-a のドキュメント説明は "If source-image refers to a list of images, instead of copying just the image which matches the current OS and architecture ... attempt to copy all of the images in the list, and the list itself" です。デフォルトの動作が現在のシステムに合う 1 つだけをコピーすることだ という点が核心です。x86_64 のノート PC で取得して ARM サーバーに持ち込むと、エラーも出ずに間違ったものが入ります。

--multi-arch はデフォルトが system で、allindex-only、そしてカンマ区切りのプラットフォーム一覧を受け取ります。ドキュメントは index-only とプラットフォーム一覧が "sparse manifest lists" を作り、"usually fail unless the referenced per-architecture images are already present in the destination, or the target registry supports sparse indexes" と警告しています。

--retry-times は "The number of times to retry. By default, no retries are attempted" です。デフォルトがリトライなしだという点は、大容量イメージを扱う持ち込みスクリプトでは必ず押さえておく必要があります。

skopeo sync — 閉域網ミラーリングの正解

複数のイメージを定期的に運ぶなら、こちらです。公式ドキュメントの説明文が、そのままこのシリーズの主題になっています。

"Synchronize images between registry repositories and local directories. Synchronization is achieved by copying all the images found at source to destination - useful when synchronizing a local container registry mirror or for populating registries running inside of air-gapped environments."

出発地と目的地のトランスポートを別々のフラグで受け取るのが copy との違いです。ソースは dockerdiryaml を、目的地は dockerdir を受け取ります。

# 接続網: リポジトリの全タグを持ち込み媒体のディレクトリへ
skopeo sync --src docker --dest dir \
  registry.access.redhat.com/ubi9/ubi /media/transfer/images

# 閉域網: 媒体から社内レジストリへ
skopeo sync --src dir --dest docker \
  /media/transfer/images/ubi:9.4 registry.internal.example.com/base/

# 同じ名前のイメージが複数の出所から来るときにパスの衝突を防ぐ
skopeo sync --src docker --dest dir --scoped \
  registry.access.redhat.com/ubi9/ubi /media/transfer/images

# 実際には運ばず、何が運ばれるかだけを見る
skopeo sync --src docker --dest dir --dry-run \
  registry.access.redhat.com/ubi9/ubi /media/transfer/images

ソースが docker のとき、ドキュメントは "If no image tag is specified, skopeo sync copies all the tags found in that repository" と明記しています。タグを指定しなければ全部持ってくるという意味なので、持ち込み容量を見積もるときは注意が必要です。

--scoped は "Prefix images with the source image path, so that multiple images with the same name can be stored at destination" です。ドキュメントの例によると、このオプションを使うと媒体に registry.example.com/busybox:1-glibc という形のパスが作られ、使わなければ busybox:1-glibc になります。複数のレジストリから同じ名前を持ってくるときには必須です。

--dry-run("Run the sync without actually copying data to the destination") と --keep-going("If any errors occur during copying of images, those errors are logged and the process continues syncing rest of the images and finally fails at the end") は、持ち込み作業の自動化で特に役立ちます。

持ち込み対象の一覧が大きくなったら、YAML で管理するほうがよいです。ドキュメントが定義する形式はこうです。

# sync.yml — 持ち込み対象イメージの一覧
registry.access.redhat.com:
    images:
        ubi9/ubi:
            - "9.4"
            - "latest"
        ubi9/nginx-124: []
    images-by-tag-regex:
        ubi9/python-311: ^3\.11-[0-9]+$
    tls-verify: true
quay.io:
    images:
        skopeo/stable:
            - latest
# YAML の一覧どおりに一括で同期する
skopeo sync --src yaml --dest docker sync.yml registry.internal.example.com/mirror/

ドキュメントによると、空の一覧はすべてのタグを意味し、タグの位置にダイジェストを書くこともでき、images-by-tag-regex は正規表現で、images-by-semver は semver 制約でタグを選びます。このファイル自体を持ち込み記録として残せば、第 4 回のマニフェストと同じ役割を果たします。

skopeo sync には --multi-arch がありません。マルチアーキテクチャが必要なときは --all-a を使います。

署名を実際に強制します

閉域網でイメージ署名が意味を持つには、検証が 強制 されなければなりません。その設定が policy.json です。

ドキュメントによると、ポリシーは基本的に $HOME/.config/containers/policy.json から読み、なければ /etc/containers/policy.json から読みます。構造はグローバルな default とトランスポート別の transports の 2 部分で、"The global default set of policy requirements is mandatory" です。

{
    "default": [{"type": "reject"}],
    "transports": {
        "docker": {
            "registry.internal.example.com": [
                {
                    "type": "signedBy",
                    "keyType": "GPGKeys",
                    "keyPath": "/etc/pki/containers/internal-signing-key.gpg"
                }
            ]
        },
        "dir": {
            "": [{"type": "insecureAcceptAnything"}]
        }
    }
}

要求タイプごとの意味は、ドキュメントにこう定義されています。reject は "This requirement rejects every image, and every signature"、insecureAcceptAnything は "This requirement accepts any image (but note that other requirements in the array still apply)"、signedBy は "This requirement requires an image to be signed using "simple signing" with an expected identity, or accepts a signature if it is using an expected identity and key" です。keyType はドキュメント上いまのところ GPGKeys のみ対応で、keyPathkeyPathskeyData のうち正確に 1 つが存在する必要があります。

マッチング規則も知っておく必要があります。"If multiple policy requirements match a given image, only the requirements from the most specific match apply, the more general policy requirements definitions are ignored." 広い範囲に厳しいポリシーを掛けておき、狭い範囲に例外を置くと、その狭い範囲では広いほうのポリシーは 適用されません

署名を付けるのは skopeo 側の仕事です。

# 社内レジストリへ押し込みながら署名する
skopeo copy --sign-by security@example.com \
  dir:/media/transfer/images/ubi9 \
  docker://registry.internal.example.com/base/ubi9:9.4

--sign-by は "Add a "simple signing" signature using that key ID for an image name corresponding to destination-image" です。sigstore 方式が必要なら --sign-by-sigstore-private-key があります。

レジストリが署名の保存に対応していないなら、分離署名ストレージを使います。registries.d ドキュメントは lookaside を "URL of the signature storage. This URL is used for reading existing signatures, and if lookaside-staging does not exist, also for adding or removing them"、lookaside-staging を "URL of the signature storage, used for editing it (adding or deleting signatures)" と定義しています。

# /etc/containers/registries.d/internal.yaml
docker:
    registry.internal.example.com:
        lookaside: http://sigstore.internal.example.com/signatures
        lookaside-staging: file:///srv/signatures-staging

ドキュメントは sigstoresigstore-staging のキーがもはや文書化されていない古い名前だとソースコメントに明記しているので、新しく書くなら lookaside 表記を使ってください。

対象アーキテクチャを必ず指定します

この節は短いですが、失敗の頻度が高いところです。

# アーキテクチャを明示して取得する
podman pull --arch arm64 registry.access.redhat.com/ubi9/ubi:9.4

# OS とアーキテクチャを一度に (--arch, --os とは併用不可)
podman pull --platform linux/arm64 registry.access.redhat.com/ubi9/ubi:9.4

--arch は "Override the architecture, defaults to the host, of the image to be pulled"、--platform は "Specify the platform for selecting the image. (Conflicts with --arch and --os)" です。デフォルトがホストだという点が問題の出発点です。

--all-tags-a には別途の警告が付いています。"IMPORTANT: When using the all-tags flag, Podman does not iterate over the search registries in the containers-registries.conf(5) but always uses docker.io for unqualified image names." 閉域網の準備作業で短い名前とともにこのオプションを使うと、意図しないレジストリを向いてしまいます。

Kubernetes へつながります

ここまでがイメージを閉域網レジストリに載せるところまでです。そのイメージを実際のクラスターに使わせるのは別の主題で、このブログにすでに整理してある記事があります。

パッケージマネージャー自体を比較した npm, uv, rpm, brew の比較 も、rpm の位置づけを掴むのに役立ちます。

サブスクリプションなしに Red Hat コンテンツを再配布することは契約違反になりうるので、組織のライセンス条件を先に確認してください。Red Hat のコンテナイメージにも別途の利用規約が適用されるため、社内レジストリのミラーを構成する前に確認が必要です。

おわりに — 規模に合わせて道具を選んでください

イメージが 2、3 個なら podman savepodman load で十分です。数十個で定期作業なら skopeo sync が正解で、その間に skopeo copy があります。

どちらを選ぶにしても、2 つのことは落とさないでください。アーキテクチャを明示すること と、署名検証をポリシーで強制すること です。どちらも落としてもコマンドは成功し、問題は数週間後に表に出てきます。

コマンドとオプションは 2026-08-15 に公式ドキュメントで確認しました。RHEL のバージョンによって podman と skopeo の提供方式が異なるので、インストール方法は使用中のバージョンのドキュメントで改めて確認してください。

自分で試す

前回 / 次回

参考資料

현재 단락 (1/144)

パッケージの持ち込み手順が固まると、すぐ次の要求が来ます。コンテナイメージも同じ媒体で内側に入れてほしい、という話です。

작성 글자: 0원문 글자: 11,873작성 단락: 0/144