共有AI環境を管理するプラットフォームチームのための実践ガイド

本ブログはグローバルブログ「A practical guide for platform teams managing shared AI deployments」の抄訳版です。

Rate Limiting(レート制限)vs. Quota Reservations(クォータ予約):それぞれの使いどころ

あなたはある一つの gpt-oss-20b デプロイメントを運用しています。そこへ6つのチームが利用を希望してきました。マーケティングチームは午前3時にバッチ処理で要約ジョブを回しています。不正検知チームは24時間365日、1秒未満(サブセコンド)のレスポンスを必要としています。インターンのJupyter Notebookは、不具合のある無限ループによって誤ってエンドポイントを激しく叩き続けています。そして、あなたの元に届くGPUの請求額は、すでに目玉が飛び出るほどの金額(eye-watering)になっています。

聞き覚えのある状況ではないでしょうか? DataRobotは、この問題を解決するために2つのツールを提供しています。それが「レート制限(Rate Limiting)」と「クォータ予約(Quota Reservations)」です。本記事では、ステージング環境での実際の負荷テストの例を交えながら、それぞれをどのような場面で使い分けるべきかを解説します。

レート制限とクォータ予約:分かりやすい解説

レート制限(Rate Limits) – DataRobot v11.4 から利用可能

レート制限は、コンシューマー(利用者)ごとに、複数の次元にわたって上限を設定する機能です。具体的には、「1分あたりのリクエスト数(RPM)」、「1時間あたりのトークン数」、「同時実行リクエスト数」、そして「入力シークエンス長(入力トークン長)」などが含まれます。デフォルトのポリシーがすべてのコンシューマーに適用されますが、特定のエンティティ(チームやユーザーなど)に対して個別にポリシーを上書き(オーバーライド)する例外設定も可能です。

キャパシティ管理

導入メリット(回避できるトラブル):特定のコンシューマー(利用者)による過剰消費。リクエスト数の急増、大規模な入力データ、あるいは過度な同時実行リクエストなど、あらゆるパターンの過剰消費からシステムを保護します。

Quota Reservations(クォータ予約) – DataRobot v11.9 から利用可能

クォータ予約は、デプロイメントが処理できる「総スループット(1分あたりの最大値)」と、制限の発動トリガーとなる「利用率のしきい値」を定義する機能です。その全体のバジェット(予算・枠)の中で、特定のエンティティに対して一定の割合を「予約枠」として割り当てることができます。これにより、他のコンシューマーに奪われることのない「最低限のキャパシティ」を確実に保証(ギャランティ)します。

導入メリット(回避できるトラブル): 優先度の高いワークロードの枯渇(Priority starvation)。予約枠がない場合、「うるさい隣人(Noisy Neighbor)」がキャパシティのバジェットをすべて使い果たしてしまい、最も重要な重要ワークロードに何も残らない、という事態が起こり得ます。

レート制限とクォータ予約の連携(および単体での動作)

これらを単体で使用した場合、それぞれのツールは特定の課題を解決します。

  • レート制限のみを使用した場合: 総スループットに「天井(上限)」を設けます。システムが飽和状態になると、すべてのコンシューマーは対等に競争することになり、早い者勝ち(ファーストカム・ファーストサーブ)で処理されます。
  • クォータ予約のみを使用した場合: 他のコンシューマーがどのような動きをしていようとも、特定のコンシューマーに対して「最低限のスループット」を保証します。

そしてこれらを組み合わせることで、両方のコントロールサーフェス(管理面)が手に入ります。すなわち、モデル全体を保護する「天井(上限)」と、最も重要なコンシューマーのために確保された「床(最低保証)」の双方を同時に維持できるようになります。

マルチテナント・デプロイメントの負荷テスト

これらの機能が、高負荷な状況下でどのように動作するかを評価するため、ステージング環境にある gpt-oss-20b デプロイメントに対して負荷テストを実施しました。この検証では、1つのモデルを4つのコンシューマー(利用者)が共有し、それぞれが異なる優先度を持っているという、実際のマルチテナント(複数チームでの共有)シナリオを再現しています。

設定例
設定項目 設定値
モデルgpt-oss-20b (NVIDIA NIM)
容量1000 RPM
使用率のしきい値(スループットの設定)80% (enforcement starts at 800 RPM)
利用者種別予約容量実効保証枠
本番エージェントAデプロイ30%300 RPM
本番エージェントBデプロイ20%200 RPM
本番エージェントCデプロイ30%300 RPM
開発ユーザー(予約枠なし)ユーザーなし(予約枠を持たない全体の20%のプールを共有)

