ローカルLLM本番投入でハマった話|量子化・マルチGPU・コスト削減の実録2026
月300万超のAPI費用とセキュリティ問題が重なり、ローカルLLM本番移行を決断。vLLM・llama.cppで実際に踏んだ量子化の落とし穴やマルチGPU構成のトラブルを赤裸々に共有します。
先日、チームのML基盤を大幅に刷新して、ようやくローカルLLMがまともにプロダクションで動くようになった。正直、去年の時点では「ローカルLLMはまだ玩具だな」と半ば諦めていたんだけど、2026年に入ってモデルの量子化品質・推論ランタイムの成熟度・GPUメモリ効率が一気に実用ラインを超えてきた実感がある。
Gemma 3系・Qwen3・Llama 4系モデルをvLLM + llama.cppで本番投入するまでの試行錯誤、量子化の選び方、マルチGPU構成時のトラブル、コスト比較まで、自分たちが実際に踏んだ轍を共有したい。API経由でクラウドLLMを使ってる人も「切り替えられるかも」と思えるくらい状況は変わってきてると思う。
同じくローカルLLM周辺で試行錯誤してる人には、以前書いたLoRAファインチューニング6ヶ月の実録も読んでもらえると、モデル適応の全体像が掴みやすいと思う。
なぜ今ローカルLLMに本腰を入れたか
きっかけはシンプルで、OpenAI APIのコストと、社内データをクラウドに送ることへの経営層からのプレッシャーが両方同時にきたことだ。月のAPI請求が300万円を超えたあたりで「ここで一回構成を見直さないとまずい」という話になった。
セキュリティ観点では、SOC2審査でCloudTrailとConfig設計が泥沼になった実録でも書いたように、外部APIへのデータ送信は監査証跡の管理が複雑になる。社内ドキュメントを扱うRAGパイプラインでは特に問題になりやすい。
それと、2025年後半からモデル品質が本当に変わった。EQ-Bench・MMLU・HumanEvalの各ベンチマークでローカルで動く70B級モデルがGPT-4クラスに肉薄してきて、「これ実用的じゃないか?」という認識が社内でも広まっていた。コスト・セキュリティ・品質の三拍子が揃ったタイミングで動かない理由がなくなった、という感じだ。
2026年のモデル選定と量子化の現実
最初にやったのがモデルと量子化フォーマットの選定で、ここで正直かなり時間を使った。
現時点でうちのチームが本番で使っているのは以下の3モデルだ:
- Qwen3-72B(Q4_K_M):コーディング支援・ドキュメント要約
- Gemma 3 27B(Q6_K):多言語対応・日本語ドキュメント処理
- Llama 4 Scout(Q4_0):軽量・低レイテンシが求められる用途
量子化の選び方、これが最初に迷うポイントだと思う。llama.cppのGGUFフォーマットは今や選択肢が多すぎて逆に辛い。個人的には「まずQ4_K_Mを試して、精度が足りなければ上げる」という順番で攻めるのがいちばんストレスが少なかった。
| 量子化 | メモリ使用量 | 品質劣化 | 推論速度 | 用途 |
|---|---|---|---|---|
| Q2_K | 最小 | 大きい | 最速 | 検証・デモのみ |
| Q4_0 | 小 | 中程度 | 速い | 低優先タスク |
| Q4_K_M | 中 | 小さい | やや速い | 本番バランス型 |
| Q5_K_M | 中大 | 非常に小 | 標準 | 精度優先タスク |
| Q6_K | 大 | ほぼなし | やや遅い | 日本語など繊細な用途 |
| Q8_0 | 大 | なし(実用上) | 遅い | ベースライン比較用 |
Q4_K_MとQ5_K_Mの品質差を実際に測ってみたところ、日本語生成では体感で分かるくらい差があった。英語のコード生成はQ4_K_Mで十分なんだけど、日本語の文章生成はQ6_K以上を使わないとアウトプットが微妙になる場面が多かった。これは想定外で、最初は「英語より日本語のほうが語彙が少ないから大丈夫でしょ」と甘く見ていた。
実際に計測したスループット(H100 SXM5 × 2枚構成):
xychart-beta
title "量子化別スループット(tokens/sec) - Qwen3-72B"
x-axis ["Q4_0", "Q4_K_M", "Q5_K_M", "Q6_K", "Q8_0"]
y-axis "tokens/sec" 0 --> 120
bar [98, 87, 74, 62, 41]
Q4_K_Mが実用上の「コスパ最強帯」というのはデータ的にも明らかで、Q8_0との差は倍以上ある。まだ完全に最適化しきれていない部分はあるが、まずはここを基準に考えるのが正解だと思う。
推論ランタイムの選定:vLLM vs llama.cpp vs Ollama
ランタイムの選択でかなり悩んだ。今の構成はユースケースによって使い分けている。「一つで全部賄おう」という発想を早めに捨てたのが良かった。
flowchart TB
subgraph Client["クライアント層"]
A[社内Webアプリ]
B[Slack Bot]
C[RAGパイプライン]
end
subgraph LB["Load Balancer (Nginx + upstream動的切替)"]
D[Router]
end
subgraph Inference["推論サーバー層"]
subgraph vLLM_Cluster["vLLM Cluster(高スループット)"]
E[vLLM Worker 1\nQwen3-72B Q4_K_M]
F[vLLM Worker 2\nLlama 4 Scout Q4_0]
end
subgraph LlamaCPP["llama.cpp サーバー(低レイテンシ)"]
G[llama-server\nGemma3 27B Q6_K]
end
subgraph Ollama_Dev["Ollama(開発・検証環境)"]
H[Ollama Instance\n各種モデル]
end
end
subgraph GPU["GPU層"]
I[H100 SXM5 × 2\nTensor Parallel]
J[RTX 4090 × 1\n単体推論]
K[RTX 4090 × 1\n開発用]
end
A --> D
B --> D
C --> D
D -->|バッチ・並列| E
D -->|低レイテンシ優先| G
D -->|Llama4軽量系| F
E --> I
F --> I
G --> J
H --> K
各ランタイムを実際に本番で使った感想をまとめると:
vLLM 0.9.x系(2026年5月時点)
PagedAttentionとcontinuous batchingの恩恵が大きく、同時接続が多いユースケースでは圧倒的だ。うちの社内チャットツール連携(Slack Bot経由で1日数千リクエスト)はvLLMなしでは成立しなかった。ただ、セットアップが素直じゃない部分がある。特にマルチGPUのTensor Parallelism設定で最初ハマった。
# vLLM起動コマンド(本番で使ってる構成)
python -m vllm.entrypoints.openai.api_server \
--model /models/qwen3-72b-q4_k_m.gguf \
--tensor-parallel-size 2 \
--max-model-len 32768 \
--gpu-memory-utilization 0.90 \
--enable-chunked-prefill \
--max-num-batched-tokens 8192 \
--port 8000
--gpu-memory-utilizationは最初0.95にしてOOMが頻発した。0.90で安定している。このあたりは環境によって違うと思うので、最初は保守的な値から始めることをすすめる。
llama.cpp(llama-server)
シングルリクエストの低レイテンシが必要な場面ではいまだにllama.cppが強い。Gemma 3 27Bを日本語ドキュメント処理に使っているが、TTFT(Time to First Token)がvLLMより一貫して速い。
# llama-server 起動設定
llama-server \
--model /models/gemma3-27b-q6_k.gguf \
--n-gpu-layers 999 \
--ctx-size 16384 \
--n-parallel 4 \
--flash-attn \
--port 8001
--flash-attnフラグ、2026年時点では安定して有効にできるようになった。去年まではたまに挙動が怪しかったが、今は問題ない。地味に嬉しい改善だ。
Ollama
開発・検証環境での使い勝手は圧倒的に良い。本番には使っていないが、エンジニアが手元でモデルを試すためのインターフェースとしては最高だと思う。プロンプト設計のイテレーションが爆速で回せる。プロンプト設計の構造化についての記事でも書いたが、試行錯誤の初期段階はOllamaで回すのが一番効率がいい。
マルチGPU構成で踏んだハマりどころ
H100 × 2枚のTensor Parallel構成で、最初の2週間はほぼハマりっぱなしだった。「GPUが2枚あれば2倍速くなる」という単純な話じゃないことを身をもって学んだ。主なトラブルを共有する。
NVLink確認の重要性
Tensor Parallelismは原則としてNVLink接続が前提だ。PCIeだとGPU間通信がボトルネックになって、シングルGPUより遅くなることもある。
# NVLink状態確認
nvidia-smi nvlink --status -i 0
# 出力例(NVLink有効な場合)
GPU 00000000:3B:00.0
Link 0: <Active>
Link 1: <Active>
...
うちの環境はHGXボードでNVLinkが有効だったが、別のプロジェクトで使っているPCIe接続のRTX 4090 × 2構成ではTP=2にしたら逆に遅くなった経験がある。GPUトポロジーの確認は必須だ。
CUDA_VISIBLE_DEVICESとプロセスアフィニティ
複数モデルを同一ホストで動かすとき、CUDA_VISIBLE_DEVICESで明示的に分離しないと干渉する。
# vLLM(GPU 0,1を使用)
CUDA_VISIBLE_DEVICES=0,1 python -m vllm.entrypoints.openai.api_server \
--tensor-parallel-size 2 ...
# llama-server(GPU 2を使用)
CUDA_VISIBLE_DEVICES=2 llama-server ...
これをやらずに同じGPUをvLLMとllama-serverが取り合って謎のクラッシュが発生した。原因特定に半日溶かした。エラーメッセージが全然ヒントにならなくて本当に辛かった。
実際のレイテンシ推移(本番導入後3ヶ月)
xychart-beta
title "P95レイテンシ推移(ms) - 社内チャットbot用途"
x-axis ["Week1", "Week2", "Week4", "Week6", "Week8", "Week10", "Week12"]
y-axis "P95 Latency (ms)" 0 --> 3000
line [2800, 2400, 1900, 1600, 1450, 1380, 1320]
Week1〜2の高さはチューニング中の値で、chunked prefillの有効化とGPUメモリ使用率の調整でWeek4以降は落ち着いてきた。今はP95で1.3秒前後で安定している。最初の2週間と比べると半分以下になったので、チューニングの効果は確かにあった。
コスト比較:クラウドLLM vs ローカルLLM
これが経営層を一番説得できた資料なので、実際の数字を出す。
| 比較軸 | OpenAI GPT-4o | ローカルLLM(H100×2) | 備考 |
|---|---|---|---|
| 月額コスト | 約320万円 | 約45万円(サーバー償却含) | 月300万token想定 |
| レイテンシ(P50) | 800ms | 680ms | うちのユースケース |
| データプライバシー | 外部送信あり | 完全内部完結 | 監査対応で重要 |
| モデル更新 | 自動(制御不可) | 任意タイミング | 再現性担保できる |
| カスタムファインチューニング | 費用高・制限あり | 自由 | LoRA適用済み |
| 障害時リスク | API障害に依存 | 自前管理が必要 | 運用コスト発生 |
月のコスト差が275万円。サーバー初期費用がH100 × 2で約800万円だったので、単純回収は3ヶ月を切る計算になる。もちろんこれは運用負荷・エンジニアの工数を含んでいないので実際はもう少しかかるけど、それでも投資対効果は十分あった。スライド一枚でこの表を見せたら、会議室の空気が変わったのを覚えている。
ただし、誤解してほしくないのは「ローカルLLMが常に正解」ではないということだ。うちでも最先端の推論能力が必要な研究系タスクはOpenAI o3を継続して使っている。使い分けが正解だと思う。
監視・運用体制で実際にやっていること
ローカルLLMの運用で地味に大変なのが監視だ。APIサービスと違ってSLAを自分たちで担保しなければならない。これが想定より工数を食った正直なところで、「動かすこと」と「安定して動かし続けること」は全然別の話だった。
現在の監視スタック:
sequenceDiagram
participant Client
participant Router as Nginx Router
participant vLLM
participant Prometheus
participant Grafana
participant Slack
Client->>Router: リクエスト
Router->>vLLM: 転送
vLLM->>Client: レスポンス
vLLM-->>Prometheus: メトリクス収集(/metrics)
Prometheus-->>Grafana: 可視化
Grafana-->>Slack: アラート通知
note over Prometheus: GPU使用率・キュー深度<br/>TTFT・スループット監視
vLLMは/metricsエンドポイントでPrometheusメトリクスを吐いてくれる。特に監視しているのは:
vllm:num_requests_waiting:キュー詰まりの検知vllm:gpu_cache_usage_perc:KVキャッシュ枯渇の予兆vllm:time_to_first_token_seconds:P95レイテンシ
KVキャッシュ使用率が85%を超えたらアラートを出す設定にしている。これを超えるとスループットが急激に落ちる経験をした。閾値の設定は経験則に近いが、80〜85%あたりが現実的なラインだと思う。
llama-serverはPrometheusネイティブ対応ではないので、カスタムエクスポーターを書いた。正直これが地味に面倒で、vLLMとllama.cppで監視コードが分離してしまっているのは今も課題として残っている。誰かいい解決策を持っていたら教えてほしい。
RAGパイプラインとの統合についてはRAG本番運用1年で痛感した実録が参考になると思う。特にチャンキング戦略とベクトル検索の部分はローカルLLM構成と組み合わせることが多いので、セットで読んでおくと良い。
まとめ
2026年時点のローカルLLM本番運用を実際にやってみて、重要だと思ったポイントを整理する。
-
量子化はQ4_K_Mをベースラインにする:日本語など精度が重要な用途はQ6_K以上。コードはQ4_K_Mで十分なケースが多い。
-
ランタイムはユースケースで使い分ける:高スループットが必要ならvLLM、低レイテンシシングルリクエストならllama.cpp、開発・検証はOllama。全部を一つで賄おうとしない。
-
マルチGPU構成はNVLinkの確認が最初のステップ:PCIe接続でTensor Parallelismを使っても恩恵が得られないケースがある。
-
監視はPrometheus + Grafanaでvllm:num_requests_waitingとKVキャッシュ使用率を必ず監視する:これを怠るとキュー詰まりで無音障害が起きる。
-
コスト回収は思ったより早い:うちのケースでは3ヶ月以内に初期投資を回収できる試算になった。ただし運用工数を忘れずにカウントすること。
次のアクションとして、まず手元で試したい人はOllamaをインストールしてollama run qwen3:14bから始めるのが一番早い。14Bクラスならゲーミング用のRTX 4070 Ti(16GB VRAM)でもQ4_K_Mが動く。「本当にローカルで動くんだ」という体験から始めると、その後の技術選定の解像度が上がると思う。
皆さんのチームではローカルLLMの検討はどこまで進んでますか?「まだクラウドAPIで十分」という判断も全然ありだと思うし、逆に「もっと早く移行すればよかった」という声も聞く。ユースケース次第で答えが変わる領域なので、ぜひコメントや@での共有を聞かせてほしい。