ヘルスチェックにより、正常稼働中のモデルサーバーが1日に8回再起動されました
オペレーション
7月22日、ウォッチドッグによりローカルモデルサーバーが8回再起動されました。そのうちクラッシュを伴うものは1つもありませんでした。モデルを稼働させ続けるはずのチェックが、逆に停止させていたのです。
何が起きたか
モデルサーバーはNVIDIA GB10マシン1台でvLLMを実行し、単一リクエストに対して毎秒約8トークンを生成していました。ウォッチドッグは5分ごとに実際のチャットリクエストを送信し、応答を最大45秒間待ち受けます。連続して5回のチェックに失敗するとサーバーがハングしたと判断され、ウォッチドッグが再起動を実行します。再起動には数分かかり、その間約19 GBの重みデータが再読み込みされます。
その日、バッチジョブがサーバー上で2〜4件のリクエストを数分間占有していました。ウォッチドッグのリクエストはそれらの後ろでキューに入り、タイムアウトして失敗とカウントされました。こうしたチェックが5回続いた後、正常なサーバーが再起動されました。ウォッチドッグが尊重する「ビジー」フラグファイルは存在しましたが、バッチジョブはそれを設定していませんでした。
1回の誤った再起動が次の誤った再起動を招いた経緯
再起動のたびに、進行中のリクエストは500エラー(「cannot schedule new futures after shutdown」)で失敗しました。別のウォッチドッグは500エラーをカウントし、数が多すぎるとサーバーを再起動します。エラーが増えるほど、その動作は早くなります。つまり、誤った再起動が1回発生するたびに、次の再起動がより早く実行されるようになりました。合計4つの独立したウォッチドッグが、5分、10分、15分、30分のタイマーでそのサーバーを再起動できました。
そこから得たルール
ヘルスチェックは、監視対象のリソースを使ってはいけません。モデルサーバーの希少なリソースは、進行中のリクエスト用のスロットです。そのスロットを占有する、またはスロットを待つチェックは、判定すべき負荷の一部になってしまいます。低コストでチェックを行い、サーバーがスタックしていることを確認できた場合のみ再起動してください。
置き換えたチェックの内容
vLLMは独自のカウンタを /metrics で公開しています。新しいチェックはまずそれらを読み取ります。
- 実行中のリクエストがあり、エンジンが進行している場合: 正常かつビジー状態です。テストリクエストは送信されません。
- 2回の読み取りの間で実行中のリクエストはあるが進行がない場合: スタック状態です。この場合のみ、依然として再起動が行われます。
- 実行中のリクエストがない場合: アイドル状態です。この場合のみ、チェックは短い応答を要求します。アイドル状態のエンジンは即座に応答します。
最初のバージョンでは、生成されたトークン数を進行状況としてカウントしていました。8回のテストに合格しましたが、初回のライブ実行では、サーバーがビジーかつ正常であることが判明しました。実行中のリクエストは2件、2回の読み取りの間で107個の新しいトークンが生成されました。
それ以降の変更点
- 出力トークンではなく、エンジンステップ。vLLM が長いプロンプトを読み込んでいる間、しばらく出力トークンは生成されませんが、ステップは実行され続けています。ステップ数(vllm:iteration_tokens_total のカウント)を追跡することで、長いプロンプトの処理をサーバーのスタックと誤認することを防ぎます。
- 「スタック」と判定する前に 2 回の待機を設けることで、短い一時停止を障害としてカウントしないようにします。
- 2 回の読み取りの間に発生した再起動は、同じメトリクス内のプロセス起動時刻によって検出されます。その場合、チェックは異なる 2 つのプロセスのカウンターを比較するのではなく、応答を要求します。
- プロキシは /models のみを介してチェックします。最小接続数プロキシの背後では、/metrics の各読み取りが異なるバックエンドから返される可能性があります。これは、私たちが 2 台のマシンそれぞれにモデルのコピーを実行した際に確認した挙動です。バックエンドは直接チェックされます。
その違いは明白です。このメモを書いている時点で、両方のモデルサーバーは上限である 12 件のリクエストをそれぞれ処理していました。テストリクエストはキューで待機することになります。新しいチェックは、リクエストを送信せずに、両サーバーをビジーかつ正常(それぞれ毎秒 126 トークンと 101 トークン)と報告しました。
Factory Floor 1.0.2 では、このチェックを models/llm-health.py として提供しています。アイドル、ビジー、スタック、再起動したサーバー、メトリクスを持たないサーバー、プロキシ、および停止したバックエンドに対するテストも含まれています。