これにより、開発ユーザーおよび割り当て超過分(オーバーフロー)のために、予約されていない20%のプール(200 RPM)が残される形となりました。

負荷プロファイルの例

システムが様々な飽和状態に達した際の挙動を観察するため、負荷を段階的に高めていく6つのシナリオを17分間にわたって実行しました。

シナリオ発生する事象(内容)合計負荷(合算されたロード)
正常なトラフィック4つのコンシューマーすべてが、制限(スロットリング)範囲内の穏やかなレートでアクセスしている状態~600 RPM(利用率のしきい値未満)
軽度の過負荷4つのコンシューマーすべてが、キャパシティ(最大処理能力)をわずかに超えるまで負荷を上昇させた状態~1,200 RPM(キャパシティの1.2倍)
重度の過負荷4つのコンシューマーすべてが、可能な限り最速のスピードでリクエストを連打している状態~7,200 RPM(キャパシティの7倍)
極度の過負荷コンシューマーあたりの同時実行ワーカー数が最大に達した状態~12,000 RPM(キャパシティの12倍)
遅れての参加まず3つのエージェントがトラフィックを急増(フラッディング)させ、その60秒後に開発ユーザーが参加した状態~9,000 RPM
予約枠のみ(の競合)つのエージェントのみが競合し、開発ユーザーはアクティビティなし(サイレント)の状態~7,200 RPM

レート制限(Rate Limiting)単体を使用すべきケース

以下の条件に当てはまる場合、レート制限単体での運用が最適な選択肢となります。

  • すべてのコンシューマー(利用者)の重要度が等しい場合: どのチームのトラフィックも他方より重要というわけではない場合、クォータ予約を設定する必要はありません。システムが飽和状態になった際に、全員が対等に競合する(早い者勝ちになる)というルールで十分に公平だからです。
  • GPUを保護することだけが目的の場合: トラフィックの急増によってモデルのレイテンシーが低下したり、OOM(メモリ不足)エラーが発生したりするのを防ぐことが最優先の関心事であるケースです。必要なのは緻密なトラフィック管理ポリシーではなく、「安全弁(セーフティバルブ)」です。
  • コンシューマーが1つしか存在しない場合: そのデプロイメントにアクセスするアプリケーションが1つだけであるなら、予約枠を設定する意味はありません。競合する相手(割り込みを制限すべき他者)がそもそも存在しないからです。
検証結果が示したこと(正常トラフィック時の挙動)

「正常なトラフィック」のシナリオ(合計約600 RPMで、利用率のしきい値である800 RPMを大きく下回る状態)では、レート制限機能は表面化せず、4つのコンシューマーすべてがリクエスト拒否ゼロで「成功率100%」を達成しました。

シナリオ合計RPM成功率429S
正常なトラフィック~600100%0

予約枠(クォータ)のサイズは、ピーク時の競合において各コンシューマー(利用者)が絶対的に必要とする最低限のスループットに基づいて決定してください。これは意図的な設計であり、これにより平常時のトラフィックが不当に制限(ペナルティ)を受けることはなくなります。

また、この仕組みは極端な悪用(過剰アクセス)が発生した状況下でもモデルを保護します。「極度の過負荷」シナリオ(キャパシティ1,000 RPMに対して20,000 RPM以上という、実質20倍の過負荷)の最中、レート制限機能はリクエストの95%を拒否(ドロップ)しました。しかし、モデル自体は完全に健全な状態を維持し続けました。

NIM メトリクス20倍の過負荷状態における挙動
GPU使用率91–95%(安定)
エンドツーエンド(E2E)レイテンシー1.25秒 ➔ 2.09秒(一時的にスパイクした後に安定)
最初のトークン生成時間 (TTFT)35ミリ秒(変動なし)
トークン間レイテンシー (ITL)18ミリ秒(変動なし)
KVキャッシュ3%未満(負荷なし)

レート制限機能は、混沌としたクライアントからの要求(リクエスト)と、安定したモデルの推論処理との間で、まさにファイアウォールとして機能しました。もしこの機能がなければ、1分間に20,000件ものリクエストがNIMの内部にキュー(待ち行列)として溜まり続け、レイテンシーは天文学的に膨れ上がり、結果としてモデルはすべての利用者にとって事実上使い物にならない状態になっていたでしょう。

結論(要点): あなたの目的が単に「トラフィックの急増によってモデルを破綻させないこと」であるならば、レート制限単体の導入で十分です。その場合、キャパシティ(最大処理能力)の数値を設定するだけで、それ以外の細かな構成(ゼロ構成)は不要です。

