Cloud Run で OpenAI クライアント互換の LLM サーバーを動かす
今回の記事では、localai というオープンソースの Docker イメージを使い、Google Cloud の Cloud Run 上で OpenAI クライアント互換の LLM サーバーを実行する方法について紹介します。
Cloud Run
Cloud Run は Google Cloud のコンテナサービスです。Terraform + Cloud Runを使ったDifyの環境構築と運用 でも紹介しましたが、Cloud Runを使うことには以下のようなメリットがあります。
- Docker イメージをデプロイするだけ:今回使う localai のイメージを使えば、追加で依存パッケージをインストールしたりイメージを書き出すことなくオープンモデルをデプロイできます。
- 実行時間のみの課金:Cloud Run の「リクエストベース課金」を利用することで、最低限の料金(リクエスト時の CPU 時間)で LLM サーバーを実行することができます。
- GPU が使える:先日一部のリージョンのみのサポートですが Cloud Run で GPU が利用できるようになり、サーバレスでありながら LLM モデルを GPU で動かすことができます。
一方でマシンスペックの制限として、CPU は 8 コア & メモリ 32GB まで、GPU は L4(VRAM 24GB)固定です。量子化モデルを使っても 14B 程度のモデルしか動かないため、あくまで簡単な検証程度の用途で使うのが良いかと思います。
localai
ローカルで簡単にオープンモデルを動かすことを目的に作られたオープンソースライブラリです。バックエンドとしては llama.cpp や vllm といった有名どころを使っており、その繋ぎ込み部分をいい感じにラップしてくれているもののようです。^1
CPU 用、GPU 用それぞれ Docker イメージが公開されており、これをこのまま使うことができます。
インフラ設定
terraform で書くとこんな感じになります。
provider "google" {
project = var.project_id
region = var.region
}
# Cloud Storageバケットの作成
resource "google_storage_bucket" "model_bucket" {
name = var.bucket_name
location = var.region
force_destroy = true
uniform_bucket_level_access = true
}
# Cloud Runサービスの作成
resource "google_cloud_run_v2_service" "localai" {
name = var.service_name
location = var.region
template {
containers {
image = "localai/localai:latest-gpu-nvidia-cuda-12"
resources {
limits = {
cpu = "8"
memory = "32Gi"
"nvidia.com/gpu" = "1"
}
startup_cpu_boost = true
}
env {
name = "LOCALAI_API_KEY"
value = var.localai_api_key
}
volume_mounts {
name = "model-volume"
mount_path = "/build/models"
}
}
node_selector {
accelerator = "nvidia-l4"
}
gpu_zonal_redundancy_disabled = true
scaling {
min_instance_count = 0
max_instance_count = 1
}
volumes {
name = "model-volume"
gcs {
bucket = google_storage_bucket.model_bucket.name
read_only = true
}
}
}
deletion_protection = false
}
# Cloud Runサービスの公開
resource "google_cloud_run_service_iam_member" "public_access" {
service = google_cloud_run_v2_service.localai.name
location = google_cloud_run_v2_service.localai.location
role = "roles/run.invoker"
member = "allUsers"
}
メインの Cloud Run サービスの他、モデルファイルを配置するバケットを用意し、これをマウントしています。
このままだとインターネットから接続可能な設定になっているのでご注意ください。ただし、LOCALAI_API_KEY として API キーをサーバー側で指定することができ、これを使って簡単な認証が行えます。
本格的な認証を入れる場合は、Identity-Aware ProxyやIAMによるアクセス制御で利用できるユーザーを限定することができます。
GPU を初めて利用する場合は、クオータ上限の引き上げ申請が必要になります。こちらの記事等を参考に申請してください。
GPU を使わない場合は、node_selector の指定を外せば CPU で動きます。
モデル
モデルは HuggingFace 等でダウンロードしてきて model_bucket にアップロードします。
今回はTinySwallow-1.5B-Instruct-GGUFとgemma-3-12b-it-GGUF を使いました。VRAM 24GB に載り切るように適度に量子化されたモデルを使ってください。14B・Q5_K_M 量子化 程度のモデルであればギリギリ載るはずです。
curl -L https://huggingface.co/SakanaAI/TinySwallow-1.5B-Instruct-GGUF/resolve/main/tinyswallow-1.5b-instruct-q5_k_m.gguf?download=true -o $(MODELS_DIR)/tinyswallow-1.5b-instruct-q5_k_m.gguf
curl -L https://huggingface.co/unsloth/gemma-3-12b-it-GGUF/resolve/main/gemma-3-12b-it-Q5_K_M.gguf?download=true -o $(MODELS_DIR)/gemma-3-12b-it-Q5_K_M.gguf
gsutil -m cp -r $(MODELS_DIR)/* gs://$(BUCKET_NAME)/
Python コード
あとは OpenAI のモデルを使う場合と同様です。base_url と api_key(デプロイ時に指定したもの)をセットするのがポイントです。
import openai
client = openai.OpenAI(
base_url="https://llm-server-******.asia-northeast1.run.app",
api_key="******",
)
response = client.chat.completions.create(
model="TinySwallow-1.5B-Instruct-GGUF", # モデルのファイル名をそのまま入れる
messages=[
{"role": "user", "content": "こんにちは"}
]
)
print(response.choices[0].message.content)
以上、最小限の作業で簡単に LLM サーバーを立ち上げることができました 👏👏
コードを書かずに最新のオープンモデルをサクッと試せるのはとても良いですね!

「プロダクトの力で、豊かな暮らしをつくる」をミッションに、法人向けに生成AIのPoC、コンサルティング〜開発を支援する事業を展開しております。 エンジニア募集しています。カジュアル面談応募はこちらから: herp.careers/careers/companies/explaza
Discussion