Chuyển đến nội dung chính
友田 陽大
Llama & open-weight LLMs
Llama
vLLM
生成AI
GPU
MLOps
セルフホスト
Python

vLLM で Llama を本番セルフホストする:高スループット推論サーバ運用記

Llama を自前GPUで本番運用するための vLLM 実践ガイド。連続バッチとPagedAttentionでスループットを最大化し、FP8量子化・テンソル並列で詰め、OpenAI互換エンドポイントとして提供。ヘルスチェック・可観測性・オートスケール・グレースフルドレイン・Bedrockフォールバック・ネットワーク隔離まで、落ちない推論基盤の作り方を実コードで。

Published
Reading time
9 min read
Author
友田 陽大
Chia sẻ

この記事のゴール

Llama を本番投入する全体像で「自前運用なら vLLM」と書きました。本稿はその実装の runbook です。データを外に出せない・1リクエスト単価を最小化したい・微調整した重みを動かしたい——そんな要件で Llama を自分のGPUで、落ちずに、速く動かすための全工程を実コードで示します。

ゴールは、vllm serve を叩けることではなく、**ヘルスチェック・可観測性・オートスケール・回復性・セキュリティを備えた“本番の推論基盤”**を組めることです。

信頼性の開示:GPU を本番で回す難しさ(スループット・障害・コスト)は、動画AIローカライズ基盤で実際に踏んだ領域です。本稿のコマンド・フラグは公式(vLLM Blog: Llama 4vLLM Recipes)に基づきます。スループットの実数は環境依存・要ベンチです。


なぜ vLLM なのか

自前サーブの勝敗は「GPU をどれだけ遊ばせないか(実効スループット)」で決まります。vLLM はここに最適化されています。

  • PagedAttention:KVキャッシュを OS の仮想メモリのようにページ管理し、断片化を抑えてより多くの同時リクエストを載せる。
  • 連続バッチ(continuous batching):到着したリクエストを動的に束ねて処理。1件が終わる前に次を詰めるので、GPU が空転しない。これがセルフホストの損益分岐を直接動かす。
  • OpenAI 互換サーバ:8000/v1 を提供。本番コードを一切変えずに向け先だけ差し替えられる。

サーブする:Scout を FP8 × テンソル並列で

公式 recipe に沿った最小の本番コマンドです。FP8 量子化で VRAM を圧縮し、テンソル並列で複数 GPU に分散します。

# Llama 4 Scout(FP8)を 2GPU にテンソル並列、KVキャッシュもFP8で文脈を稼ぐ
vllm serve meta-llama/Llama-4-Scout-17B-16E-Instruct-FP8 \
  --tensor-parallel-size 2 \
  --kv-cache-dtype fp8 \
  --max-model-len 500000 \
  --gpu-memory-utilization 0.90 \
  --port 8000
  • --tensor-parallel-size 2:重みとKVを2GPUに分割。台数は VRAM とモデルで決める。
  • --kv-cache-dtype fp8:KVキャッシュをFP8化。使える文脈長が実質的に伸び、スループットも改善(精度劣化は実用上小さい)。
  • --max-model-len:実効上限。Scout は本来 10M だが、VRAM とレイテンシの現実で絞る。青天井にしない。
  • --gpu-memory-utilization:確保率。上げすぎると OOM、低すぎると同時実行が減る。

🔧 ハマりどころ:FP8 は H100/H200/Ada/Blackwell など対応GPUが前提。非対応GPUだと起動失敗かフォールバックで遅くなる。nvidia-smi で世代を、起動ログで実際の dtype を必ず確認。

立ち上がれば、そのまま OpenAI クライアントで叩けます

import OpenAI from "openai";
// 本番コードは無改修。baseURL を自前エンドポイントに向けるだけ。
const llm = new OpenAI({ baseURL: "http://llama-internal:8000/v1", apiKey: "internal" });
const r = await llm.chat.completions.create({
  model: "meta-llama/Llama-4-Scout-17B-16E-Instruct-FP8",
  messages: [{ role: "user", content: "RAGの再ランキングを2行で" }],
});

コンテナ化:再現可能なイメージにする

本番は手作業の pip install で再現性を失います。イメージに固めるのが原則です。

# Dockerfile — vLLM の公式イメージを基盤に、モデルは実行時にマウント/取得
FROM vllm/vllm-openai:latest

