🤖

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

https://localai.io/

ローカルで簡単にオープンモデルを動かすことを目的に作られたオープンソースライブラリです。バックエンドとしては 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 ProxyIAMによるアクセス制御で利用できるユーザーを限定することができます。

GPU を初めて利用する場合は、クオータ上限の引き上げ申請が必要になります。こちらの記事等を参考に申請してください。
GPU を使わない場合は、node_selector の指定を外せば CPU で動きます。

https://zenn.dev/ksk2/articles/42ac0ab7de14f8

モデル

モデルは HuggingFace 等でダウンロードしてきて model_bucket にアップロードします。
今回はTinySwallow-1.5B-Instruct-GGUFgemma-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 サーバーを立ち上げることができました 👏👏
コードを書かずに最新のオープンモデルをサクッと試せるのはとても良いですね!

株式会社エクスプラザ

Discussion