Artifact Registry ADR 009: API 設計
コンテキスト
Artifact Registry は、次の制約を持つ包括的な API 設計を必要とします。
- 3 つの API カテゴリ: レジストリの概念とやり取りする管理 API、特定のクライアントが使用する厳密な仕様に従うアーティファクト管理クライアント API、そしてプラットフォームとレジストリ間の通信のための GitLab API。
- データベースのデータ構成に従う: API エンドポイントはアーティファクトフォーマットごとに構成され、リポジトリがフォーマットファミリーごとのテーブルとフィールドを持つデータベーススキーマ に一致します(
dockerとociはcontainer_*テーブルを共有します)。このマッピングは、複雑なマルチフォーマット抽象化を回避することで実装を簡素化し、フォーマット固有の最適化を可能にします。 - 3 つのリポジトリタイプ: レジストリは 3 つのリポジトリタイプをサポートします。ホスト型(プッシュされたアーティファクトのためのプライベートストレージ)、リモート(外部レジストリのためのプロキシ/キャッシュ)、仮想(ホスト型とリモートのアップストリームを組み合わせた集約された pull エンドポイント)です。このモデルは業界の慣行に一致します。3 つのタイプはすべて独立しており、個別に管理可能なエンティティです。
Artifact Registry は、すべてのお客様向け URL に現れる不変で顧客が選択するスラッグ(ADR-022)を通じて、Organizations にスコープされます(根拠については ADR-001 を参照)。すべてのクライアントおよび管理エンドポイントは、API バージョンプレフィックス(またはクライアント API の場合はプロトコルプレフィックス)の後に /:slug パスセグメントを含み、すべてのリクエストを特定のスラッグにスコープします。スラッグは、組織パスの解決に依存することなく、トポロジーサービスを介した安定した Cells ルーティングを提供します。例外は 2 つあります。OCI で必須の GET /v2/ バージョンプローブ(Container を参照)と GitLab API エンドポイント(GitLab API を参照)です。
Artifact Registry は、メインの GitLab アプリケーションドメインとは別の、専用ドメイン(例: artifact-registry.gitlab.com)で提供されます。これにより、メインアプリケーションの Cookie、認証情報、内部ネットワークコンテキストからレジストリを分離することで、XSS および SSRF の脆弱性に対するセキュリティ態勢が劇的に改善されます。
この ADR は API サーフェス、それらに適用されるルール、エンドポイント構造を定義します。リクエストおよびレスポンスのペイロードは、Artifact Registry コードベース内の対応する OpenAPI 仕様に存在します。
バージョニング
Artifact Registry は専用ドメインで動作するため、GitLab Rails モノリスで使用される /api/v4 プレフィックスは適用されません。モノリスは、同一ドメイン上で API ルートを Web UI ルートから分離し、その API バージョンの系譜(v3 から v4 へ)を反映するために /api/v4 を使用します。スタンドアロンサービスにはどちらの懸念も存在しません。
管理 API ルートは /api/v1 パスベースのバージョンプレフィックスを使用します。/api/ プレフィックスは、同一ドメイン上で管理ルートをプロトコル固有のクライアントルートから分離し、管理 API バージョンが将来引き上げられた場合の namespace 衝突を防ぎます(例: OCI は /v2/ を必須とするため、裸の /v2 管理プレフィックスは衝突します)。これは業界の慣行に一致します。パスベースのバージョニングは、ヘッダー検査を必要とせずに、URL、ログ、ルーティングルールにおいてバージョンを可視に保ちます。
クライアント API はバージョンプレフィックスを使用しません。プロトコル固有のバージョニングは、必要に応じてクライアント自身によって処理されます(例: OCI は /v2/ を必須とします)。これにより、クライアントが構成する URL を可能な限り短く保ちます。
GitLab API ルートは /api/gitlab/v1 プレフィックスを使用します。明示的な gitlab セグメントにより、サーフェスは URL、ログ、ルーティングルールで可視になり、エッジが専用のパスルールを適用できます。これは、レジストリからプラットフォームへのサーフェスで /gitlab/v1/ プレフィックスを使用する Container Registry の先例に従います。
API 分類
API の表面は、異なるルールを持つ 3 つの明確なカテゴリに分けられます。
管理 API: レジストリの概念(リポジトリ、アーティファクト、ポリシーなど)に対する CRUD 操作のための REST および GraphQL API
- 認証: GitLab の標準的な REST/GraphQL 認証
- 目的: UI、自動化スクリプト、管理ツール
- フォーマット: 標準的な GitLab API パターンを持つ JSON レスポンス
- ページネーション: すべてのリストエンドポイントはページネーションされ、できればキーセットページネーション戦略を使用します
アーティファクト管理クライアント API: 業界標準の仕様を実装するプロトコル固有の API
- 認証: プロトコル固有(OCI 用の Bearer トークン、Maven 用の Basic 認証など)
- 目的: ネイティブクライアントの互換性(
docker、npm、mvnコマンド) - フォーマット: プロトコル固有のレスポンス(OCI Distribution Spec、Maven Repository Layout、NPM Registry API)
GitLab API: プラットフォームとレジストリ間の通信のための REST エンドポイント
- 認証: サービス間認証情報。エンドユーザー ID は使用しない
- 目的: ネームスペースのプロビジョニング、解決、サービス条件、リソース検証
- フォーマット: JSON
- 公開範囲: 信頼されたプラットフォーム呼び出し元のみ
管理 API とクライアント API は、3 つのリポジトリタイプすべて(ホスト型、仮想、リモート)を提供します。クライアント API はプロトコルごとに構成されます(プロトコルごとに 1 セットのエンドポイント。OCI Distribution Spec のエンドポイントは、docker と oci の両フォーマットのリポジトリを提供します)が、管理 API は統合されたリポジトリ CRUD を共有し、フォーマットはフォーマット固有のサブリソースにのみ現れます。
URL 構造設計
管理 API ルートは リポジトリにアンカーされます。/api/v1/:slug プレフィックスはすべてのルートで必須であり、概念上のレベルとしてはカウントされません。リポジトリエンドポイントは、一意のリポジトリ名を :repository_name パラメータとして受け取ります。
すべてのリポジトリレベルのリソース(アーティファクト、ライフサイクルポリシー、アップストリームの関連付け)はリポジトリにスコープされます。これはホスト型とリモートのリポジトリが別個のフォーマットファミリーごとのテーブルにデータを保存するためです。
:format セグメントは、すべてのフォーマット固有のサブリソースに対して リポジトリの後 に現れます: /api/v1/:slug/repositories/:repository_name/:format/...(例: イメージのリスト、ライフサイクルポリシー)。リポジトリレベルのサブリソースは特定のフォーマットに専用であり、エンドポイントの構成と返される構造のカスタマイズにおいてより高い柔軟性を可能にします。これにはアーティファクト操作とフォーマット固有の構成の両方が含まれます。namespace レベルのフォーマット固有の操作は :format をプレフィックスとして使用します: /api/v1/:slug/:format/statistics。
リポジトリ CRUD 自体はフォーマットフリーです。これは repositories 親テーブルがすべてのフォーマットとタイプにわたって共有されるためです。フォーマットと種類はリポジトリリソースのプロパティであり、URL セグメントではありません。
例えば:
- すべてのリポジトリのリスト:
GET /api/v1/:slug/repositories - リポジトリの作成:
POST /api/v1/:slug/repositories - リポジトリの読み取り/更新/削除:
GET/PATCH/DELETE /api/v1/:slug/repositories/:repository_name(対象概念の識別子を使用するトップレベルルート) - コンテナファミリーのリポジトリ内のイメージのリスト:
GET /api/v1/:slug/repositories/:repository_name/:format/images(フォーマット固有のサブリソース。:formatはdockerまたはoci) - ID によるイメージの取得:
GET /api/v1/:slug/repositories/:repository_name/:format/images/:image_id(アーティファクト詳細、リポジトリにスコープ)
リポジトリ名の不変性
リポジトリ名は作成時に設定され、変更できません。リポジトリの説明は可変のままです。これは、壊れたクライアント構成、名前の再取得による認可バイパス、黙ったミスルーティングを防ぎます。また、名前ベースの URL(下記を参照)を可能にし、認可ルールを簡素化します。
これは業界の慣行に一致します。詳細は gitlab-org/gitlab#592582 を参照してください。
人間にやさしい URL
リポジトリエンドポイントは、一意の名前を識別子として受け取ります。リポジトリ名は、フォーマットやリポジトリタイプに関係なく、スラッグ内でグローバルに一意でなければなりません。このグローバルな一意性制約は業界の慣行に一致し、あいまいな名前ベースのルックアップを回避します。後でフォーマットごとの一意性へと緩和しても誰も壊しませんが、厳格化は破壊的な変更となります。名前の不変性(上記を参照)により、名前ベースの URL を安定したパスセグメントとして安全にします。スラッグは不変であるため(ADR-022)、URL パス内のすべてのセグメントは人間が読めて永続的に安定しています。
API 編成
管理用 API
管理 API は GitLab の REST API 認証 を使用します。
注: :format はリポジトリの format 値(docker、oci、maven、または npm)を表します。docker と oci はコンテナファミリーを形成します。これらはコンテナアーティファクトモデル(イメージ、タグ、マニフェスト)と container_* テーブルを共有します。:format セグメントがリポジトリのフォーマットと一致しないリクエストは、404 Not Found を返します。
ネームスペースレベル API
リポジトリ管理:
GET /api/v1/:slug/repositories- すべてのリポジトリをリスト(すべてのフォーマットにわたるホスト型、仮想、リモート)。フォーマットとリポジトリタイプによるフィルタリングをサポートPOST /api/v1/:slug/repositories- リポジトリを作成GET /api/v1/:slug/repositories/:repository_name- リポジトリの詳細を取得PATCH /api/v1/:slug/repositories/:repository_name- リポジトリを更新DELETE /api/v1/:slug/repositories/:repository_name- リポジトリを削除
リポジトリ詳細のレスポンスはポリモーフィックであり、その形状はフォーマットと種類によって異なります。
- すべてのリポジトリは、親
repositoriesテーブルからの共通フィールドを返します:name、format、kind、visibility、description、カウンター(artifacts_count、downloads_count、size_bytes)、last_updated_at。 - フォーマット固有およびタイプ固有のフィールドは、オプションのトップレベルキーとして現れるのではなく、単一の
settingsオブジェクトの下にネストされます。formatとkindのフィールドは判別子として機能します。クライアントはこれらを使用してsettingsの形状を解釈します。 POSTとPATCHは、作成および更新操作に対して同じネストされた構造を受け取ります。
統計:
GET /api/v1/:slug/statistics- 集約されたストレージとダウンロードの統計を取得GET /api/v1/:slug/:format/statistics- 特定のフォーマットのストレージとダウンロードの統計を取得
ライフサイクルポリシー:
GET /api/v1/:slug/lifecycle_policy- ライフサイクルポリシーを取得PATCH /api/v1/:slug/lifecycle_policy- ライフサイクルポリシーを更新GET /api/v1/:slug/lifecycle_policy/rules- ライフサイクルポリシールールを取得POST /api/v1/:slug/lifecycle_policy/rules- ライフサイクルポリシールールを作成GET /api/v1/:slug/lifecycle_policy/rules/:rule_id- ライフサイクルポリシールールを取得PATCH /api/v1/:slug/lifecycle_policy/rules/:rule_id- ライフサイクルポリシールールを更新DELETE /api/v1/:slug/lifecycle_policy/rules/:rule_id- ライフサイクルポリシールールを削除
リポジトリレベル API
仮想リポジトリ - アップストリーム:
アップストリームは、フォーマット固有のアップストリームルールを持つフォーマットファミリーごとのテーブル(docker と oci で共有される container_virtual_repository_upstreams、および maven_virtual_repository_upstreams と npm_virtual_repository_upstreams)に保存されます。リモートとホスト型のリポジトリは独立したエンティティであるため、仮想リポジトリのアップストリームは既存のリポジトリへの参照です。アップストリームタイプ(ホスト型またはリモート)は、参照されるリポジトリの kind によって決定されます。
GET /api/v1/:slug/repositories/:repository_name/:format/upstream_repositories- 仮想リポジトリのアップストリームリポジトリ(ホスト型とリモート)を、解決の優先順位順にリストPOST /api/v1/:slug/repositories/:repository_name/:format/upstream_repositories- リポジトリ(ホスト型またはリモート)を仮想リポジトリのアップストリームとして関連付け。upstream_repository_idを受け取るGET /api/v1/:slug/repositories/:repository_name/:format/upstream_repositories/:id- アップストリームリポジトリの関連付けを取得PATCH /api/v1/:slug/repositories/:repository_name/:format/upstream_repositories/:id- 関連付けの位置を更新。positionフィールドのみ更新可能DELETE /api/v1/:slug/repositories/:repository_name/:format/upstream_repositories/:id- 仮想リポジトリからアップストリームの関連付けを解除
リモートリポジトリ - 接続テスト:
POST /api/v1/:slug/repositories/:repository_name/test- 構成されたリモートレジストリへの接続をテスト
統計:
GET /api/v1/:slug/repositories/:repository_name/statistics- リポジトリのストレージとダウンロードの統計を取得
ライフサイクルポリシー:
リポジトリレベルのライフサイクルポリシーは、namespace レベルのデフォルトを上書きするフォーマットファミリーごとのテーブル(container_repository_lifecycle_policy_settings など)を使用します。
GET /api/v1/:slug/repositories/:repository_name/:format/lifecycle_policy- リポジトリのライフサイクルポリシーを取得PATCH /api/v1/:slug/repositories/:repository_name/:format/lifecycle_policy- リポジトリのライフサイクルポリシーを更新GET /api/v1/:slug/repositories/:repository_name/:format/lifecycle_policy/rules- リポジトリのライフサイクルポリシールールを取得POST /api/v1/:slug/repositories/:repository_name/:format/lifecycle_policy/rules- リポジトリのライフサイクルポリシールールを作成GET /api/v1/:slug/repositories/:repository_name/:format/lifecycle_policy/rules/:rule_id- ライフサイクルポリシールールを取得PATCH /api/v1/:slug/repositories/:repository_name/:format/lifecycle_policy/rules/:rule_id- ライフサイクルポリシールールを更新DELETE /api/v1/:slug/repositories/:repository_name/:format/lifecycle_policy/rules/:rule_id- ライフサイクルポリシールールを削除
フォーマット固有のアーティファクト API
すべてのアーティファクトエンドポイントはリポジトリにスコープされ、ホスト型とリモートの両方のリポジトリに一様に適用されます。ルート、動詞、ページネーションは両方の種類で同一です。リモートリポジトリは、ホスト型スキーマをミラーリングする階層テーブル(*_remote_images、*_remote_packages、*_remote_versions、*_remote_files など)にアーティファクトをキャッシュします(ADR-007 を参照)。
レスポンスボディはリポジトリの kind によってポリモーフィックです。これは リポジトリ詳細の settings オブジェクト で使用されるのと同じ慣例です。kind が remote の場合、鮮度追跡された行(タグ、パッケージファイル、メタデータファイル)にマッピングされる各エントリは、upstream_checked_at と upstream_etag を公開するネストされた cache オブジェクトを持ちます。コンテナのマニフェストと blob はダイジェストによってコンテンツアドレス指定され、行ごとの鮮度を持たないため、cache ブロックを持ちません。リポジトリレベルのキャッシュ構成(cache_validity_hours、metadata_cache_validity_hours)はリポジトリ詳細の settings オブジェクトに存在し、アーティファクトごとには反映されません。ホスト型リポジトリに対するリクエストは、cache オブジェクト なし で同じ形状を返します。
リモートリポジトリ上の動詞のセマンティクスは、アップストリームではなくキャッシュされた行を記述します。
DELETEはキャッシュされた行を退避します(アップストリームへの影響なし。アーティファクトはクライアント API を通じた次の pull で再取得されます)。PATCH .../quarantineはキャッシュされた行をブロック済みとしてフラグします。アップストリームにまだアーティファクトがあっても、クライアントの pull は404 Not Foundを返します。フラグはキャッシュされた行のライフサイクルに紐付けられ、退避するとクリアされます。永続的またはダイジェストレベルのブロックは意図的にスコープ外です(API ではなくライフサイクル管理の関心事)。GETは、上記のcacheサブオブジェクトを除いて両方の種類で同一です。
コンテナファミリー固有 - イメージ:
コンテナファミリーのリポジトリでは、アーティファクトを「イメージ」と呼びます。これらのルートの :format セグメントは、リポジトリのフォーマットである docker または oci です:
GET /api/v1/:slug/repositories/:repository_name/:format/images- リポジトリ内のイメージをリストGET /api/v1/:slug/repositories/:repository_name/:format/images/:image_id- イメージの詳細を取得GET /api/v1/:slug/repositories/:repository_name/:format/images/:image_id/manifests- 所定のイメージのマニフェストをリストGET /api/v1/:slug/repositories/:repository_name/:format/images/:image_id/blobs- 所定のイメージの blob をリストGET /api/v1/:slug/repositories/:repository_name/:format/images/:image_id/statistics- イメージのストレージ、使用量、ダウンロードの統計を取得DELETE /api/v1/:slug/repositories/:repository_name/:format/images/:image_id- イメージを削除(ソフトまたはハード削除)DELETE /api/v1/:slug/repositories/:repository_name/:format/images- イメージを一括削除PATCH /api/v1/:slug/repositories/:repository_name/:format/images/:image_id/quarantine- 所定のイメージを隔離
コンテナファミリー固有 - イメージタグ:
GET /api/v1/:slug/repositories/:repository_name/:format/images/:image_id/tags- 所定のイメージのタグをリストGET /api/v1/:slug/repositories/:repository_name/:format/images/:image_id/tags/:tag/statistics- タグのストレージ、使用量、ダウンロードの統計を取得DELETE /api/v1/:slug/repositories/:repository_name/:format/images/:image_id/tags/:tag- イメージタグを削除DELETE /api/v1/:slug/repositories/:repository_name/:format/images/:image_id/tags- 一連のイメージタグを削除
Maven/NPM 固有 - パッケージ:
GET /api/v1/:slug/repositories/:repository_name/:format/packages- リポジトリ内のパッケージをリストGET /api/v1/:slug/repositories/:repository_name/:format/packages/:package_id- パッケージの詳細を取得GET /api/v1/:slug/repositories/:repository_name/:format/packages/:package_id/statistics- パッケージのストレージ、使用量、ダウンロードの統計を取得DELETE /api/v1/:slug/repositories/:repository_name/:format/packages/:package_id- パッケージを削除(ソフトまたはハード削除)DELETE /api/v1/:slug/repositories/:repository_name/:format/packages- パッケージを一括削除PATCH /api/v1/:slug/repositories/:repository_name/:format/packages/:package_id/quarantine- 所定のパッケージを隔離GET /api/v1/:slug/repositories/:repository_name/:format/packages/:package_id/versions- 所定のパッケージのバージョンをリスト
Maven/NPM 固有 - パッケージバージョン:
GET /api/v1/:slug/repositories/:repository_name/:format/versions/:version_id- バージョンの詳細を取得GET /api/v1/:slug/repositories/:repository_name/:format/versions/:version_id/statistics- パッケージバージョンのストレージ、使用量、ダウンロードの統計を取得DELETE /api/v1/:slug/repositories/:repository_name/:format/versions/:version_id- バージョンを削除(ソフトまたはハード削除)DELETE /api/v1/:slug/repositories/:repository_name/:format/versions- バージョンを一括削除GET /api/v1/:slug/repositories/:repository_name/:format/versions/:version_id/files- 所定のバージョンのファイルをリスト
Maven/NPM 固有 - パッケージファイル:
GET /api/v1/:slug/repositories/:repository_name/:format/files/:file_id- ファイルの詳細を取得GET /api/v1/:slug/repositories/:repository_name/:format/files/:file_id/download- ファイルをダウンロードDELETE /api/v1/:slug/repositories/:repository_name/:format/files/:file_id- ファイルを削除(ソフトまたはハード削除)DELETE /api/v1/:slug/repositories/:repository_name/:format/files- ファイルを一括削除
NPM 固有 - 配布タグ:
GET /api/v1/:slug/repositories/:repository_name/npm/packages/:package_id/tags- 所定のパッケージのタグをリストGET /api/v1/:slug/repositories/:repository_name/npm/tags/:tag_id- タグの詳細を取得GET /api/v1/:slug/repositories/:repository_name/npm/tags/:tag_id/statistics- タグのストレージ、使用量、ダウンロードの統計を取得DELETE /api/v1/:slug/repositories/:repository_name/npm/tags/:tag_id- タグを削除
アーティファクト管理クライアント API
クライアント API の URL は、すべてのリポジトリタイプ(ホスト型、リモート、仮想)で同じです。レジストリはリポジトリの種類を内部的に解決し、タイプ固有の挙動を適用します(例: リモートおよび仮想リポジトリへの書き込みを拒否する)。
Container
OCI Distribution Spec v1.1 を実装します。認証: Bearer トークン。
以下のリテラルの container パスセグメントはプロトコルレベルで固定されています。1 セットの /v2 エンドポイントが、docker と oci の両フォーマットのリポジトリを提供します。これはルーターが挿入するリテラルであり、リポジトリの format 値ではありません(管理 API を参照)。
GET /v2/- API バージョンとレジストリ実装を確認(OCI 必須、スラッグにスコープされない)GET /v2/:slug/container/:repository_name/:image_name/manifests/:reference- マニフェストを取得(reference はタグまたはダイジェスト)HEAD /v2/:slug/container/:repository_name/:image_name/manifests/:reference- マニフェストの存在を確認PUT /v2/:slug/container/:repository_name/:image_name/manifests/:reference- マニフェストをアップロード(リモートおよび仮想リポジトリでは利用不可)DELETE /v2/:slug/container/:repository_name/:image_name/manifests/:reference- マニフェストを削除(ダイジェストまたはタグ参照による、リモートおよび仮想リポジトリでは利用不可)DELETE /v2/:slug/container/:repository_name/:image_name/manifests/:tag- 特定のタグを削除(リモートおよび仮想リポジトリでは利用不可)GET /v2/:slug/container/:repository_name/:image_name/blobs/:digest- blob をダウンロードHEAD /v2/:slug/container/:repository_name/:image_name/blobs/:digest- blob の存在を確認DELETE /v2/:slug/container/:repository_name/:image_name/blobs/:digest- blob を削除(リモートおよび仮想リポジトリでは利用不可)POST /v2/:slug/container/:repository_name/:image_name/blobs/uploads/- blob アップロードを開始(リモートおよび仮想リポジトリでは利用不可)PATCH /v2/:slug/container/:repository_name/:image_name/blobs/uploads/:uuid- blob チャンクをアップロード(リモートおよび仮想リポジトリでは利用不可)GET /v2/:slug/container/:repository_name/:image_name/blobs/uploads/:uuid- blob アップロードのステータスを取得(再開可能なアップロード用、リモートおよび仮想リポジトリでは利用不可)PUT /v2/:slug/container/:repository_name/:image_name/blobs/uploads/:uuid?digest=:digest- blob アップロードを完了(リモートおよび仮想リポジトリでは利用不可)DELETE /v2/:slug/container/:repository_name/:image_name/blobs/uploads/:uuid- blob アップロードをキャンセル(リモートおよび仮想リポジトリでは利用不可)POST /v2/:slug/container/:repository_name/:image_name/blobs/uploads/?digest=:digest- 単一リクエストで完全な blob をアップロード(リモートおよび仮想リポジトリでは利用不可)GET /v2/:slug/container/:repository_name/:image_name/tags/list- リポジトリ内のすべてのタグをリストGET /v2/:slug/container/:repository_name/:image_name/tags/list?n=100&last=tag_name- ページネーションされたタグのリストGET /v2/:slug/container/:repository_name/:image_name/referrers/:digest- マニフェストを参照するアーティファクト/アテステーションをリストGET /v2/:slug/container/:repository_name/:image_name/referrers/:digest?artifactType=<type>- アーティファクトタイプで referrer をフィルタリング
注: OCI 必須の GET /v2/ エンドポイントは /:slug プレフィックスを含まないため、Cells ルーターはパスだけからどの Cell がリクエストを処理すべきかを判断できません。GET /v2/ はステートレスなバージョンプローブ(OCI 準拠を示す 200 OK、そうでなければ 401 Unauthorized)であるため、どの Cell でも提供できます。スラッグやルーティングコンテキストは不要です。他のすべてのクライアントリクエストは /:slug セグメントを持ち、Cells ルーターがそれを使用してターゲット Cell を判断します。クライアントは glab を介してクライアント側で認証情報を取得し(ADR-020 を参照)、最初から Bearer トークンを提示するため、OCI の 401 WWW-Authenticate リダイレクトチャレンジは使用されません。GET /v2/_catalog(Docker Registry HTTP API V2)は OCI Distribution Spec の一部ではなく、実装されません。
クライアント設定例
スラッグ acme-engineering で、リポジトリ名 my-repo、イメージ名 my-app、タグ latest のコンテナファミリーのリポジトリからイメージを pull する場合:
docker pull artifact-registry.gitlab.com/acme-engineering/container/my-repo/my-app:latest
Maven
Maven Repository Layout を実装します。認証: Basic 認証。カスタムヘッダー認証(元の GitLab Maven パッケージレジストリからのレガシーメカニズム)はサポートされません。Basic 認証は Maven レジストリ全体で普遍的な標準であり、すべての主要なビルドツール(mvn、gradle、sbt)でサポートされています。特に sbt は Basic 認証のみをサポートします。
GET /:slug/maven/:repository_name/*path/:file_name- Maven のホスト型、リモート、仮想リポジトリからパッケージファイルをダウンロードPUT /:slug/maven/:repository_name/*path/:file_name- Maven のホスト型リポジトリにパッケージファイルをアップロード(リモートおよび仮想リポジトリでは利用不可)
クライアント設定例
スラッグ acme-engineering の my-repo という名前の Maven リポジトリのリポジトリ URL。settings.xml、build.gradle、または build.sbt で使用します:
https://artifact-registry.gitlab.com/acme-engineering/maven/my-repo
NPM
NPM Registry API を実装します。認証: Bearer トークン。
GET /:slug/npm/:repository_name/:package_name- パッケージメタデータを取得PUT /:slug/npm/:repository_name/:package_name- パッケージを公開(リモートおよび仮想リポジトリでは利用不可)PUT /:slug/npm/:repository_name/:package_name/-rev/:rev- 単一バージョンの公開取り消し、ステップ 1: そのバージョンを削除したパッケージドキュメントに置き換える(リモートおよび仮想リポジトリでは利用不可)DELETE /:slug/npm/:repository_name/:package_name/-/:file_name/-rev/:rev- 単一バージョンの公開取り消し、ステップ 2: バージョンの tarball を削除(リモートおよび仮想リポジトリでは利用不可)DELETE /:slug/npm/:repository_name/:package_name/-rev/:rev- パッケージ全体の公開取り消し(npm unpublish <pkg> --force、リモートおよび仮想リポジトリでは利用不可)GET /:slug/npm/:repository_name/:package_name/-/:file_name- パッケージファイルをダウンロードGET /:slug/npm/:repository_name/-/package/:package_name/dist-tags- パッケージの dist-tags をリストPUT /:slug/npm/:repository_name/-/package/:package_name/dist-tags/:tag- dist-tag を作成または更新(リモートおよび仮想リポジトリでは利用不可)DELETE /:slug/npm/:repository_name/-/package/:package_name/dist-tags/:tag- dist-tag を削除(リモートおよび仮想リポジトリでは利用不可)POST /:slug/npm/:repository_name/-/npm/v1/security/audits/quick- クイックセキュリティ監査POST /:slug/npm/:repository_name/-/npm/v1/security/advisories/bulk- 一括セキュリティアドバイザリ
クライアント設定例
スラッグ acme-engineering の my-repo という名前の NPM リポジトリのレジストリ URL。.npmrc で使用します:
https://artifact-registry.gitlab.com/acme-engineering/npm/my-repo/
GitLab API
GitLab API は、最初は Rails モノリスであるプラットフォーム呼び出し元にサービスを提供します。リクエストは呼び出すサービスを認証し、エンドユーザーを認証することはありません。ユーザーのチェックは呼び出しをトリガーするプラットフォームフローに属し、ユーザー向けトラフィックは代わりに管理 API とクライアント API を使用します(ADR-014、ADR-021)。「GitLab」は呼び出し元の名前です。つまり GitLab プラットフォームデプロイメントであり、ハイブリッドデプロイメントモデルではお客様がホストするモノリスです。そのためこのサーフェスはインターネット向けですが、お客様向けではありません。
ハイブリッドデプロイメントモデル(SaaS レジストリに対する Self-Managed または Dedicated モノリス)を含め、このサーフェスで呼び出し元を認証する方法は、ADR-020 と ADR-021 で扱います(artifact-registry#255)。
ネームスペースのライフサイクル
これらのエンドポイントにはスラッグが不要です。プロビジョニングはスラッグが取得される前に実行され、解決は UUID をスラッグに変換し、サービス条件はスラッグの状態にかかわらず機能する必要があるためです。ネームスペースを所有者アンカー(ADR-001)または UUID(ADR-022)で参照します。
POST /api/gitlab/v1/namespaces- 所有者アンカーのネームスペースをプロビジョニングします(gitlab#603023)。アンカーに対して冪等であり、呼び出し元が永続化するネームスペース UUID を返します(ADR-022)。GET /api/gitlab/v1/namespaces/:uuid- UUID でネームスペースを解決します。所有者アンカー、スラッグ、ステータスを返します。呼び出し元はスラッグとステータスをキャッシュします(ADR-022、ADR-014)。POST /api/gitlab/v1/namespaces/:uuid/<action>- 可逆的なサービス条件を適用または解除します(ADR-007)。アクションごとに 1 つのエンドポイントを設け、<action>はblock、unblock、disable、enable、suspend、unsuspendのいずれかです。
スラッグが不要なルートは Cells ルーティングを弱めません。ネームスペースの作成はキーでルーティングできません。まだルーティングの対象となるものが存在しないためです。Cell に存在するモノリスはその Cell ローカルのレジストリを直接呼び出し、ネームスペースはその Cell に配置されます。ハイブリッドの呼び出し元には Cell がないため、その呼び出しはエッジに入り、エッジが対象 Cell を決定します。どちらのパスもネームスペース UUID をルーティングキーとして登録します(スラッグの取得時には第 2 のキーとして追加されます)。他のすべての GitLab API エンドポイントはその UUID をキーとし、呼び出し元の場所にかかわらず、スラッグプレフィックス付きリクエストと同じトポロジー機構を経由してルーティングされます。UUID をキーにしたルーティングが十分でないと判明した場合、Container Registry で以前に検討されたように、GitLab API リクエストは所有者アンカーをルーティングヒントとして追加で埋め込むこともできます。これは潜在的な経路であり、現時点の要件ではありません。
リソース検証
プラットフォームの認可フローは、一括ロール付与の対象リソースがネームスペースに属することを確認しなければなりません(artifact-registry#181)。ネームスペース対象の付与にはレジストリ呼び出しは不要です。プラットフォームが永続化されたマッピングに対して所有権を検証します(gitlab#603023)。リポジトリ対象の付与は、ネームスペース UUID にスコープされ、単一の HASH(namespace_id) パーティションから提供されるバッチ検証を使用します。
POST /api/gitlab/v1/namespaces/:uuid/repositories/verifications- 指定されたリポジトリ ID がネームスペース配下に存在することを一括検証します。データは返しません。すべての ID が属する場合は204、失敗時にはすべて同じ不透明な400を返し、ID の存在有無を明らかにしません。
レジストリは Organization データを返しません。呼び出し元はネームスペーススコープを提供し、その所有者をすでに把握しています。将来の割り当て可能なリソースタイプは、同じタイプ別のパターン(POST /api/gitlab/v1/namespaces/:uuid/<resource_type>/verifications)に従います。
GitLab API は、提供先のプラットフォームバージョン間で後方互換性を維持しなければなりません。ハイブリッドデプロイメントモデルでは、Self-Managed モノリスは SaaS レジストリより遅れることがあります(ADR-022)。
帰結
ポジティブ
- 明確な関心の分離: 3 つの API サーフェスは明確な目的、認証メカニズム、ターゲットオーディエンスを持ち、混乱を減らし、独立した進化を可能にします
- リポジトリにアンカーされた URL パターン: すべてのアーティファクト操作はリポジトリにスコープされ、明確なルーティングコンテキストを提供し、ホスト型とリモートの両方のリポジトリに対して同じ URL 構造を可能にします
- 永続的に安定した、人間が読める URL: スラッグとリポジトリ名はどちらも不変であるため、すべての URL パスセグメントは人間が読めて決して変わりません。クライアント向けのどの URL にも数値 ID は現れません
- 統合されたハイブリッドリスト: 単一のエンドポイントが、すべてのフォーマットにわたるすべてのリポジトリ(ホスト型、仮想、リモート)をフィルタリング付きでリストし、プラットフォームエンジニア向けのフォーマット横断のガバナンスと監査性のビューを可能にします
- 独立したエンティティとしてのリモートリポジトリ: リモートリポジトリは個別に管理可能で、複数の仮想リポジトリにまたがって再利用でき、独自のライフサイクルを持ち、業界の慣行(JFrog Artifactory、Sonatype Nexus、Google Cloud AR)に一致します
- 一様なホスト型とリモートのアーティファクト構造: リモートリポジトリはホスト型リポジトリと同じアーティファクト階層(images、packages、versions、files)を使用します。同じルートが両方の種類を提供し、それを追跡する行にネストされた
cacheサブオブジェクトが鮮度メタデータ(upstream_checked_at、upstream_etag)を表面化します。キャッシュの変更はホスト型リポジトリと同じ動詞を再利用するため(DELETE= 退避、PATCH .../quarantine= ブロック)、リモートリポジトリに対して API の表面は拡大しません。レスポンスボディのみがポリモーフィックです - 簡素化されたアップストリームモデル: 仮想リポジトリのアップストリームは、アップストリームタイプごとに別個のエンドポイント階層を使用するのではなく、ID で既存のリポジトリを参照し、API の表面と実装の複雑さを減らします
- ADR 外でのスキーマ進化: リクエストおよびレスポンスのペイロードは Artifact Registry コードベースの OpenAPI 仕様に存在するため、ペイロードの変更で ADR を更新する必要はありません
- 将来の拡張性: この設計パターンは、アーキテクチャの変更なしに追加のパッケージフォーマット(PyPI、NuGet、Helm など)に容易に対応します。管理 API とクライアント API は、それぞれの要件に基づいて独立して進化できます
ネガティブ
- リポジトリ名の不変性が柔軟性を制限する: タイプミスや組織的なリネームには、単にリネームするのではなく、新しいリポジトリを作成してアーティファクトを移行することが必要になります
- グローバルな名前の一意性が制限的:
my-appのような名前はフォーマットをまたいで再利用できません(例:my-appという名前の Docker と Maven の両方のリポジトリ)。これはmy-app-dockerやmy-app-mavenのような命名規則につながる可能性があります。この制約は後で破壊的な変更なしに緩和できます - フォーマット固有の API 表面: フォーマットごとにエンドポイントを専用化することは、フォーマットをまたいだ一部の重複と、管理タスクに対する統合されたフォーマット横断の操作がないことを意味します
参考文献
- ADR-001: アンカーポイントとしての組織
- ADR-022: ネームスペースデカップリング
- Cells アーキテクチャ
- Container Registry Routing Service (Cells)
- GitLab REST API 認証
- OCI Distribution Spec v1.1
- Docker Registry Bearer Token Authentication
- Maven Repository Layout
- NPM Registry API
- Repository Name Immutability Proposal
c955a93f)