# モデルIDと並列数は環境変数で外出し(イメージは1つ、構成は差し替え)
ENV MODEL_ID=meta-llama/Llama-4-Scout-17B-16E-Instruct-FP8 \
    TP_SIZE=2

# ヘルスチェック:/health が 200 を返すまでトラフィックを入れない
HEALTHCHECK --interval=15s --timeout=5s --start-period=600s --retries=10 \
  CMD curl -fsS http://localhost:8000/health || exit 1

ENTRYPOINT ["sh", "-c", \
  "vllm serve $MODEL_ID --tensor-parallel-size $TP_SIZE --kv-cache-dtype fp8 --port 8000"]

--start-period を長め(モデルロードは数分かかる)に取るのが地味だが重要。ロード中に unhealthy 判定で再起動ループに陥る事故を防ぎます。


可観測性:vLLM は最初からメトリクスを出す

vLLM は Prometheus 形式のメトリクス/metrics に出します。これを取り込めば、スループット・待ち行列・GPUキャッシュ使用率・TTFT/TPOTが可視化でき、「いつスケールすべきか」を数字で判断できます。

# 監視で見るべき主要メトリクス(例)
# vllm:num_requests_running     … 実行中リクエスト数(飽和の指標)
# vllm:num_requests_waiting     … 待ち行列(増え続けるならスケール)
# vllm:gpu_cache_usage_perc     … KVキャッシュ使用率(高止まりは限界)
# vllm:time_to_first_token_*    … 体感レイテンシ
curl -s http://localhost:8000/metrics | grep -E "num_requests|gpu_cache_usage"

num_requests_waiting継続的に積み上がるなら容量不足のサイン。ここをトリガにGPUノードをオートスケールします。可観測性の設計思想はOpenTelemetry の記事に揃えてください(メトリクス・ログ・トレースの相関、PII非出力)。


オートスケールとグレースフルドレイン

GPU は高価なので、需要に追従させます。同時に、スケールインや更新で処理中のリクエストを切らないことが品質です。

  • スケールアウト判断num_requests_waiting / キュー滞留時間をトリガに、GPUノードを増やす。コールドスタート(モデルロード数分)を見込み先回りで増やす。
  • グレースフルドレイン:終了シグナルを受けたら新規受付を止め、処理中を完走させてから落とす。ロードバランサのレディネスを先に落とし、preStop で待つ。
# k8s(抜粋):レディネスで安全に出し入れし、preStop で在庫を捌いてから落とす
readinessProbe:
  httpGet: { path: /health, port: 8000 }
  periodSeconds: 10
lifecycle:
  preStop:
    exec: { command: ["sh", "-c", "sleep 30"] } # 在庫リクエストの完走待ち
terminationGracePeriodSeconds: 120

回復性:自前ノードの障害を全体障害にしない

セルフホストは落ちる前提で設計します。クライアント側にタイムアウト・リトライ・フォールバックを持たせ、自前ノードが死んでもBedrockに逃がして止めない

// lib/llama-client.ts — 自前vLLM優先、ダメならBedrockへフォールバック(止めない)
import OpenAI from "openai";

const selfHosted = new OpenAI({ baseURL: process.env.VLLM_URL, apiKey: "internal", timeout: 30_000 });

export async function generate(prompt: string): Promise<{ text: string; via: "self" | "bedrock" }> {
  try {
    const r = await selfHosted.chat.completions.create({
      model: "meta-llama/Llama-4-Scout-17B-16E-Instruct-FP8",
      messages: [{ role: "user", content: prompt }],
    });
    return { text: r.choices[0]?.message.content ?? "", via: "self" };
  } catch (err) {
    // タイムアウト/接続不可/5xx は“正常系”として吸収し、マネージドへ退避する。
    const text = await viaBedrock(prompt); // Converse API 実装は別関数に分離(SRP)
    return { text, via: "bedrock" };
  }
}

連鎖障害を防ぐには、フォールバック側にもサーキットブレーカーを入れて殺到を止めます(リトライ/バックオフ/サーキットブレーカー)。


セキュリティ:推論エンドポイントは「公開しない」

vLLM の OpenAI 互換サーバはそれ自体に強い認証がありませんインターネットに直接晒さないのが大前提です。

  • ネットワーク隔離:private subnet / VPC に置き、外部から直接到達できないようにする。
  • 前段に認証ゲートウェイ:API Gateway / リバースプロキシで認証・レート制限・監査ログを担う。アプリは内部DNSで叩く。
  • データ非送出:そもそもセルフホストの動機は「外に出さない」こと。ログにもプロンプト本文(PII)を残さない——メタデータだけ記録する。
  • 入力検証:プロンプトも外部入力。長さ上限・スキーマ検証を境界で行う(型安全の規律)。