「クォータ予約」を追加すべきケースと、その具体的なサイジングのコツについてのセクションですね。実務的なベストプラクティスが語られている重要なパートです。

原文の持つ開発・運用上の具体的なニュアンスをしっかりと活かし、要約せずに全訳をお届けします。

クォータ予約(Quota Reservations)を追加すべきケース

以下の条件に該当する場合、クォータ予約の導入が不可欠になります。

  • コンシューマー(利用者)間で重要度に差がある場合: 例えば、不正検知システムがバッチ分析ジョブによってリソースを枯渇させられるような事態は許されません。開発者のテストツールによって、本番環境のエージェントに必要な保証スループットが奪われないようにする必要があります。
  • マルチテナント・デプロイメントを運用している場合: 複数のチーム、アプリケーション、または下流のデプロイメントで同じモデルを共有しているケースです。予約枠がない場合、最も大きなトラフィックを流したコンシューマー(声の大きい隣人)が勝者となってしまいます。
  • 予測可能なSLA(サービス品質保証)を提供したい場合: あるチームに対して「あなたのアプリケーションには最低でも 300 RPM を確保します」と約束している場合、クォータ予約を使うことで、その約束をインフラレベルで強制執行できます。
  • 対話型(リアルタイム)ワークロードとバッチワークロードが混在している場合: バッチジョブは突発的(バースト的)に発生し、利用可能なキャパシティをすべて使い果たしてしまいます。予約枠を設定しておくことで、バッチ処理のスパイク中であっても、対話型ワークロードが自分たちの取り分を確実に受け取れるようになります。
予約枠(クォータ)のサイジング方法

予約枠のサイズは、ピーク時の競合において各コンシューマーが絶対的に必要とする最低限のスループットに基づいて決定してください。

運用の鉄則(経験則):
  • 100%すべてを予約枠に割り当てないこと: アドホック(単発)のトラフィック、新規のコンシューマー、およびオーバーフロー(割り当て超過)に備えて、予約されていないプール(10〜20%)を残しておきます。もしすべてを予約枠で埋めてしまうと、設定を変更するまで、新しく追加されたアプリケーションのスループットがゼロになってしまいます。
  • ピーク時の需要ではなく、最低限必要な需要に合わせてサイジングすること: 予約枠は「床(最低保証)」を保証するものであり、「天井(上限)」を設けるものではありません。30%の予約枠を持つエンティティであっても、キャパシティに空きがある状態なら、30%を超えてリソースを使用できます。
  • 予約枠の大きさは、チームの規模ではなく「ビジネスにおける重要度」に合わせること: 不正検知システムのリクエスト数はバッチ分析パイプラインよりも少ないかもしれませんが、アクセスを確実に保証する必要性は遥かに高くなります。

今回の検証例では、3つの本番エージェントにそれぞれ 30% / 20% / 30% の予約枠を割り当て、開発ユーザー向けに 20% の未予約プールを残しました。これにより、開発ユーザーも引き続きデプロイメントを利用できますが、リソースが競合した際にはアクセスの保証を受けられない、という設計にしています。

実際の負荷がかかった状態で、予約枠は機能するのか?

軽度の過負荷時(キャパシティの1.2倍):システムは緩やかに性能低下(グレースフル・デグラデーション)する

軽度の過負荷シナリオ(キャパシティ 1,000 RPM に対して約 1,200 RPM の負荷)の間、4つのコンシューマーすべてが100%の成功率を達成しました。トークンバケットのバーストキャパシティ(一時的な許容量)が、このわずかな超過分を吸収したためです。これは、まだ予約枠の機能が必要とされない「緩やかな性能低下(グレースフル・デグラデーション)」の領域ですが、システムが突発的なバースト負荷を処理できることを証明しています。

重度〜極度の過負荷時(キャパシティの7〜12倍):予約枠が最低保証の床を維持する

4つのコンシューマーすべてが可能な限り最速でリクエストを連打した際(キャパシティ 1,000 RPM に対して 7,000〜12,000 RPM の負荷)、システムは圧倒されました。テスト全体を通して、各コンシューマーが経験した結果は以下の通りです。

コンシューマー予約枠成功率成功したリクエスト数
本番エージェント A30%29.0%4,172
本番エージェント B20%30.2%4,332
本番エージェント C30%28.9%4,176
開発ユーザー(予約枠なし)28.9%2,828

