Cloud Run GPUで検証用のLLM推論基盤を構築し、コールドスタートを最大約60%短縮した検証記録
株式会社コロプラ / 斎藤拓海
メンバー / 機械学習エンジニア / 従業員規模: 501名〜1,000名 / エンジニア組織: 101名〜300名
| 利用プラン | 利用機能 | ツールの利用開始時期 | 事業形態 |
|---|---|---|---|
従量課金 | GPU(NVIDIA RTX PRO 6000)、Cloud Storage ボリュームマウント、Direct VPC egress、scale-to-zero | 2026年7月 | B to C |
| 利用プラン | 従量課金 |
|---|---|
| 利用機能 | GPU(NVIDIA RTX PRO 6000)、Cloud Storage ボリュームマウント、Direct VPC egress、scale-to-zero |
| ツールの利用開始時期 | 2026年7月 |
| 事業形態 | B to C |
アーキテクチャ

アーキテクチャの意図・工夫
- 1GPUに複数のvLLMを相乗りさせている。GPUのクォータの制約下で複数モデルを提供するため。llama-swapのgroup機能(swap: false)で全モデルを常駐させ、
modelパラメータでルーティング。ウォーム時のモデル切り替えは0〜2秒 - keep-aliveを自作した。Cloud Runに「アイドル後 N 時間維持」の設定はないため、コンテナ内から自サービスへ認証付きpingを送る仕組みを実装。プロキシログを監視して「最後の実アクセスから2時間」のスライディングウィンドウで維持する
- 接続ツールを配布している。Cloud Run IAMのIDトークンは最長1時間で失効するため、
gcloud run services proxyをラップしたワンクリックツール(モデル選択メニュー付き)を配布し、利用者からは「localhost に無認証・無期限で叩ける」体験にした
導入の背景・解決したかった問題
導入背景
社内のクリエイターがローカルLLM(オープンウェイト)の精度検証を行うための基盤が必要になりました。
ツール導入前の課題
それまでクリエイターは、ローカルLLMを個人の社用PCで動かして検証していました。この方法には3つの課題がありました。
- 性能不足で大型モデルが動かない。社用PCでは小型モデルを動かすのが精一杯で、本命である12B〜31Bクラスのモデルは検証できない
- 環境構築が属人化していた。vLLMなどの推論環境のセットアップはクリエイターには敷居が高く、検証のたびにエンジニアの手を借りる必要があった
- かといって専用GPUマシンはコストが見合わない。高性能なGPUマシンを人数分や常設サーバーとして用意しても、「検証したいときだけ使う」という断続的な用途では遊休時間の費用が無駄になる
どのような状態を目指していたか
一言でいえば、ゲームに採用するモデルを決めるための「意思決定基盤」です。
サイズや量子化の異なるモデルを同一条件で比較できて初めて、「このユースケースにはこのモデル」という判断ができます。
具体的な要件は次の通りです。
- サイズ・量子化の異なる複数モデル(Gemma 4シリーズ5種)を、同一条件で切り替えながら比較できる
- クリエイターが環境構築なしに OpenAI互換APIを叩くだけで検証を始められる
- 検証したいときだけ使うので、使っていない時間の費用をゼロにしたい
- RTX PRO 6000 Blackwellはリージョンのクォータが限られている(当時5基)ため、1モデル = 1サービスではなく、VRAM(96GB)が許す範囲で1GPUに複数モデルを相乗りさせたい
推論エンジンにはvLLMを採用し、複数のvLLMプロセスへのルーティングには llama-swapをルーターとして使う構成(group 機能 + 全モデル常駐)にしました。
比較検討したサービス
- GKE + GPUノードプール
- GCE(GPUインスタンスを運用)
- RunPodのGPU Pod
比較した軸
- scale-to-zeroが可能か(アイドル時のGPU課金ゼロ)
- クラスタやノードの運用が不要か
選定理由
Cloud Runは、クラスタやノードの運用なしにGPUのscale-to-zeroを実現できる点で他の選択肢にない強みがあり、「従量課金」と「運用レス」を両立できることが決め手でした。
特に、2026年4月にRTX PRO 6000(VRAM 96GB)が一般提供になり、従来はVRAMが多いGPUを利用しようとするとGCEで費用の高いA100 80GBを借りなければならなかった点が解消されたところも大きかったです。
導入の成果
当初のscale-to-zeroのLLM推論基盤の構築は完了し、コールドスタートのボトルネックを定量的に特定・改善する知見が得られました。
検証の全体像
すべての計測は同一スクリプト(wake時刻からの経過秒をプロセス内で記録)で行い、各条件2回以上の再現を確認しています。
対象は小型グループ(Gemma 4 12B / E4B / E2B、重み合計 ~50GB)と大型グループ(同 31B NVFP4 / 26B A4B NVFP4、~52GB)の 2 サービスです。
第1章: ボトルネックはネットワーク帯域だった
モデルはGCSに置きCloud Storage FUSEでマウントしていましたが、ロードが実測 ~60MB/sしか出ませんでした。gcsfuseの並列ダウンロードを設定するとむしろ悪化しました。
GPUなしのCloud Runジョブで素の帯域を測ったところ、並列でも52MB/s。つまりgcsfuseではなくインスタンスのegress経路の帯域キャップが原因でした(仕様上もデフォルト経路は送受信合計600Mbps ≒ 75MB/sが上限)。
対策は Direct VPC egress + Private Google Access。
これで素の帯域が並列 381MB/s(7.3 倍)になり、gcsfuse の並列ダウンロードと組み合わせて初めて効果が出ました。
| 構成 | GCS 読み込み帯域 | 16GB モデルの完全コールド |
|---|---|---|
| デフォルト egress | 52〜60MB/s | 4分34秒〜7分47秒 |
| Direct VPC + 並列DL | 381MB/s | 2分53秒 |
第2章: 「モデルをイメージに焼き込めば速い」は10GB超では逆
Google公式ガイドにも記載がありますが、実測で確認しました。
24GBのモデルをコンテナイメージに焼き込むと、image streamingの遅延読み込みが FUSE並列DLより遅く、ロードが 200 秒 → 508 秒に悪化。
10GB 超のモデルでは選ばない方がよいです。
第3章: vLLM の初期化コストは compile キャッシュの永続化で削れる、ただし置き方が重要
IOを解決すると、次はvLLMの初期化(torch.compile / CUDAグラフ)が支配的になります。
compileキャッシュをGCSに永続化して2回目以降のwakeで再利用する作戦は有効でしたが、キャッシュの実体は約2,800個・465MBの小ファイル群(小型グループの場合)で、GCS FUSE経由だとファイルごとのメタデータ往復が数千回発生し、効果が相殺されました。
そこでキャッシュをtarアーカイブ1本にして起動時にインメモリへ展開する方式に変更。86MB(圧縮後)のtarballのダウンロード + 展開は約2秒で、全モデルが最速を更新しました。
| モデル | キャッシュなし | FUSE キャッシュ | tarball 方式 |
|---|---|---|---|
| 12B | 200s | 149s | 133s |
| E4B | 494s | 452s | 370s |
| E2B | 707s | 679s | 545s |
第4章: GCS Rapid Cacheは今回の構成では効果がなかった
GCSのRapid Cacheを3ゾーンに張って検証しましたが、A/B比較で有意差なしという結果でした。
※ Cloud Next 2026でRapid Cacheを使用するとモデル読み込みを高速化できるとあったのに対し、結果が伴わなかったので、この検証に関してはより細かく確認しています。
Rapid Cacheの有無だけが違う同一構成の2サービスを同じ時間帯に交互に測定する A/B 形式(小型グループは各3回、大型グループは各2回以上)で行い、ingest-on-write を有効にして初回読みからキャッシュヒットする状態で計測しています。
| モデル | A: Rapid Cache あり(平均) | B: なし(平均) | 差 |
|---|---|---|---|
| 12B | 159s | 146s | +8% |
| E4B | 432s | 418s | +3% |
| E2B | 652s | 657s | ±0% |
| 31B NVFP4 | 255s | 239s | +7% |
| 26B A4B NVFP4 | 1493s | 1598s | −7% |
各回の測定値は次の通りです(A→B→A→B… の順に交互測定)。
小型グループ:
| 測定 | 12B | E4B | E2B |
|---|---|---|---|
| A1(Rapid あり) | 168s | 442s | 668s |
| B1(なし) | 148s | 416s | 700s |
| A2(Rapid あり) | 160s | 438s | 660s |
| B2(なし) | 148s | 426s | 648s |
| A3(Rapid あり) | 148s | 416s | 628s |
| B3(なし) | 143s | 411s | 622s |
大型グループ:
| 測定 | 31B | 26B |
|---|---|---|
| A 先行1(Rapid あり)※ | 257s | 1839s |
| A 先行2(Rapid あり)※ | 329s | 1721s |
| B1(なし) | 235s | 1710s |
| A1(Rapid あり) | 257s | 1602s |
| B2(なし) | 242s | 1485s |
| A2(Rapid あり) | 252s | 1383s |
※ 大型の「先行」2回は対照グループを立てる前の測定です。平均表は条件を揃えるため同日交互測定分(A1/A2 と B1/B2)で算出しています。
いずれの差もグループ内のばらつき(±3〜15%)の範囲内で、速くなる方向の再現性はありませんでした。
生データを見ると分かる通り、E2B は Bグループだけでも 622〜700s、26B は 1383〜1839s の幅があり、グループ間の差(数%)はグループ内の揺らぎに埋もれています。
余談ですが、当初の単発測定では「E2B が −9%」に見えており、試行回数を増やした A/B 比較でノイズだと判定できました。
この過程で「26B のロード時間は日によって 1.2〜1.7 倍振れる」ことも分かり、測定する際に留意するのが良さそうです。
GCSからのモデルダウンロードが速くならなかった原因は経路を分解すると明快でした。
ダウンロードの速さは経路全体の一番細い場所で決まります。
GCSサーバー → ネットワーク経路 → Cloud Runインスタンス(gcsfuse → vLLM)
(送り出し側) (通り道) (受け取り側)
| 区間 | 実測・仕様 |
|---|---|
| GCSサーバー(Rapid Cacheなし) | 381MB/s を余裕で供給できていた |
| インスタンスの取り込み(Direct VPC) | ~381MB/s で頭打ち ← ボトルネック |
| Rapid Cache適用後のGCSサーバー | 最大 2.5TB/s(集約)— 送り出し側の天井だけが上がる |
つまり Rapid Cacheを入れる前から、GCS はインスタンスが受け取れる上限まで送れていたのです。さらに、モデル1個のロード時間の約半分はvLLM の初期化(GPU/CPU処理)で、そもそもダウンロードとは無関係です。
なお Rapid Cache が有効なのは、多数のインスタンスが同時に同じモデルを読むケースです。私たちのような「1 インスタンス × scale-to-zero」構成では出番がありませんでしたが、フリートを一斉にコールドスタートさせるようなスケールアウト構成なら有効な可能性があると思います。
この検証によりボトルネックはGCSサーバー側ではなくインスタンス側(VPC 実効帯域・gcsfuse・vLLM 初期化)にあると考えました。
第5章: 並列ロードの意外な結末 — 26B の「遅さの正体」
複数モデルの起動時プリロードは逐次実行のため、後ろのモデルほどwakeから使えるまでが遅くなります。並列化を試みたところ、vLLMの次の2つのメモリチェックに阻まれました。
- KV キャッシュサイズの計算が「空き VRAM の実測」ベースで、並列ロード中は他プロセスの確保が進むため破綻する(
Available KV cache memory: -40.43 GiB)→--kv-cache-memory-bytes(絶対値指定)で解決 - それでも起動直後の早期チェック(
空き VRAM ≥ gpu-memory-utilization × 全体、未指定だとデフォルト 0.92)が残る → 小さい--gpu-memory-utilizationを併記して解決
修正後の結果はモデル特性で明暗が分かれました。
小型グループでは、ロードが共有リソース(帯域・CPU・gcsfuse キャッシュ)で飽和しているため、並列にすると個々のモデルのロードは大きく遅くなります。
それでも、モデルの初期化中に別のモデルがIOを進めることにより、全モデルreadyは679→499秒と26%高速化されました。
一方、「主力の12Bだけ先行ロードし、ready後に残り2つを並列」という段階並列は、後段の2モデルの単体ロード時間がそれぞれ約1.8倍(E4B 303→539秒、E2B 227→417秒)に伸び、全モデルreadyは逐次と変わりませんでした。
表の値はいずれもwakeからそのモデルが使えるようになるまでの時間です。
| 小型グループ | 12B | E4B | E2B | 全モデルready |
|---|---|---|---|---|
| 逐次(従来) | 149s | 452s | 679s | 679s |
| 完全並列 | 497s | 428s | 198s | 499s |
| 段階並列(12B 先行 → 2 つ並列) | 156s | 695s | 573s | 695s |
なお、第3章のtarball方式キャッシュと完全並列の組み合わせも測定しましたが、全モデルreadyは499〜525秒とFUSE方式キャッシュのとき(494〜499秒)から変わりませんでした。逐次ではキャッシュの置き方で−11〜20%の差が出ていたのに、並列時はその差が消えます。並列ロード時は共有リソースの飽和が支配的で、キャッシュ読み込み方式の違いは競合の中に埋もれるようです。
つまり小型グループの並列化は全体−26%と引き換えに主力の12Bが149→497秒(3倍以上)に悪化するトレードオフです。「全モデルを最速で温めたい」用途なら完全並列が正解になります。
一方、大型グループでは驚きの結果になりました。20分かかっていた 26B(MoE + NVFP4)のロードは、「31BがVRAM を確保した後に逐次で起動していたこと」自体が原因で、VRAMがほぼ空の状態から並列で起動すると 約400秒で上がります(3回のコールド起動すべてで 378 / 388 / 412 秒と再現)。
| 大型グループ | 31B | 26B | 全モデル ready |
|---|---|---|---|
| 従来(逐次・キャッシュなし) | 242s | 1208s | 1208s |
| tarball + 並列 | 490s | 410s | 490s(−59%) |
まとめ: 最終的にどれだけ速くなったか
効果のあった施策をすべて適用した場合(小型 = compileキャッシュ + 完全並列、大型 = tarballキャッシュ + 並列)の、wakeからグループ内の全モデルが使えるようになるまでの時間です。
| グループ | 全モデル ready(初期 ※) | 全モデル ready(最終構成) | 短縮率 |
|---|---|---|---|
| 小型(12B / E4B / E2B) | 707s | 499s | −29% |
| 大型(31B / 26B A4B) | 1208s | 490s | −59% |
※ 初期 = 帯域対策(Direct VPC egress + 並列ダウンロード)は適用済みで、compileキャッシュなし・逐次ロードの構成。ここからのキャッシュ・並列化施策による短縮を示しています。
まとめ: 効いたもの / 効かなかったもの
| 施策 | 結果 |
|---|---|
| Direct VPC egress + gcsfuse 並列DL | ✅ 帯域 7.3 倍、コールド半減(セットで初めて効く) |
| compile キャッシュの tarball 永続化 | ✅ 全モデル短縮(小ファイルのまま FUSE に置くと相殺) |
| 大型グループの並列ロード | ✅ 全体 −59%(VRAM ヘッドルーム依存のモデルに劇的) |
| モデルのイメージ焼き込み | △ モデルサイズ依存(10GB超では効果が出ず) |
| GCS Rapid Cache | △ 構成依存(多数インスタンスの同時読み込み向け) |
| 小型グループの並列ロード | △ 全体 −26% だが主力モデルが 3 倍悪化(利用パターンにより判断) |
導入時の苦労・悩み
一番の苦労は コールドスタート(wake後にモデルが使えるようになるまでの時間) です。scale-to-zeroの代償として、最初のリクエストは「GPUインスタンス起動 + モデルロード + 推論エンジン初期化」を待つことになります。当初は16GBのモデル1つで4.5〜8分、大型グループでは20分かかっており、この改善のために一連のボトルネック検証を行いました(詳細は「導入の成果」)。
その他、遭遇した細かい注意点です。
- L4 GPUノードのドライバがCUDA 12.2相当で、最新のvLLMイメージ(CUDA 13ビルド)が起動しない(2026年7月時点)
- 2026年8月11日のアップデートでドライバはCUDA 13.0相当(580.x.x)に更新されています。ただしドライバの更新タイミングは選べないため、今後も同様のズレは起こり得ます。小型モデルを安価なL4に逃がす場合などは、イメージのCUDAビルドとドライバの対応を確認するのがおすすめです
- デフォルトのegress経路ではGCSからの読み込みが実測 ~60MB/s で頭打ち(仕様上の上限は送受信合計600Mbps ≒ 75MB/s)
導入に向けた社内への説明
上長・チームへの説明
弊社ではCloud Runを既に活用している実績があったため、特に詳細な説明を行うことはなかったですが、要点は次の通りです。
- Cloud Run IAMによる認証で利用者を制限できる
- scale-to-zeroで費用を抑えて検証ができる
- 社内のPCと異なり検証に必要な台数を柔軟に揃えることができる
活用方法
社内クリエイターがゲームのモック開発におけるローカルLLMの精度検証に利用。
検証したいタイミングだけwakeし、keep-alive(2時間)とあわせて日中は実質ウォームで運用。
よく使う機能
- GPU(NVIDIA RTX PRO 6000)+ scale-to-zero
- アイドル時のGPU課金ゼロ。wakeは数分
- Cloud Storage ボリュームマウント(gcsfuse)
- モデル・キャッシュの置き場。mount optionsで並列ダウンロードを有効化
- Direct VPC egress + Private Google Access
- GCS読み込み帯域のボトルネック解消(60→381MB/s)
- リビジョン管理とTerraform(google_cloud_run_v2_service)
- 検証サービスの複製・破棄が数分で完結し、A/B 計測が容易
ツールの良い点
- クラスタ運用なしでGPUをscale-to-zeroできるのは強力で、検証用途のコスト構造に完璧に合う
- サーバーレスなのにGCS FUSE・Direct VPC・GPUと構成の自由度が高く、Terraform で完結する
- 検証環境の複製が容易で、性能検証のA/Bが高速に回せる
ツールの課題点
- デフォルトegressの帯域上限(送受信合計600Mbps)がLLMのモデルロードには狭く、Direct VPCが実質必須
- GPUノードのドライバ更新のタイミングは選べず、イメージ側のCUDAビルドとバージョンのズレが起こり得る(2026年7月にL4で経験。執筆時点では更新により解消済み)
- 「アイドル後 N 時間維持」のようなスケールダウン制御がなく、自作が必要
ツールを検討されている方へ
LLM推論をCloud Run GPUで行うなら、コールドスタート対策は「計測してから」着手するのをおすすめします。直感と逆の結果になることが多いためです。
- イメージへの焼き込みはモデルサイズによっては速くならない
- サーバー側キャッシュは構成によって効き方が変わる
- 並列化の損得がモデル特性で逆転する
私たちの場合は、1つの検証サービスを立てて同条件で計測する仕組み(測定スクリプト)を最初に作ったことが近道でした。
今後の展望
- Cloud Schedulerによる営業時間前のプリウォームで、利用者がコールドスタートを見る機会をゼロに近づけるなど
株式会社コロプラ / 斎藤拓海
メンバー / 機械学習エンジニア / 従業員規模: 501名〜1,000名 / エンジニア組織: 101名〜300名
よく見られているレビュー
株式会社コロプラ / 斎藤拓海
メンバー / 機械学習エンジニア / 従業員規模: 501名〜1,000名 / エンジニア組織: 101名〜300名
レビューしているツール
目次
- アーキテクチャ
- 導入の背景・解決したかった問題
- 活用方法



