7月31日、Together AIが「Autoscaling endpoints for LLM inference」と題した記事を公開した。LLM推論エンドポイントのオートスケーリングでは、WebサービスやバッチMLワークロードで通用するメトリクスがそのまま機能しない——この記事はその理由を実験データで明示し、現場で使える設計指針を示している。
「とりあえずオートスケール」がLLMでは通じない理由
WebサービスのオートスケーリングはCPU使用率をトリガーにするのが定番だが、LLM推論ではこの前提が崩れる。Together AIはその理由として2点を挙げている。
CPUと違い、GPU使用率は「詰まり具合」を反映しない。 GPUが60%使用中と表示されていても、推論エンジンのリクエストキューはすでに積み上がっている可能性がある。GPU使用率が測っているのは「演算の密度」であって「負荷の圧力」ではないからだ。
また、LLM推論はバッチ推論とも性質が異なる。バッチ推論は事前にジョブサイズが確定しているためGPU使用率がほぼ100%に張り付くことが多く、使用率をトリガーにスケールしやすい。一方、オンラインLLM推論はリクエストが不規則に到着し、各リクエストの処理時間もプロンプト長・生成長によって大きくばらつくため、使用率だけでは「今まさにキューが詰まっているか」を判別できない。vLLMのcontinuous batchingブログでも解説されているように、推論エンジン側は内部でリクエストを細粒度にインタリーブ処理しており、この仕組みがメトリクスの見かけ上の安定をさらに強める。
コールドスタートに数分かかる。 新しいレプリカを起動するには、GPUノードへの配置→数十GBの重みのダウンロード→VRAMへのロード→ウォームアップという工程が必要で、実測では1×H100のレプリカで最短86秒、カスタムファインチューン済みモデルでは145秒かかる。さらに、レプリカがREADYになってからルーティングで最初のトークンが返るまでに**+26〜40秒**の追加レイテンシがある。
| シナリオ | 計測値 |
|---|---|
| カタログモデル create → READY | 86秒 |
| カスタムファインチューン (18GB) create → READY | 145秒 |
| READY → 最初のトークン(ルーティング経由) | +26〜40秒 |
| 1→2レプリカへのスケールアップ | 約2.5分 |
| STOPPED状態からの再起動(重みはプラットフォーム側にキャッシュ済み) | 約1〜2分 |
スパイクが90秒で10倍になり、コールドスタートが4分かかるなら、スケールアップは間に合わない。「スケールアップのコストは数レプリカ分の課金、スケールダウンの失敗コストはレイテンシ悪化+再スケールアップ時のコールドスタート代」というトレードオフを踏まえると、スケールアップのウィンドウは短く、スケールダウンのウィンドウは長くするのが基本方針だ。
3種類のメトリクスと、実験で見えた現実
Together AIのプラットフォームでは8種類のメトリクスからオートスケールの指標を選べる。大きく3カテゴリに分類される。
- 並列数ベース(inflight_requests): 先行指標。リクエストがキューに積まれ始めた時点で反応するため、レイテンシが悪化する前にスケールを判断できる。デフォルト値はターゲット8(1レプリカあたり同時8リクエスト)。
- SLOベース(ttft、e2e_latency): 遅行指標。TTFTのp95がしきい値を超えた時点でユーザーはすでに遅延を体感している。
min_replicasに余裕を持たせることとセットで使う必要がある。なお、ttftはストリーミングのパスでしか計測されないため、ノンストリーミングのクライアントには使えない(e2e_latencyは両方に対応)。 - 効率ベース(gpu_utilization、token_utilization): 遅行指標。コストを最優先する場合向けだが、GPUが「ビジー」でも「レイテンシが健全」とは限らない。100%に近いターゲットはバーストの余裕をなくす。
同一負荷に3つのポリシーをぶつけた実験
カタログモデル(1×H100、レプリカ上限3)で、12〜48 RPSのサイン波に80 RPSのスパイクを2回乗せた負荷を3つのポリシーで処理した結果が以下だ。
| ポリシー | スケールしたか | レプリカ分 | リクエスト数(エラー数) | 何が起きたか |
|---|---|---|---|---|
| inflight_requests (target 8) | 1→2→3 | 26 | 40,600件(536件) | 波ごとに反応し、追加レプリカ稼働中はp95が改善 |
| ttft p95 (target 300ms) | スケールせず | 18 | 46,400件(6件) | エンジン側TTFTは閾値以下のまま。スケール不発 |
| gpu_utilization (target 75%) | スケールせず | 18 | 46,500件(2件) | GPU使用率は75%を超えなかった。スケール不発 |
この実験の核心はここだ。クライアント側のp95レイテンシは3〜5秒で明らかに飽和しているのに、ttftもgpu_utilizationもスケールしなかった。
理由は、LLMエンジンのcontinuous batching(コンティニュアスバッチング)にある。この仕組みでは、進行中のリクエストの生成ステップの隙間に新規リクエストのprefillを差し込むことで、GPUを遊ばせずに次々とリクエストを処理する。その結果、エンジン内部で見えるTTFT(最初のトークンが返るまでの時間)は見かけ上低く抑えられる。しかし実際にはキューで待機している時間がクライアント側の体感レイテンシに積み上がっており、エンジン内部のTTFTだけを見ていると「詰まり」を見逃す。gpu_utilizationは短いバーストリクエストがGPUの帯域を占有し切らないために75%の壁を超えない。
inflight_requestsだけが問題を見抜き、唯一スケールした。 これがデフォルト指標である理由だ。
スケールゼロとコールドスタートの扱い
min_replicas: 0はmax_replicas: 0とセットでのみ有効で、デプロイメントを明示的に停止する操作を意味する。スケールゼロからの自動復帰(リクエスト起動)には対応していない。停止中のエンドポイントへのリクエストはエラーを返す。
tg beta endpoints update dep_abc123 --min-replicas 0 --max-replicas 0 # 明示的な停止
この動作から、開発・ステージング環境では自動停止+手動起動の運用が現実的だが、p95 SLOがある本番環境や、無人で稼働するカナリア・ブルーグリーンデプロイのような並行環境ではmin_replicas: 1以上を維持するのが推奨だ。リクエストが突発的に到着しうる環境でスケールゼロを使うと、コールドスタートが完了するまでの数分間すべてのリクエストがエラーになる点に注意が必要だ。
ポリシーの設定例
tg beta endpoints update "$DEPLOYMENT_ID" \
--min-replicas 1 --max-replicas 6 \
--scale-up-window 60s --scale-down-window 300s \
--scaling-metric ttft --scaling-target 500 --scaling-percentile p95
Together AIが推奨する手順は「まずデフォルト(inflight_requests target 8)で1週間動かしてメトリクスを観察し、その後チューニングする」だ。レプリカカウントがのこぎり波状に上下する場合はscale_down_windowが短すぎるサインで、窓を広げることで余分なコールドスタートを削減できる。
詳細はAutoscaling endpoints for LLM inferenceを参照していただきたい。




