次世代レート制限アーキテクチャ
| Status | Authors | Coach | DRIs | Owning Stage | Created |
|---|---|---|---|---|---|
| accepted | grzesiek
marshall007
fabiopitino
hswimelar | andrewn | sgoldstein | devops enablement | 2022-09-08 |
概要
合理的なアプリケーション制限の導入は、SaaS プラットフォームのスケーリング戦略において非常に重要なステップです。SaaS プラットフォームのユーザーが増えれば増えるほど、可用性目標の達成を助け、ユーザーにとってのノイジーネイバー問題を削減し、ユーザーがプラットフォームを継続的に正常に使用できるようにするための、合理的なレート制限とポリシー施行の導入がより重要になります。
これは GitLab.com にとって特に重要です。私たちの目標は、アプリケーション制限を施行するための合理的で透明な戦略を持つことです。これは責任ある使用の定義となり、私たちの可用性とユーザー満足度を望ましいレベルに保つのに役立ちます。
私たちはすでに長年にわたってさまざまなアプリケーション制限を導入してきましたが、一貫した戦略を持つことはありませんでした。今私たちが構築したいのは、制限とポリシーを定義・公開・施行するために、アプリケーションスタック全体でエンジニアとプロダクトマネージャーが使用する一貫したフレームワークです。
制限の定義に一貫性がなく、ユーザー、サポートエンジニア、サテライトサービスに公開できないことが、私たちの生産性に悪影響を与え、新しい制限の導入を困難にし、最終的にはアプリケーションスタックのすべての層で責任ある使用を施行することを妨げています。
このブループリントは、私たちの制限を統合し、次世代のレート制限とポリシー施行アーキテクチャのビジョンを説明するために書かれました。
目標
レート制限とポリシー定義のための次世代アーキテクチャを実装する。
課題
- アプリケーション制限を定義する方法が多数あり、多くの異なる場所に存在している。
- リクエストにどの制限が適用されているかを理解することが困難。
- 新しい制限の導入が困難で、ポリシーの定義はさらに困難。
- 定義されている制限を見つけるにはコードベースの監査が必要。
- Registry などのサテライトサービスに制限を公開する良い方法がない。
- 不透明な外部システム(Pipeline Validation Service、Bouncer、Watchtower、Cloudflare、HAProxy)を通じていくつかの異なるポリシーを施行している。
- 制限の定義と一貫したポリシーを定義する標準化された方法がない。
- ユーザーが制限の閾値に近づいているときに理解することが困難。
- ユーザーが閾値に近づいているときに自動的に通知する方法がない。
- 名前空間 / プロジェクト / ユーザー / 顧客の制限を変更する単一の方法がない。
- リアルタイムメトリクスで制限を監視する単一の方法がない。
- 階層的な制限設定(インスタンス / 名前空間 / サブグループ / プロジェクト)のフレームワークがない。
- 一部の主要な SaaS 顧客に対してレート制限を無効にすることを許可しているが、これは同じ顧客にとってリスクを高める。代わりに、より高い制限を設定できるようにすべきである。
機会
新しいフレームワークを構築し、制限、クォータ、ポリシーの定義を容易にし、堅牢な監視機能を通じて制御された方法でそれらを施行/調整できるようにしたいと考えています。
- GitLab Rails で制限を定義・施行するフレームワークを構築する。
- サテライトサービスで制限を消費し、ユーザーに公開するための API を構築する。
- このフレームワークの一部を専用の GitLab Limits Service に切り出す。
最も重要な機会は、複数のレベルで起こる統合です:
- GitLab Rails で使用されるアプリケーション制限ツールを統合する。
- アプリケーション制限の追加と管理プロセスを統合する。
- 制限の階層的なカスケードとオーバーライドの動作を統合する。
- アプリケーションスタック全体で使用されるアプリケーション制限ツールを統合する。
- 会社全体で使用されるポリシー施行ツールを統合する。
それが完了すると、別の機会が生まれます: 新しいフレームワーク/ツールを GitLab の機能として提供し、ユーザー、顧客、より広いコミュニティ向けにこれらの統合のメリットを解放することです。
制限、クォータ、ポリシー
このドキュメントは GitLab.com の次世代レート制限アーキテクチャを構築するための技術的ビジョンを説明することを目的としています。このアーキテクチャの進化を「次世代レート制限アーキテクチャ」と称していますが、これは思考の近道です。実際には、レート制限だけでなく、クォータとポリシーをより簡単に管理できるより良いフレームワークを構築したいからです。
以下に、制限、クォータ、ポリシーについての短い定義を示します。
- 制限: アプリケーション使用に対する制約で、通常はパフォーマンス、安定性、セキュリティへのリスクを軽減するために使用される。
- 例: 特定の IP アドレスに対する 1 秒あたりの API 呼び出し
- 例: 特定のユーザーに対する 1 分あたりの
git cloneイベント - 例: 最大アーティファクトアップロードサイズ 1 GB
- クォータ: アプリケーション使用に対するグローバルな制約で、請求サイクルの期間にわたって名前空間全体で集計される。
- 例: 名前空間ごとに 1 か月あたり 400 コンピュートミニット
- 例: 名前空間ごとに 1 か月あたり 10 GB の転送
- ポリシー: アプリケーションコードから切り離されたビジネスロジックの表現。切り離されたポリシー定義により、アプリケーションの新しいバージョンをリリースせずに、複数のサービスでロジックを共有したり、ランタイムでホットロードしたりできる。
- 例: JWT をデコードして検証し、JWT スコープとクレームに基づいてユーザーがリソースへのアクセス権を持っているかどうかを判断する
- 例: すべてのサービスでグループレベルの制約(IP 許可リスト、SSO、2FA など)に基づいてアクセスを拒否する
技術的には、これらはすべて制限です。レート制限は依然として「制限」であり、クォータは通常ビジネス上の制限であり、ポリシーは特定のルールを施行するためにアプリケーションで何ができるかを制限するためです。このドキュメントで「制限」と言う場合、ビジネス、可用性、セキュリティを保護するために定義された制限を意味します。
制限を定義・施行するフレームワーク
まず最初に、GitLab Rails プロジェクトのコンテキストで、より一貫した確立された方法でアプリケーション制限を定義・施行できる新しいフレームワークを構築したいと思います。そのためには、エンジニアが構造化された方法(おそらく YAML または Cue フォーマットを使用)で制限を定義する方法と、アプリケーション自体で制限を消費する方法を伝える新しい抽象化を構築する必要があります。
アプリケーションには既に多くの制限が定義されており、それらを使用して合理的な抽象化を見つけ、制限の定義、使用、施行を統一できます。
エンジニアが特定の制限が超過されているかどうかを確認するのを簡単にする、シンプルな Ruby ライブラリを構築することを想定しています(LabKit に追加できます)。
name: my_limit_name
actors: user
context: project, group, pipeline
type: rate / second
group: pipeline::execution
limits:
warn: 2B / day
soft: 100k / s
hard: 500k / s
Gitlab::Limits::RateThreshold.enforce(:my_limit_name) do |threshold|
actor = current_user
context = current_project
threshold.available do |limit|
# ...
end
threshold.approaching do |limit|
# ...
end
threshold.exceeded do |limit|
# ...
end
end
上記の例では、my_limit_name が YAML で定義されている場合、エンジニアは過去の使用/リソース消費に応じて現在の状態を確認し、適切なコードブロックを実行できます。
デフォルトで構築・サポートしたいもの:
- 制限がどのくらいの頻度でヒットされているかを示す包括的なダッシュボード。
- 制限ヒットのリスクに関する通知。
- 制限の定義が適切に施行されているかどうかを確認する自動化。
- さまざまな種類の制限 - 時間制限 / リソースごとの数など。
- プラン / 名前空間ごとに制限を簡単にオーバーライドできるパネル。
- Kibana で適用された制限を公開するログ。
- すべての制限を説明する自動生成されたドキュメントページ。
使用リソースに基づくレート制限のサポート
私たちのレート制限システムの問題の 1 つは、値が静的(例: 1 分あたり 100 リクエスト)であり、操作の複雑さやリソース使用量に関係なく設定されていることです。例えば:
- 単純なリソースを取得するために 1 分あたり 100 リクエストを送信することは、CI パイプラインを作成することとは大きく異なる影響を持つ可能性があります。
- 各パイプライン作成アクションは、作成されるパイプライン(小さな MR パイプライン vs 大きなスケジュールパイプライン)によって非常に異なるパフォーマンスを持つ可能性があります。
- 1000 のオフセット後のリソースのページネーションはデータベースで高コストになり始めます。
一部のレート制限を コンピューティングスコア / 期間 として定義できるようにすべきです。コンピューティングスコアでは、特定の期間内の(実行中とインフライトのすべてのリクエストに対して)累積ミリ秒を計算します(例: 1 分)。
このようにすると、ユーザーが高コストのリクエストを送信している場合、レート制限に早く到達する可能性が高くなります。
制限とポリシーを公開する API
アプリケーション制限を定義する一貫した方法が確立されたら、ユーザー、顧客、それらを消費したいサテライトサービスに公開するための API エンドポイントを構築できます。
ユーザーは自分のために設定された制限/閾値、それらにどのくらいの頻度でヒットしているか、それらがビジネスにどのような影響を与えるかを API に問い合わせることができます。この種の透明性は、GitLab のカスタマーサクセスチームとのニーズ伝達を助け、私たちは責任ある使用がある瞬間にどのように定義されているかを伝えることができます。
GitLab アーキテクチャの構築方法のため、GitLab Rails アプリケーションは多くの場合、中央のエンタープライズサービスバス(ESB)として機能し、いくつかのサテライトサービスがそれと通信しています。コンテナレジストリ、GitLab Runner、Gitaly、Workhorse、KAS などのサービスは API を使用して施行すべきアプリケーション制限のセットを受け取ることができます。これにより、すべてを 1 か所で定義できます。
ただし、突然の使用増加が起こった場合に Rails アプリケーションに追加の負荷をかける可能性のある負のフィードバックループを避ける必要があります。これは大きな顧客が API を横断する新しい自動化を開始したり、DoS 攻撃であったりする可能性があります。このような場合、追加のトラフィックが GitLab Rails に到達し、その後他のサテライトサービスにも到達します。サテライトサービスは増加したトラフィックのレート制限に関する新しい指示/ポリシーを取得するために Rails に再度相談する必要があるかもしれません。これは Rails アプリケーションにさらに負担をかけ、最終的にはパフォーマンスをさらに低下させる可能性があります。この問題を避けるために、増加したトラフィックの量にそれらのエンドポイントへのリクエストレートが依存する場合は、API エンドポイントを別のサービスに切り出す必要があります(以下のセクションを参照)。あるいは、増加したトラフィックがこれらの API エンドポイントへの Rails 側のリクエストレートの増加やリソース消費の増加に変換されない場合は、それらのエンドポイントを Rails に維持できます。
切り離された制限サービス
ある時点で、制限に関するメタデータ、すべてのカウンター、必要な状態を保存し、API を公開するためのステートフルバックエンドを Rails から切り出す時期であると判断する可能性があります。
このような切り離された制限サービスを切り出すかどうかについて決定することはまだ不可能です。いつ、どのように行うべきかをより適切に知らせるためのプルーフオブコンセプト作業と具体的なイテレーションを出荷する必要があるためです。切り離された制限サービスを切り出すか否かに向けて、Evolution Architecture の実践に依存します。
このブループリントを進化させるにつれて、このセクションでこのサービスがどのように見えるべきかについての調査と洞察を文書化します。
GitLab Policy Service
免責事項: GitLab Policy Service の切り出しは、このブループリントの実装を中心に組織された現在のワークストリームの範囲外である可能性があります。
すべての制限を YAML で簡単に記述できるわけではありません。より洗練されたアプローチと宣言型プログラミング言語を使用して施行する必要があるより複雑なポリシーがいくつかあります。そのような言語の 1 つの例は、OPA - Open Policy Agent でポリシーを定義するための標準化された方法である Rego 言語かもしれません。GitLab では、すでにいくつかの部門で OPA を使用しています。私たちは GitLab 内部で使用するツールを統合するだけでなく、次世代レート制限アーキテクチャを製品自体の一部にすることができるものに変換するための追加の統合の必要性を想定しています。
今日、私たちはすでにパイプラインを作成できるかどうかを決定するために使用するポリシーサービスを持っています。Pipeline Validation Service には多くのポリシーが定義されています。GitLab 製品自体によく統合された汎用の GitLab Policy Service / GitLab Policy Agent に Pipeline Validation Service を変換するという大きな機会があります。
Pipeline Validation Service を GitLab Policy Service に一般化することにはいくつかの興味深い利点があります:
- 効率を向上させるために会社全体のツールを統合する。
- GitLab Rails の制限フレームワークをポリシーサービスを使用してポリシーを解決するように統合する。
- YAML で複雑なポリシーを定義し、Ruby でそれらを評価するためのハックに苦労しない。
- クエリ実行コスト推定を使用した GraphQL クエリの制限のためのポリシーを構築する。
- 「階層的な制限」構造を必要としないポリシーの解決を容易にする。
- GitLab Policy Service を製品の一部にし、単一のアプリケーションに統合する。
私たちは GitLab Policy Service を使用して、制限の階層構造について何も知る必要がないポリシーを定義する場所と想定しています。IP アドレス許可リスト、スパムチェック、設定検証などのように、これを必要としない制限があります。
私たちは「ポリシー」をステートレスな関数型スタイルの制限として定義しました。入力引数を取り、true または false のいずれかに評価されます。グローバルカウンターや評価のためのその他の不安定なグローバル状態を必要としません。グローバルに定義されたルール/設定が必要な場合があっても、この状態はレート制限カウンターやクォータ制限を評価するために消費されたメガバイトと同じ意味では不安定ではありません。
内部的および外部的に使用されるポリシー
GitLab Policy Service は 2 つの異なる方法で使用される可能性があります:
- Rails の制限フレームワークは内部的に施行されるポリシーのソースとしてそれを使用する。
- ポリシーサービスの機能はユーザーが定義したポリシーを保存するためのバックエンドとして使用される。
これら 2 つは少し異なるユースケースです: 最初のユースケースは GitLab インスタンス(GitLab.com またはセルフマネージドインスタンス)の安定性/可用性を確保するために内部的に定義されたポリシーを使用することについてです。2 番目のユースケースは GitLab Policy Service をユーザーが上に構築できる機能にすることについてです。
両方のユースケースは有効ですが、それらを分離する方法について技術的な決定を下す必要があります。両方を単一のサービスに実装することに決めた場合でも、2 つの間に強い境界を引く必要があります。
同じ原則は、このドキュメントのセクションの 1 つで説明されている切り離された制限サービスにも適用されるかもしれません。
2 つの制限/ポリシーサービス
GitLab Policy Service と切り離された制限サービスは実際には同じものである可能性があります。ただし、それはまだ予測できない実装の詳細に依存しており、これらのサービスをマージするかどうかの決定は、後続のイテレーションのフィードバックによって知らされる必要があります。
階層的な制限
GitLab アプリケーションはユーザー、プロジェクト、グループ、名前空間を階層的な方法で集約しています。この階層構造は、権限の管理を容易にし、ワークフローを合理化し、ユーザーと顧客が関連するプロジェクト、リポジトリ、その他のアーティファクトをまとめて保存できるように設計されています。
新しいレート制限フレームワークをこの階層構造の上に構築し、エンジニア、顧客、SRE、その他のステークホルダーが名前空間、グループ、プロジェクトの階層内で制限がどのように適用、施行、オーバーライドされているかを理解できるように設計することが重要です。
既存の権限構造内での制限の管理方法を理解するために必要な認知負荷を削減したいと考えています。アプリケーションが特定のリクエストと特定のアクターにどの制限と閾値を適用するかを決定する方法についての、シンプルで理解しやすい公式を構築する必要があるかもしれません:
GitLab はすべての操作のデフォルト制限、設定されたすべてのオーバーライドを読み取り、最も高い優先度が設定された制限を選択します。制限の優先度はすべてのオーバーライドに対して明示的に設定する必要があります。デフォルト制限の優先度は 100 です。
制限管理を全体的に簡素化できる 1 つの方法は以下の通りです:
- デフォルト優先度 100 を持つ YAML ファイルにデフォルトの制限/閾値を定義する。
- API を通じて制限をオーバーライドできるようにし、データベースにオーバーライドを保存する。
- すべての制限/閾値オーバーライドには整数の優先度値を提供する必要がある。
- アクターを取得してそれに適用可能な制限を公開する API を構築する。
- 非標準の制限/オーバーライドを持つアクターを示すダッシュボードを構築する。
- 非標準の制限が使用されているときに Kibana で表示する可観測性を構築する。
上記のポイントは優先度スコア(制限に対する Z インデックス)を使用するアイデアを表していますが、より良いソリューションがある可能性があります。例えば、オーバーライドの方向を定義するだけで、より低い制限が階層の上位で定義された制限を常にオーバーライドするなど。適切なソリューションを選択するには、慎重な調査が必要です。
原則
- レート制限フレームワークを密結合な方法で構築しないようにする。
- アプリケーション制限 API を別のサービスに簡単に切り出せるように構築する。
- アプリケーション制限の定義を Rails アプリケーションから独立した方法で構築する。
- プログラミング言語全体で一貫した動作と結果を生み出すツールを構築する。
- セルフマネージド管理者が制限をカスタマイズできるように拡張できる方法で新しいフレームワークを構築する。
- SaaS とセルフマネージドのコードベース全体で一貫した機能と動作を維持する。
- 階層的な制限によって追加される認知負荷に注意を払い、それを削減することを目指す。
フェーズとイテレーション
最も重要なアプリケーション制限の現在の例をコンパイルする(所有チーム)
- 所有チーム(ステージグループと協力して)は今日 Rails で使用されている最も重要なアプリケーション制限のリストをコンパイルする。
Rails でレート制限フレームワークを実装する(所有チーム)
- フェーズ 1 で収集したデータに基づいてレート制限の抽象化を三角測量する。
- 制限の YAML モデルを開発する。
- Rails SDK を構築する。
- 新しいレート制限 SDK の使用例を示すサンプルを作成する。
Rails SDK のチームへの展開(ステージグループ)
- 個々のステージグループが新しい制限とポリシーのためにフェーズ 2 で構築された SDK を使用し始める。
- ステージグループが歴史的なアドホック制限実装を SDK に置き換え始める。
- (所有チーム)交換努力の進捗を監視・観察する手段を提供する。理想的にはこれは
feature_categoryレベルに分解されてグループレベルの buy-in を促進する。
サテライトサービスがレート制限フレームワークを使用できるようにする(所有チーム)
- フェーズ 4 の目標が以下のどちらで最もよく達成されるかを判断する:
- Rails のレート制限サービスを切り離されたサービスに切り出す。
- レート制限のために同じバックエンド(例: Redis)を使用する別の Go ライブラリを実装する。
- フェーズ 4 の目標が以下のどちらで最もよく達成されるかを判断する:
サテライトサービスの SDK(所有チーム)
- Go SDK を構築する。
- 新しいレート制限 SDK の使用例を示すサンプルを作成する。
サテライトサービスのチームへの展開(ステージグループ)
- 個々のステージグループが新しい制限とポリシーのためにフェーズ 5 で構築された SDK を使用し始める。
- ステージグループが歴史的なアドホック制限実装を SDK に置き換え始める。
ステータス
承認済みです。このブループリントで説明されているアプリケーションレベルのフレームワークは、labkit を通じて実装されています。詳細な技術設計については、Unified Rate Limiting Architecture 設計ドキュメントを参照してください。
タイムライン
- 2022-04-27: レート制限アーキテクチャワーキンググループが開始。
- 2022-06-07: ワーキンググループのメンバーが次世代レート制限アーキテクチャの技術的な提案を提出し始めた。
- 2022-06-15: ワーキンググループのメンバーが提出した提案のスコアリングを開始した。
- 2022-07-06: 4 番目の統合された提案が提出された。
- 2022-07-12: アーキテクチャ進化ワークフローに従って設計ドキュメントの作業を開始した。
- 2022-09-08: ブループリントの初期バージョンがマージされた。
c955a93f)