なぜ成功率が似通って見えるのか: 12倍の過負荷状態では、300 RPMの予約枠があったとしても、それは各コンシューマーが送信しようとしているリクエスト量(コンシューマーあたり約 3,000 RPM vs 300 RPM の最低保証)のわずか2.5%程度にすぎないからです。予約枠は、各コンシューマーが保証された 200〜300 RPM を確実に受け取れるようにすることで機能します。しかし、極度の過負荷の中では全トラフィックの97%が拒否されるため、相対的なパーセンテージの差は圧縮されて見えなくなります。

より実態を明らかにしているメトリクスは「絶対スループット(成功数)」です。 予約枠を持つコンシューマーは 4,172〜4,332 件の処理を成功させました。一方で、予約枠を持たない開発ユーザーの成功数は 2,828 件にとどまり、約34%少なくなっています。開発ユーザーのアクティブな時間が短かったことを考慮にいれても、リソースが共有されているシナリオにおいて、予約枠を持つコンシューマーが一貫してより多くのリクエストを処理できていたことが分かります。

遅れて参加者が加わった飽和状態時:予約枠が既存の利用者を保護する

「遅れての参加」シナリオでは、3つの本番エージェントがすでにシステムにトラフィックを氾濫させている状態の60秒後に、開発ユーザーが参加しました。すべての予約キャパシティがすでに使用されていたため、開発ユーザーは20%の未予約プール(約 200 RPM)に閉じ込められました。本番エージェントたちは、新しく追加された負荷に影響を受けることなく、自分たちの保証されたバケットからリクエストを処理し続けました。

これこそが、本番環境で最も重要となるシナリオです。バッチジョブが開始されたり、新しいアプリケーションが本番稼働したりして、突然「供給」を上回る「需要」が発生することがあります。予約枠がない場合、この新しい負荷によって全員のスループットが平等に押し下げられてしまいます。しかし予約枠があれば、ミッションクリティカルなコンシューマーは保護されます。

予約枠を持つコンシューマー同士の公平な競合

「予約枠のみ」のシナリオでは、開発ユーザーのアクティビティがなくなり(サイレント状態)、3つの本番エージェントだけが競合しました。彼らの成功率はほぼ同一(28.9%〜30.2%)であり、システムがそれぞれの予約枠に応じてプロポーショナル(比率に比例)にスループットを分配したことを示しています。

サーバー側の視点:OTELメトリクスが物語る真実

クライアント側のメトリクス(成功率や429エラーのカウント数)は、コンシューマー(利用者)が何を体験したかを教えてくれます。これに対して、サーバー側のOTEL(OpenTelemetry)メトリクスは、プラットフォーム自体が何を体験したかを示します。今回の検証用デプロイメントの内部は、以下のような状態になっていました。

レート制限機能がモデルの健全性を保護する

ピーク負荷時(エンドポイントに対して毎分 20,596 件のリクエストが到達している状態)において、NIMが実際に処理していたのは、レート制限機能が通過を許可した約 1,000 RPM のみでした。

エンドポイント側から見えたものNIM(モデル)側から見えたもの
毎分 20,596 件のリクエスト毎分約 1,000 件のリクエスト(処理実行)
毎分 19,603 件をレート制限により拒否18〜22 件の同時実行リクエスト
1.25秒のエンドツーエンド(E2E)レイテンシー(安定)
91–95% のGPU使用率(健全)

もしレート制限機能がなければ、これら毎分20,000件のリクエストはすべてNIMの内部にキュー(待ち行列)として溜まっていたはずです。GPUの使用率はすでに91〜95%に達しているため、これ以上生産性が上がることはありません。ただリクエストが積み重なるにつれて、レイテンシーが螺旋を描くように悪化していくだけです。しかし、実際にはレート制限機能が超過したリクエストを(推論処理のスピードではなく、高速な429応答のスピードで)即座に拒否したため、処理が受け付けられたトラフィックに対してモデルの応答性を完全に維持することができました。

OTELで収集したリクエスト数
OTELで収集したGPU使用率
トークンスループットは成功リクエスト数に連動する

ピーク時のトークンスループットは合計で毎分約 199,350 トークンに達し、その内訳は入力(インプット)が約 115,939 トークン、出力(アウトプット)が約 83,411 トークンでした。これらの数値は、クライアントが送信しようとした「試行リクエスト総数」ではなく、レート制限機能が通過を許可した「許容スループット」に直接連動しています。

この結果は、レート制限機能がトラフィックを正確にシェーピング(制御)できていることを示す、もう一つの証明と言えます。

OTELで収集したトークン生成数
OTELの統合ダッシュボード画面

