Artifact Registry ADR 004: データおよびアプリケーションの上限
背景
Artifact Registry は、システム安定性を確保し、不正利用を防ぎ、予測可能なリソース消費を可能にするため、明示的な上限を定義する必要があります。これらの上限は、データの保持期間を管理するデータ保持ポリシー(ADR-010を参照)とは異なります。ここでの上限は、任意の時点でどれだけのデータと操作が許可されるかを管理します。
定義された上限がなければ、少数のテナントが不釣り合いなリソースを消費し、すべての顧客のサービス品質を劣化させる可能性があります。CI/CD ワークロードは非常にスパイクのあるトラフィックを生み出します(ADR-003を参照)。バースト制御は特に重要です。
本 ADR では、3 つのタイプの制約を区別します。
- 上限 (Limits) はアプリケーションの安定性とセキュリティを保護します。リクエスト単位または時間ウィンドウ単位で強制され、ビリングには紐づきません。例: アーティファクトアップロードの最大サイズ、API レート上限、エンティティ数のキャップ。
- クォータ (Quotas) は請求サイクルやプランに紐づく総消費の制約です。namespace が全体としてどれだけリソースを消費できるかを管理します。例: namespace あたりのストレージクォータ。
- ポリシー (Policies) は、Artifact Registry へのアクセスを制御する、アプリケーションコードから切り離されたビジネスロジックを表します。ポリシーは上限とクォータの前に評価されます。
- 例: Organization の IP 許可リストにない IP からのアクセスを拒否
- 例: ブロックまたは禁止されたユーザーのアクセスを拒否
本 ADR では、上限(アーティファクトサイズ、API レート、エンティティ数)と クォータ(ストレージ)を扱います。
Artifact Registry は GitLab.com 規模をターゲットとします: 定常状態で毎秒数千の API リクエスト、合計数十ペタバイトのデータ、大規模 Organization あたり数百万のアーティファクト(ADR-003を参照)。上限は正当なエンタープライズワークロードを支えるのに十分高く、かつ単一の namespace がサービスを不安定にしないように低くする必要があります。
該当する場合、上限は Package Registry と Container Registry で既に確立されたものと整合または拡張し、GitLab レジストリ全体で一貫した体験を提供します。
決定
Artifact Registry は、Artifact Registry のテナンシーおよびパーティション境界である namespace レベルで、ストレージ(クォータ)、アーティファクトサイズ(上限)、API レート(上限)、エンティティ数(上限)の 4 つの次元にわたってクォータと上限を強制します。レート上限のプランティアごとのデフォルトは、請求アンカー(Organization)から継承されますが、すべての上限とクォータは namespace ごとに強制および計測されます。複数の namespace を所有する Organization は、それらの namespace の利用量合計に対して請求されます。namespace をまたいだ重複排除やプーリングはありません。
アーティファクトサイズ上限、エンティティ数上限、ストレージクォータは、すべてのインストールタイプで同じデフォルトを使用します。レート上限は GitLab.com ではプラン依存です。各プランティアが独自のデフォルトレート上限を定義し、個別の namespace は正当な高負荷利用に対してカスタムオーバーライドを受け取れます。Self-Managed では、管理者がインスタンスレベルですべての上限を調整できます。
ストレージクォータ
ストレージクォータは namespace レベルで強制されます。ストレージは namespace 内のすべての一意な Blob の重複排除済み合計として計測されます。重複排除は ADR-002と整合して namespace ごとにスコープされます。namespace をまたいだ重複排除はありません。複数の namespace を所有する Organization は、各 namespace のストレージ合計を消費し、その合計に対して請求されます。Organization は引き続き請求アンカーです(ADR-001)。
ストレージクォータは、アーティファクトサイズ上限やエンティティ数上限と同様に、不正利用に対するプラットフォームの安全上限です。課金を制御するものではありません。すべてのプランで同じ値を使用し、プランティアや購入内容によって定義されることはありません。ストレージは GitLab Credits を通じて GB 単位で課金されます(ADR-012)。課金上の制御としては、代わりに namespace を読み取り専用として利用停止にします(ADR-010)。
| クォータ | デフォルト | 注 |
|---|---|---|
| namespace あたり最大ストレージ | すべてのプランで同じプラットフォームのデフォルト値 | 課金制御ではなく、不正利用に対する安全上限です。デフォルト値はロールアウト戦略の Observe フェーズ中に決定します |
| リポジトリあたり最大ストレージ | デフォルトでは厳格な制限なし | namespace レベルで構成可能なオプショナルな警告閾値。超過時に namespace オーナーに通知されますが、アップロードはブロックされません |
警告閾値(例: クォータの 80%)は namespace ごとに構成できます。利用がこの閾値を超えると、namespace オーナーに通知されますが、クォータに完全に達するまでアップロードはブロックされません。
新しいアップロードセッションを受け入れる前に、システムは namespace がストレージクォータに達しているか超過しているかを確認します。namespace にまだ余裕がある場合、アップロードは受け入れられ、利用カウンターは正常完了後に更新されます。namespace が既にクォータに達しているか超過している場合、アップロードセッションは拒否されます。
利用カウンターは正常アップロードごとに更新されるため、並行アップロードセッションはそれぞれ独立してクォータチェックを通過し、合計でクォータをわずかに超過する可能性があります。これは許容されます。厳密な強制はすべてのアップロードを直列化することを必要とし、ADR-003の並行性目標と矛盾します。超過は並行アップロード数によって境界付けられ、次のクォータチェックで修正されます。
アーティファクトサイズ上限
アーティファクトサイズ上限はアーティファクトタイプによって異なります。以下のデフォルト値はすべてのインストールタイプに適用されます。Self-Managed では、管理者がインスタンスレベルでこれらのデフォルトをオーバーライドできます。
| アーティファクトタイプ | 最大アーティファクトサイズ | 注 |
|---|---|---|
| Maven | 5 GB | 既存の Package Registry 上限と一致 |
| npm | 5 GB | 既存の Package Registry 上限と一致 |
| コンテナイメージ | 50 GB | Blob あたりの最大サイズ。 |
加えて、コンテナイメージには次の上限が適用されます。
| 上限 | デフォルト | 注 |
|---|---|---|
| 最大マニフェストペイロード | 250 KB | Docker および OCI マニフェストに適用。 |
| 最大マニフェスト参照数 | 200 | マニフェストが参照できる Blob またはマニフェストの最大数 |
アップロードセッションには非アクティブタイムアウトが課されます。タイムアウトウィンドウ内にデータが受信されない場合、セッションは終了し、部分的にアップロードされたデータは破棄されます。これにより、Slowloris のような低速 DoS 攻撃を緩和し、アイドル状態のセッションがリソースを無期限に保持することを防ぎます。
レート上限
レート上限は API を不正利用から保護し、単一のユーザーまたは namespace が共有インフラを使い果たすのを防ぎます。レート上限違反は HTTP 429(Too Many Requests)と Retry-After ヘッダーを返します。
Self-Managed では、管理者がインスタンスレベルでレート上限のデフォルトをオーバーライドできます。
GitLab.com では、レート上限は最初はロギング/警告モードでデプロイされ、トラフィック分布が分析された後にのみ強制されます(ロールアウト戦略を参照)。この段階的ロールアウトは、合理的なデフォルトを確立するための一回限りのプロセスです。Self-Managed では、管理者は直接レート上限を構成し強制できます。
Artifact Registry サービスは制限を段階的に提供します。まず namespace ごとの制限を Maven の仮想リポジトリの読み取り経路とともに提供し、IP ベースとユーザーごとの制限は、それぞれを利用する機能とともに導入します。
IP ベースの上限
IP ベースのレート上限は、不正利用に対する最外層の防御を提供します。Container Registry のレート制限ミドルウェアのパターンを再利用します。これは GCRA と名付けられたトークンバケットで、gcra.lua が tokens += elapsed × refill_rate によってトークンを補充し、拒否時にはバケットをゼロにします。IP をキーとし、呼び出し元の時計を使用します。Artifact Registry サービスが取り入れるのはコードではなく、このパターンです。単一の Lua スクリプトを介して Redis 側にキーごとのセルを 1 つ持ち、go-redis/redis_rate を通じて GCRA を実装します。この実装は Redis サーバーの時計を読み取り、拒否時にはトークンを消費しません。初期デフォルト値は、本番トラフィック分析に基づいて Observe フェーズ中に決定されます。
ユーザーごとの上限
ユーザーごとのレート上限はプラン依存です。Organization 内のユーザーは、その Organization のプランティアからデフォルトを継承します。プランごとのデフォルト値は、ユーザーごとのトラフィック分布に基づいて ロールアウト戦略の Observe フェーズ中に決定されます。既存の Package Registry レート上限がベースライン参照として機能します。
Namespace ごとの上限
Namespace ごとのレート上限はプラン依存です。各プランティア(Premium、Ultimate)が独自のデフォルトレート上限を定義し、namespace の請求アンカーから継承されます。個別の namespace は構成のファーストクラスのプロパティとして独自のレート上限を持つことができ、例外単位で管理されるのではなく、同じプランのすべての namespace のデフォルトを引き上げずに正当な高負荷利用に対応します。プランごとのデフォルト値は、namespace ごとのトラフィック分布に基づいて ロールアウト戦略の Observe フェーズ中に決定されます。
このティア分けされたアプローチは、単一のグローバルデフォルトの問題を回避します。重い正当ユーザー向けに 1 つの値を高く設定すると、残り全員にレート上限を実質的に無効にします。プランベースのデフォルトと namespace ごとのオーバーライドにより、プラットフォームは下位ティアでは不正利用から保護し、上位ティアでは大量利用顧客をサポートできます。
Namespace レベルの上限は、プラットフォームの主要な保護メカニズムです。ユーザーごとの上限は、単一のユーザーが namespace の全クォータを消費するのを防ぐための二次層を提供します。
並行アップロードセッション
並行アップロードセッション上限は、ユーザーあたりおよび namespace あたりの進行中アップロード数をキャップします。これにより、単一のアクターがアップロードインフラを独占するのを防ぎます。レート上限と同様、並行アップロードセッション上限はプラン依存です。プランごとのデフォルト値は ロールアウト戦略の Observe フェーズ中に決定されます。
エンティティ数上限
エンティティ数上限は、API の振る舞い、プロキシチェーンの複雑さ、ポリシー評価コストに影響する特定の運用パラメータをキャップします。リポジトリ数は、自動化バグからのランナウェイ作成を防ぐため、namespace ごと、アーティファクトタイプごとにキャップされます。
| エンティティ | 上限 | 注 |
|---|---|---|
| namespace あたりアーティファクトタイプごとのリポジトリ数 | 1,000 | インスタンスレベルで構成可能 |
| アーティファクト/パッケージバージョンあたりのタグ数 | 1,000 | 不正利用と誤用を防止 |
| パッケージあたりのバージョン数 | 25,000 | 不正利用と誤用を防止 |
| 仮想リポジトリあたりのアップストリームソース数 | 20 | プロキシチェーンの複雑さを制限 |
| リポジトリあたりのライフサイクルポリシールール数 | 50 | ポリシー評価の複雑さを制限 |
アップストリームソースとライフサイクルポリシールールの上限は、書き込み時に同期的に強制されます。これらの上限を超えようとする試みは HTTP 422(Unprocessable Entity)を返します。
結果
ポジティブ
- システム安定性: 明示的な上限により、ランナウェイワークロードが共有インフラを不安定にするのを防ぐ
- 予測可能なパフォーマンス: 境界のあるポリシー評価とプロキシチェーンの複雑さにより、メタデータクエリがスケールでも高性能を保つ
- 明確な顧客期待値: 文書化された上限により、顧客は利用とキャパシティを計画できる
- 公平なマルチテナンシー: namespace スコープのレート上限、ストレージクォータ、エンティティキャップにより、テナント間でリソースを分配する
- 強制可能な SLA: 定義された上限は、API 応答時間に対する意味のある SLO 設定の前提条件
ネガティブ
- 顧客摩擦: 正当な大量利用ワークロードがデフォルト上限に達し、手動でのクォータ増加が必要になる可能性
- 運用オーバーヘッド: クォータ管理は利用のモニタリングと上限増加リクエストの処理という継続的な作業を追加
- 結果整合性のあるストレージアカウンティング: 利用カウンターは非同期に更新される。大規模アップロード後の短時間、古い使用量が表示される可能性
- 上限チューニングが必要: デフォルト値は初期見積もりであり、観測される本番トラフィックパターンに基づいて調整が必要
検討した代替案
代替案 1: 明示的な上限なし(公正な利用を信頼)
プロアクティブな上限ではなく、モニタリングとリアクティブな介入に依存。
ポジティブ
- 上限に達することからの顧客摩擦なし
- 上限チューニングのオーバーヘッドなし
ネガティブ
- 単一の不適切なテナントがすべての顧客のサービスを劣化させる可能性
- リアクティブな介入は自動強制より遅い
- インシデント中の顧客コミュニケーションの明確な根拠がない
却下理由: GitLab.com 規模では、信頼性のあるマルチテナンシーのためにプロアクティブな上限が必要です。Container Registry の Organization ごとの上限の欠如は 運用上の複雑さに寄与しました。
代替案 2: インスタンスレベルの上限のみ
namespace ごとではなく、インスタンスレベルでグローバルに上限を適用。
ポジティブ
- 実装と推論がシンプル
ネガティブ
- 単一の大規模 namespace がインスタンス全体のキャパシティを消費するのを防げない
- ビリングとキャパシティプランニングのために、リソース使用量を特定の namespace に按分できない
却下理由: namespace ごとの上限は GitLab.com での公平なマルチテナンシーに必要です。Organization は引き続きアンカーおよび請求ポイントであり(ADR-001)、その namespace の利用量を集約します。
代替案 3: CDN/ロードバランサーのみで上限
すべてのレート上限をアプリケーションではなく CDN またはロードバランサー層(Cloudflare)で強制。
ポジティブ
- レート上限の計算をアプリケーションからオフロード
- スロットルされたリクエストの拒否レイテンシが低い
ネガティブ
- CDN 層の上限は GitLab.com にのみ適用される。Self-Managed インストールには同等の保護がない
- 複雑な CDN 構成なしに namespace ごとの上限を強制できない
却下理由: アプリケーション層のレート制限は、すべての GitLab インストールタイプにわたって一貫した強制を提供します。CDN 層の上限は、IP ベースのスロットリングのためにアプリケーション上限を補完できますが、置き換えることはできません。
実装ノート
Artifact Registry はスタンドアローンサービスです。レート上限については解決済み: レート上限の設定はサービス自身の設定内で保存・管理し、次のロールアウトで適用します。namespace ごとの値は、その設定の正式なプロパティとして扱います。未解決の質問: プランティアごとのデフォルト値をサービスにどのように届けるかは未決定です。ロールアウト時に適用する静的な設定では、namespace の請求アンカーからそれらを継承できません。また、ストレージクォータ、アーティファクトサイズ上限、エンティティ数上限の設定をどこで保存・管理するか(たとえば、Rails モノリス内に保存し、レジストリが API 経由で取得するか、レジストリ内で直接管理するか)も未決定です。
- ストレージクォータ: アップロードと GC サイクル後に更新される Redis カウンターを使用した、アップロードセッション開始時の非同期チェック
- アーティファクトサイズ: リクエストボディから読み取られるバイトをカウントすることでアップロード中に強制(クライアントが省略または誤報告できる
Content-Lengthヘッダーを信頼しない)。バイト数が上限を超えると読み取りは中断され、リクエストは拒否される。これは Workhorse がアップロードサイズ強制に使用するのと同じアプローチ - レート上限: GCRA アルゴリズムを使用した Redis カウンター。違反時に
Retry-After付き HTTP 429。レート上限実装は、Container Registryで確立されたミドルウェアと Redis カウンターパターンを再利用し、構成スキーマを Artifact Registry 固有の操作に適合させる - エンティティ数(アップストリームソース、ライフサイクルポリシールール): 挿入前のデータベースクエリ。違反時に HTTP 422
GitLab.com では、Runway を介したインフラレベルのレート上限が、IP ベースのスロットリングに追加の防御層を提供します。これらはアプリケーションレベルの上限を補完しますが、置き換えるものではありません。アプリケーションレベルの上限は、すべてのインストールタイプにわたる namespace ごとおよびユーザーごとの強制に必要です。
すべての上限違反には、機械可読のエラーコード(例: artifact_registry/quota_exceeded)と、超過した上限を識別する人間可読のメッセージが含まれます。
ロールアウト戦略: レート上限と namespace ごとのクォータは 3 フェーズで導入されます。
- Observe — その時点で提供されている上限を設定しますが、メトリクスの出力と警告のログ記録のみを行います(ブロックしません)。最初に namespace ごとの制限を設定し、IP ベースとユーザーごとの制限は導入され次第、このフェーズに加わります。IP ベースの上限は、Container Registry のミドルウェアのパターンを再利用します。このフェーズは、すべての次元のトラフィック分布を収集するために、少なくとも 1 マイルストーン継続します。
- Warn — namespace ごとまたはユーザーごとの上限を超えるリクエストには、警告ヘッダー(例:
X-RateLimit-Remaining)を返しますが、引き続き処理します。namespace が上限に近づくと、処理されたすべてのレスポンスに付くこの警告ヘッダーを通じて、上限の影響が生じる前にクライアントへ通知します。 - Enforce — namespace ごとおよびユーザーごとの上限は HTTP 429 応答で能動的に強制されます。デフォルト値はフェーズ 1 と 2 で収集されたデータに基づいて調整されます。
参考文献
- レート制限アーキテクチャのブループリント - 上限、クォータ、ポリシーの区別を定義
- ADR-001: アンカーポイントとしての Organizations - 主要リソース境界としての Organization
- ADR-002: ストレージ重複排除の範囲 - 重複排除済みストレージの計測方法
- ADR-003: システム要件 - スケール目標と強制に使用されるインフラコンポーネント
- Package Registry のレート上限 - ベースラインとして使用される既存のレート上限デフォルト
- Container Registry のレート制限仕様 - GCRA ベースのレート制限アプローチ
- Container Registry のレート制限実装 - トークンバケット方式のレート制限ミドルウェアと設定。Artifact Registry のパターンとして再利用可能
- GitLab Plan Limits API - 既存のタイプごとアーティファクトサイズ上限
- 上限の管理 - レート上限の導入と変更のプロセス
5d840611)