負荷試験:本番前に「どこで折れるか」を知る

スループットとレイテンシは推測せず計測します。同時実行を段階的に上げ、num_requests_waiting が跳ね TTFT が劣化する点=飽和点を見つけ、そこから1ノードあたりの安全な同時実行数を決めます。

# vLLM 同梱のベンチで実効スループットを測る(数値は環境依存・要実測)
python -m vllm.entrypoints.benchmarks.benchmark_serving \
  --backend openai --base-url http://localhost:8000 \
  --model meta-llama/Llama-4-Scout-17B-16E-Instruct-FP8 \
  --num-prompts 500 --request-rate 20

ここで得た実効スループットを、コスト記事の式に入れて初めて、セルフホストの損益分岐が数字で出ます。


よくある質問(FAQ)

Q. Ollama と vLLM はどう違う? A. Ollama はローカル開発・単機・お手軽vLLM は本番の高スループット。連続バッチで多数同時を捌くのは vLLM。開発は Ollama、本番は vLLM、と使い分けます(ピラー記事のセルフホスト章)。

Q. 何台のGPUが要る? A. モデル・量子化・文脈長次第。Scout(FP8) は少数GPUのテンソル並列が現実的。まず1ノードで負荷試験し、num_requests_waiting が飽和する点から逆算して台数を決めます。

Q. 文脈長 10M を設定していい? A. できますがVRAMとレイテンシが破綻します。--max-model-len実際に必要な長さに絞るのが本番の鉄則。長文脈は --kv-cache-dtype fp8 と併せて現実的な範囲で。

Q. ファインチューニングした重みも動かせる? A. はい。LoRAをマージした重みvllm serve の対象にすれば、特化モデルをそのまま本番提供できます。

Q. マネージド(Bedrock)と比べて運用は重い? A. 重いです。スケール・障害・更新・監視を自前で持つ分、定常大量でコストが見合うときに選ぶべき。少量・変動はBedrockが正解です。


まとめ

vLLM の serve は1行ですが、本番の推論基盤はその外側にあります。

  1. 連続バッチ × FP8 × テンソル並列でスループットを最大化する。
  2. ヘルス/メトリクス/オートスケール/ドレインで落ちない・切らない。
  3. クライアント側のフォールバックで自前障害を全体障害にしない。
  4. 隔離+認証ゲート+データ非送出で守る。
  5. 負荷試験の実数コスト式に入れて損益分岐を出す。

プライベートで可観測・回復性のある Llama 推論基盤を、負荷試験とコスト試算まで含めて構築します。GPU 本番運用の実績をご覧のうえご相談ください。一人 × 生成AIで、速く・安く・安全に。

出典・公式リソース

※ フラグ・スループット・GPU要件は更新・実測依存です。実装前に一次情報と自社ベンチで確認してください。

友田

友田 陽大

Người phát triển sản phẩm đoạt Giải thưởng Bộ trưởng (METI). Với TypeScript + Python + AWS, tôi một mình triển khai trọn gói SaaS, chuyển đổi số công nghiệp và AI tạo sinh (RAG) sẵn sàng cho production — từ phân tích yêu cầu đến hạ tầng và vận hành.

I can take on the implementation from this article as an engagement

Self-hosting infrastructure for open-weight LLMs — build, production operations, and cost optimization

I've run Qwen3-8B-AWQ and 4-bit quantized Llama 3 in production on vLLM, operated as a reproducible inference stack on spot GPUs (Tesla T4) with Terraform. Quantization choice, VRAM and throughput sizing, swappable models behind a provider abstraction, and resumption after forced preemption — I design and implement inference infrastructure that stays up.

Nhận cả dự án trọn gói lẫn vai trò cố vấn kỹ thuật. Hãy bắt đầu bằng buổi tư vấn miễn phí 30 phút.

最短ルート:カレンダーから直接予約

相談内容が固まっている方は、フォーム送信よりその場で日程を確定する方がスムーズです。下記から空き時間をお選びください。

  • 30分のオンライン無料相談
  • Google Meet / Zoom / Microsoft Teams
  • NDA 商談前締結可・無理な営業はいたしません
無料相談の空き枠を予約する

Cũng đáng đọc