レート制限とクォータ予約の選択基準

設定内容を決定する際は、以下のフローチャート(手順)に沿って判断してください。

ステップ 1:複数のコンシューマー(利用者)で共有されているデプロイメントですか?
  • いいえレート制限単体で十分です。 GPUを保護するためのキャパシティ数値を設定すれば作業は完了です。
  • はいステップ 2 へ進んでください。
ステップ 2:すべてのコンシューマーの重要度は等しいですか?
  • はいレート制限単体で事足りる可能性があります。 システム飽和時には、すべてのコンシューマーが対等に競合します(早い者勝ち)。それで問題がない場合は、ここで検討を終了してください。
  • いいえステップ 3 へ進んでください。
ステップ 3:最低限のスループットを保証しなければならないコンシューマーは存在しますか?
  • はいクォータ予約を追加してください。 ピーク時の競合において、各重要なコンシューマーが必要とする最低限の RPM に合わせてサイズを設定します。
  • いいえ、ただし一部のコンシューマーの優先度を下げたい ➔ 予約枠ではなく、エンティティごとの個別例外設定(上限値の設定) を使用してください。重要なコンシューマーに最低保証を与えるのではなく、「声の大きい隣人(ノイジーマイノリティ)」にキャップ(上限)をかけます。
ステップ 4:未予約プールを設定する
  • キャパシティの 100% すべてを予約枠に割り当てないでください。単発のトラフィック、オーバーフロー、およびまだ予約枠が割り当てられていない新規アプリケーションのために、10〜20% を未予約(共有枠)として残しておきます。

実践的な設定のヒント

  • まずはレート制限のみで開始する: 最初にデプロイメントのトラフィックパターンを1週間ほど監視します。ピーク時の RPM や、「誰が・何を送信しているか」、「特定の誰かが常にリソースを過剰消費していないか」を確認します。そのデータに基づいて、本当に必要な場所に予約枠を追加していきます。
  • 利用率のしきい値(Utilization Threshold)は 70〜80% に設定する: これにより、トークンバケットにバースト的な余裕が生まれ、ちょっとした微小な変動のたびにレート制限が発動するのを防ぎ、短時間のスパイクを吸収できるようになります。今回の検証例では 80% に設定したため、本格的な制限が適用される前に、キャパシティの1.2倍の負荷を緩やかに(グレースフルに)処理することができました。
  • OTELメトリクスで監視する: レート制限を設定した後は、サーバー側の以下のメトリクスをチェックして、意図通りに動作しているか確認してください。
  • deployment.requests vs deployment.requests.rate_limited — 適切な量が拒否されているか?
  • nvidia_gpu_utilization — モデルは飽和したままか、それともレート制限によって(処理の)ヘッドルーム(余力)が生まれたか?
  • nvidia_vllm:e2e_request_latency_seconds — 負荷がかかっている状態でもレイテンシーは安定しているか?
  • deployment.concurrent_requests — リクエストが滞留(キューイング)しているか、それともスムーズに流れているか?
予約枠のサイジング計算式

予約される具体的な分間リクエスト数(RPM)は、以下の公式で計算されます。

Reserved RPM = Capacity × Reserved %

計算例:
1000 RPM × 30% = 300 RPM (この数値が最低保証されます)

これをレート制限(上限値)と混同しないでください。30%の予約枠が意味するのは、「システムがどれほど飽和状態(大混雑)であっても、あなたは常に最低でも 300 RPM を確保できる」ということです。また、全体的なキャパシティに空きがある状態であれば、そのエンティティ(利用者)は 300 RPM を超えてリソースを消費することも可能です。

まとめ

機能防げるリスク導入すべきケース
レート制限GPUの過負荷、コンシューマーの暴走、レイテンシーのスパイク常に導入 — システムの安全網(セーフティネット)として機能します。
クォータ予約優先度の高い処理の枯渇(スターベーション)、声の大きい隣人(ノイジーマイノリティ)、SLA違反重要度の異なる複数のコンシューマーがシステムを共有している場合。
エンティティごとの例外設定特定のコンシューマーによるリソースの過剰消費他の誰かのためにキャパシティを予約(確保)することなく、特定の「声の大きい隣人」に上限(キャップ)をかけたい場合。

「レート制限」と「クォータ予約」のどちらを選ぶべきか迷ったときは、それぞれのツールが適した場所で使用してください。そして、課題の要求に応じて、これらを組み合わせてレイヤー(層)状に適用していきましょう。

AI で迅速にビジネス価値向上を実現。今すぐ始めましょう。