高パフォーマンスなLLM推論:プロダクション環境におけるvLLMとDockerのスケーリング

AI tutorial - IT technology blog
AI tutorial - IT technology blog

単なるHuggingFaceラッパーからの脱却

ローカル環境で簡単なテストのために大規模言語モデル(LLM)を動かすのは容易です。しかし、GPUを炎上させることなく、同じモデルを500人の同時実行ユーザーに提供するのは全く別の話です。多くの開発者は、まずモデルをFastAPIやFlaskアプリでラップすることから始めます。これは単一ユーザーなら機能しますが、GPUのメモリ管理方法が原因で、わずかな負荷でもパフォーマンスはすぐに限界に達します。

数個の短い文章を処理しているだけなのに、GPUが「Out of Memory(OOM)」エラーでクラッシュするのを目の当たりにしたことがあるなら、あなたはKVキャッシュの問題を直接体験したことになります。従来のサービング手法では、断片化によりGPUメモリの50%から80%が浪費されることがよくあります。vLLMはPagedAttentionによってこの問題を解決します。これはオペレーティングシステムの仮想メモリのようにメモリを管理し、スループットの大幅な向上と軽快なレスポンスを可能にします。

クイックスタート(5分)

Dockerは、プロダクション対応のサーバーを稼働させるための最もクリーンな方法です。Dockerが実際にハードウェアと通信できるように、NVIDIA Container Toolkitがインストールされていることを確認してください。

1. vLLMイメージのプル

公式イメージを取得します。これには、高速な推論に必要なCUDAの依存関係と最適化されたカーネルが含まれています。

docker pull vllm/vllm-openai:latest

2. Llama 3モデルの起動

以下のコマンドを実行して、Llama 3 (8B)サーバーを起動します。ポート8000をマッピングし、ダウンロード時間を節約するためにローカルのHuggingFaceキャッシュをマウントします。

docker run --gpus all \
    -v ~/.cache/huggingface:/root/.cache/huggingface \
    -p 8000:8000 \
    --env "HUGGING_FACE_HUB_TOKEN=<あなたのトークン>" \
    vllm/vllm-openai:latest \
    --model meta-llama/Meta-Llama-3-8B-Instruct

3. API의 테스트

vLLM은 OpenAI API의 구조를 모방하고 있으므로 기존 도구와 즉시 통합할 수 있습니다. curl로 테스트해 봅시다.

curl http://localhost:8000/v1/chat/completions \
    -H "Content-Type: application/json" \
    -d '{
        "model": "meta-llama/Meta-Llama-3-8B-Instruct",
        "messages": [{"role": "user", "content": "量子コンピュータについて1文で説明してください。"}]
    }'

アーキテクチャ:なぜPagedAttentionが重要なのか

その速度の理由を理解するには、LLMがどのようにテキストを生成するかを見る必要があります。予測されたすべてのトークンは、KVキャッシュと呼ばれるGPUメモリに保存される「コンテキスト」を作成します。標準的なバックエンドは、これを4,096トークンなどの最大シーケンス長に合わせて、1つの巨大な連続したブロックとして割り当てます。

プロンプトがわずか50トークンであっても、残りの4,046スロットは予約されたまま空の状態で放置されます。これが内部断片化(Internal Fragmentation)です。数十人のユーザーが接続すると、実際のデータがわずかであっても、これらの無駄なブロックが積み重なり、GPUのVRAMが不足してしまいます。

効率的なメモリ管理

vLLMはKVキャッシュを小さく柔軟なブロックに分割します。これらは物理メモリ上で隣り合わせである必要はありません。スペースを動的に管理することで、vLLMは同じGPUにより多くのリクエストを詰め込むことができます。A100でのベンチマークでは、単純なラッパーからvLLMに切り替えることで、長いコンテキストのタスクでスループットが24倍向上しました。

重い負荷に対する高度なスケーリング

モデルが1枚のカードに収まらないほど大きい場合や、トラフィックが激しい場合は、ハードウェア全体で水平方向にスケールさせる必要があります。

テンソル並列化(Tensor Parallelism)

70BモデルはFP16で約140GBのVRAMを必要とし、単一のA100には収まりません。--tensor-parallel-sizeフラグを使用して、モデルを複数のGPUに分割します。

docker run --gpus all vllm/vllm-openai:latest \
    --model meta-llama/Meta-Llama-3-70B-Instruct \
    --tensor-parallel-size 4

このコマンドは4枚のGPUを1つの大容量ユニットに変え、膨大なパラメータ数を処理するために同期して動作させます。

量子化(AWQおよびGPTQ)

VRAMは高価です。RTX 3090(24GB)のようなコンシューマー向けハードウェアで実行している場合は、4ビット量子化を使用してください。これにより、論理や精度にほとんど影響を与えることなく、メモリ使用量を約50%削減できます。

docker run --gpus all vllm/vllm-openai:latest \
    --model casperhansen/llama-3-8b-instruct-awq \
    --quantization awq

現場で得られた教訓

AIのデプロイは単にコマンドを実行するだけではありません。プレッシャーの下でサービスを維持し続けることが重要です。プロダクションクラスターの管理から学んだことを共有します。

メモリ使用率の調整

vLLMはデフォルトでVRAMの90%を確保します。スループットには有利ですが、他のものが入る余地がなくなります。モニタリングエージェントやサイドカーが動作している場合は、システムを安定させるために --gpu-memory-utilization 0.8 を設定してください。

70Bモデルの「コールドスタート」問題

140GBのモデルのロードには時間がかかります。Kubernetesを使用している場合、ロードが完了する前にLivenessプローブがコンテナを強制終了してしまう可能性があります。initialDelaySecondsを少なくとも300秒に設定してください。せっかちなヘルスチェックによって引き起こされる無限の再起動ループを何度も見てきました。

バージョンの固定

プロダクション環境では決して :latest を使用しないでください。vLLMは頻繁にアップデートされ、新しいイメージによってデプロイスクリプトを壊すようなフラグの変更が行われる可能性があります。vllm/vllm-openai:v0.4.2 のような特定のタグを使用し、環境の再現性と安定性を確保してください。

vLLMをDockerでセットアップすることは、OpenAIのような大手プロバイダーのパフォーマンスに匹敵する堅牢な基盤を提供します。これにより、遅くて脆弱なモデルが、現実世界のユーザーに対応できる、レスポンスの良いAPIへと生まれ変わります。

Share: