CI のパイプラインを組んでテストまで通るようになると、次は「できあがったものをどこに置くか」が問題になります。
Java なら jar、Node.js なら npm パッケージ、コンテナならイメージと、形は違ってもビルドの出力は保管して次の工程へ渡さなければなりません。
その置き場を受け持つのが、アーティファクトレジストリです。
一度だけビルドして置いておく理由と、置いたものの名付け方や運び方が分かると、CI と CD の境目にあるこの仕組みにも説明がつきます。
私も DevOps を学んでいる途中なので、公式ドキュメントと継続的デリバリーの提唱者が書いた資料をもとに組み立てました。
アーティファクトレジストリはビルドの成果物の置き場
アーティファクトは、ビルドの結果として出てくる成果物のことです。
ソースコードそのものとは違い、jar や npm パッケージ、コンテナイメージ、Helm チャートのように、配ったり動かしたりできる形になったものを指します。
コンテナイメージの配布の仕組みを定めた OCI Distribution Specification は、レジストリを「この仕様の API を提供するサービス」と定義しています。
その中でイメージの中身やタグをまとめて扱う単位がリポジトリで、1 つのレジストリにはアプリごとのリポジトリがいくつも並びます。
仕様の原文は OCI Distribution Specification です。
レジストリのサービスによって、置ける成果物の形式は違います。
| 置くもの | 形式の例 | 扱えるサービスの例 |
|---|---|---|
| 言語のパッケージ | npm・Maven など | GitHub Packages、Google Cloud の Artifact Registry |
| コンテナイメージ | Docker・OCI 形式のイメージ | Amazon ECR、GitHub Packages、Artifact Registry |
| OS のパッケージなど | Apt・RPM、Helm チャート、任意のファイル | Artifact Registry |
GitHub Packages は、ソースコードとパッケージを同じ場所に置き、権限の管理をまとめられることを特徴に挙げていました。
対応しているクライアントの一覧は Introduction to GitHub Packages(GitHub Docs) にあります。
置き場が要ること自体は昔から変わりません。
継続的デリバリーの提唱者の一人に、Dave Farley 氏がいます。
氏は 2007 年の論文「The Deployment Pipeline」で、ビルドしたバイナリをバージョン管理システムには入れていなかったと書いています。
手間に見合う利点がないという理由で、共有ファイルシステムの一角を専用の置き場にし、管理のスクリプトも自分たちで書いていたそうです。
当時は使える既製品が見つからなかったという書きぶりで、いまはこの置き場をサービスとして借りられるようになりました。
一度だけビルドして、同じものを各環境へ運ぶ
成果物を置いておく一番の理由は、環境ごとにビルドし直さずに済ませることです。
Farley 氏は、ソースコードしか保管していないと、デプロイのたびにコンパイルし直すことになると指摘しています。
性能テストの前にコンパイルし直せば、コンパイラやライブラリの版がいつの間にか変わり、テスト済みのものとは別物を試すおそれがあるという理屈です。
そこで、コミット直後のテストを通ったものを成果物として保管し、後の工程はそれをコピーして使う流れにしていました。
同じ論文は成果物に設定を含めないことも強調していて、狙いは同じバイナリをどの環境でも動かせるようにすることでした。
論文は The Deployment Pipeline(continuousdelivery.com・PDF) で読めます。
The Twelve-Factor App も、ビルド・リリース・実行の 3 段階をはっきり分けるよう求めています。
ビルドはコードを実行できる束に変える工程で、リリースはその束に環境ごとの設定を組み合わせる工程です。
リリースには日時や連番のような一意の ID を付け、作った後は書き換えず、変更があれば新しいリリースを作るとされています。
原文は V. Build, release, run(The Twelve-Factor App) です。
二つの資料を重ねると、レジストリは CI が作った成果物を CD のパイプラインへ渡す受け渡し口だと分かります。
いつでも出せる状態をパイプラインで保つ考え方は、継続的デリバリーの記事で扱いました。
名前の付け方と運び方を決める
置き場を用意したら、置いたものをどう呼び、どう次の環境へ渡すかを決めます。
タグは付け替えられ、ダイジェストは変わらない
コンテナイメージには、タグとダイジェストという 2 つの呼び方があります。
OCI の仕様では、ダイジェストは中身の暗号学的ハッシュから作る一意の識別子で、タグは人が読むための目印と定義されていました。
1 つのイメージにはタグを付けなくても、いくつ付けてもかまいません。
中身から決まる値という性質は、手元のファイルでも確かめられます。
Windows 11 の Git Bash(GNU coreutils 8.32 の sha256sum)で、同じ中身のファイルを 2 つの名前で作り、片方だけ中身を差し替えて比べました。
$ printf 'console.log("v1.4.0")\n' > app-1.4.0.js
$ cp app-1.4.0.js app-latest.js
$ sha256sum --tag app-1.4.0.js app-latest.js
SHA256 (app-1.4.0.js) = da99d823c41f6e686f5dcfe75b2678efbd6a1041e8bc608f6590fa18c7c2fdd5
SHA256 (app-latest.js) = da99d823c41f6e686f5dcfe75b2678efbd6a1041e8bc608f6590fa18c7c2fdd5
$ printf 'console.log("v1.4.1")\n' > app-latest.js
$ sha256sum --tag app-1.4.0.js app-latest.js
SHA256 (app-1.4.0.js) = da99d823c41f6e686f5dcfe75b2678efbd6a1041e8bc608f6590fa18c7c2fdd5
SHA256 (app-latest.js) = 2ad8e32c8aff4762e5502e56b72373a6f756e13e1e5cf04c2f824b485683ad3f
名前が違っても中身が同じならハッシュは同じで、名前が同じでも中身が変われば別の値になりました。
ファイル名をタグ、ハッシュをダイジェストに置き換えると、レジストリの中で起きていることと同じ関係になります。
ただし、イメージのダイジェストはイメージの構成を記した目録(マニフェスト)から計算されるので、ファイルのハッシュがそのままダイジェストになるわけではありません。
Kubernetes のドキュメントは、本番では :latest タグでデプロイしないよう勧めています。
どの版が動いているかを追いにくく、正しくロールバックするのも難しくなるからです。
代わりに示されているのは、v1.42.0 のような意味のあるタグか、ダイジェストで指定する形でした。
タグの指す中身が途中で変わると、古いコードと新しいコードの Pod が混ざって動くおそれがあるとも書かれています。
詳しくは Images(Kubernetes) のページにあります。
Pod の定義で書き分けると、次のようになります(書式の提示で、手元のクラスタでは動かしていません)。
containers:
- name: web
# タグで指す(同じタグのまま中身が差し替わることがある)
image: registry.example.com/web:1.4.0
# ダイジェストで指す(中身が 1 つに決まる)
# image: registry.example.com/web@sha256:<64 桁の 16 進数>
デプロイには使い回さないタグを付ける
Azure Container Registry のドキュメントは、タグの付け方を「安定タグ」と「一意タグ」に分けて説明しています。
安定タグは 1.0 のように使い回すタグで、セキュリティ修正などで中身が更新され続けます。
こちらはビルドの土台にするベースイメージ向けで、デプロイには使わないよう勧められていました。
デプロイに使うのは、push のたびに別の名前になる一意タグです。
台数を増やしたときや再起動したときに、ほかのノードと違う新しい版を取ってきてしまう事故を防げます。
一意タグの作り方として挙がっていたのは、次の 4 つです。
- 日時(いつ作ったかは分かるが、どのビルドかをたどりにくい)
- Git のコミット(ベースイメージだけ更新すると、同じコミットで中身の違うイメージができる)
- ダイジェスト(一意だが長くて読みにくく、ビルドとも結びつかない)
- ビルド ID(連番になりやすく、ビルドのログや成果物にたどれる)
この中ではビルド ID がいちばんよい選択肢になりうると書かれていて、出どころを逆にたどれることが理由でした。
ページは Image tag best practices(Microsoft Learn) です。
レジストリの側で、タグの上書きを禁止することもできます。
Amazon ECR の既定は、タグを上書きできる MUTABLE です。
IMMUTABLE に切り替えると、既にあるタグで push したときに ImageTagAlreadyExistsException のエラーで拒否されます。
設定のしかたは Preventing image tags from being overwritten in Amazon ECR にまとまっています。
環境の間はビルドし直さずに昇格させる
テスト用の置き場から本番用の置き場へ成果物を進めることを、昇格(プロモーション)と呼びます。
Kubernetes のプロジェクト自身も、公式のイメージをこの形で配っています。
イメージはまずステージング用のレジストリに置かれ、本番へ出すものをダイジェストとタグで YAML に並べて、プルリクエストで変更する仕組みです。
コピーには crane というツールを使い、元の目録と層のバイトを変えずに送るので、ダイジェストはそのまま保たれると説明されていました。
手順は Image Promotion(kubernetes-sigs/promo-tools) で読めます。
本番に届くのはテストを通ったのと同じダイジェストのイメージなので、テストしたものと出したものが違うという疑いが入り込みません。
置き場を運用するときの落とし穴
レジストリは置いて終わりではなく、増え続ける中身と、外部への依存に気を配る必要があります。
古いものを消すときに戻し先まで消してしまう
ビルドのたびに成果物が増えるので、何もしなければ保管の量は膨らみ続けます。
Farley 氏も、ほとんど使われないバイナリのために保管領域を大量に無駄にしやすいと書き、直近のビルドだけを残して古いものは消していました。
消した版に戻る必要が出たら、ソースのタグからパイプラインを流し直すという割り切りです。
Amazon ECR では、ライフサイクルポリシーで古いイメージを自動で消せます。
件数や push からの日数で対象を選び、条件を満たしたイメージは 24 時間以内に期限切れになります。
適用する前にプレビューで、どのイメージが消えるかを確かめるよう勧められていました。
タグの外れたイメージを push から 14 日で消すルールは、次のように書けます。
{
"rules": [
{
"rulePriority": 1,
"description": "Expire untagged images older than 14 days",
"selection": {
"tagStatus": "untagged",
"countType": "sinceImagePushed",
"countUnit": "days",
"countNumber": 14
},
"action": { "type": "expire" }
}
]
}
書式は AWS のドキュメントのテンプレートに合わせたもので、手元では Python 3.12 の json モジュールで読み込めることだけを確かめ、ECR には適用していません。
ルールの書き方と例は Automate the cleanup of images by using lifecycle policies in Amazon ECR にあります。
危ないのは、件数や日数で消すルールが、いま本番で動いている版やロールバック先まで対象にしてしまうことです。
Azure のドキュメントは、デプロイしたイメージのタグをロックして、うっかりレジストリから消さないようにすることを勧めていました。
ロックしたものは残したまま、それ以外の古いイメージを自動で消す組み合わせです。
本番で動いている版とロールバック先は、消す対象から外しておきます。
外部の公開レジストリに直接頼りきる
ベースイメージやライブラリを、ビルドのたびに公開レジストリから直接取ってくる構成にも弱点があります。
Docker Hub には pull の回数制限があります。
2026 年 10 月時点のドキュメントでは、ログインしない利用は IP アドレスごとに 6 時間で 100 回までです。
個人向けの Personal プランでログインした場合も、6 時間で 200 回までと書かれていました。
CI のランナーやクラスタのノードが同じアドレスから一斉に取りに行けば、それだけ上限に近づきます。
数字の出典は Docker Hub usage and limits(Docker Docs) です。
主要なクラウドのレジストリには、公開レジストリの中身を手元に写して使う仕組みが用意されています。
Google Cloud の Artifact Registry では、これをリモートリポジトリと呼びます。
依存物を自分たちの管理下に置き、脆弱性のスキャンもかけられるようにすることが目的だと説明されていました。
自前のリポジトリとリモートリポジトリを 1 つの入り口の後ろにまとめ、探す順番を決められる仮想リポジトリもあります。
形式とリポジトリの種類の一覧は Artifact Registry overview(Google Cloud) です。
Amazon ECR でも、プルスルーキャッシュのルールを設定すると、上流のレジストリの中身を自分のレジストリに写し、定期的に新しくしてくれます。
機能の一覧は What is Amazon Elastic Container Registry? のページで読めます。
取りに行く先が自分たちのレジストリにまとまるので、公開レジストリの回数制限にも左右されにくくなるでしょう。
最後に
アーティファクトレジストリは、一度だけビルドした成果物を置き、同じものを各環境へ運ぶための受け渡し口でした。
環境ごとにビルドし直さないことで、テストしたものと本番に出すものが同じだと言えるようになります。
運ぶときは一意のタグかダイジェストで指し、環境の間はコピーで昇格させます。
置いたものは増え続けるので、消すルールと、本番やロールバック先を守るルールを一緒に決めておくことが欠かせません。
どのイメージを本番で動かすかを Git に書いて合わせ続ける運用は、GitOps の記事で扱っています。
以上です。














コメントを残す