サーバーレス AI: Cloud Run を使用した EmbeddingGemma

1. はじめに

この Codelab では、強力な多言語テキスト エンベディング モデルである EmbeddingGemma を GPU を使用して Cloud Run にデプロイする方法について説明します。次に、このデプロイされたサービスを使用して、セマンティック検索アプリケーションのエンベディングを生成します。

テキストを生成する従来の Large Language Model(LLM)とは異なり、エンベディング モデルはテキストを数値ベクトルに変換します。これらのベクトルは、ユーザーのクエリに最も関連性の高いドキュメントを見つけることができる検索拡張生成(RAG)システムを構築するうえで重要です。

演習内容

  • Ollama を使用して EmbeddingGemma モデルをコンテナ化します。
  • GPU アクセラレーションを使用して、コンテナを Cloud Run にデプロイします。
  • サンプル テキストのエンベディングを生成して、デプロイされたモデルをテストします。
  • デプロイされたサービスを使用して、軽量のセマンティック検索システムを構築します。

必要なもの

  • 課金を有効にした Google Cloud プロジェクト
  • Docker とコマンドラインの基本的な知識があること。

2. 始める前に

プロジェクトの設定

  1. Google アカウントをまだお持ちでない場合は、Google アカウントを作成する必要があります。
    • 仕事用または学校用アカウントではなく、個人アカウントを使用します。職場用アカウントと学校用アカウントには、このラボに必要な API を有効にできない制限が設定されている場合があります。
  2. Google Cloud コンソールにログインします。
  3. Cloud コンソールで課金を有効にします
    • このラボを完了するのにかかる Cloud リソースの費用は 1 米ドル未満です。
    • このラボの最後の手順に沿ってリソースを削除すると、それ以上の料金は発生しません。
    • 新規ユーザーは、300 米ドル分の無料トライアルをご利用いただけます。
  4. 新しいプロジェクトを作成するか、既存のプロジェクトを再利用します。
    • プロジェクトの割り当てに関するエラーが表示された場合は、既存のプロジェクトを再利用するか、既存のプロジェクトを削除して新しいプロジェクトを作成します。

Cloud Shell の起動

Cloud Shell は、必要なツールがプリロードされた Google Cloud で動作するコマンドライン環境です。

  1. Google Cloud コンソールの上部にある [Cloud Shell をアクティブにする] をクリックします。
  2. Cloud Shell に接続したら、認証を確認します。
    gcloud auth list
    
  3. プロジェクトが選択されていることを確認します。
    gcloud config get project
    
  4. 必要に応じて設定します。
    gcloud config set project <YOUR_PROJECT_ID>
    

API を有効にする

次のコマンドを実行して、必要な API をすべて有効にします。

gcloud services enable \
  run.googleapis.com \
  artifactregistry.googleapis.com \
  cloudbuild.googleapis.com

3. モデルをコンテナ化する

EmbeddingGemma をサーバーレスで実行するには、コンテナにパッケージ化する必要があります。LLM を実行するための軽量フレームワークである OllamaDocker を使用します。

Dockerfile を作成する

Cloud Shell で、プロジェクト用の新しいディレクトリを作成してそのディレクトリに移動します。

mkdir embedding-gemma-codelab
cd embedding-gemma-codelab

次の内容で Dockerfile という名前のファイルを作成します。

FROM ollama/ollama:latest

# Listen on all interfaces, port 8080
ENV OLLAMA_HOST=0.0.0.0:8080

# Store model weight files in /models
ENV OLLAMA_MODELS=/models

# Reduce logging verbosity
ENV OLLAMA_DEBUG=false

# Never unload model weights from the GPU
ENV OLLAMA_KEEP_ALIVE=-1

# Store the model weights in the container image
ENV MODEL=embeddinggemma:latest
RUN ollama serve & sleep 5 && ollama pull $MODEL

# Start Ollama
ENTRYPOINT ["ollama", "serve"]

この Dockerfile では次の処理が行われます。

  1. 公式の Ollama ベースイメージから開始します。
  2. ポート 8080(Cloud Run のデフォルト)でリッスンするように Ollama を構成します。
  3. RUN コマンドは ollama サーバーを起動し、ビルドプロセス中に embeddinggemma モデルをダウンロードして、イメージに焼き付けます。
  4. OLLAMA_KEEP_ALIVE=-1 を設定して、モデルが GPU メモリに読み込まれた状態を維持し、後続のリクエストを高速化します。

4. ビルドとデプロイ

Cloud Run ソース デプロイを使用して、コンテナを 1 つのステップでビルドしてデプロイします。このコマンドは、Cloud Build を使用してイメージをビルドし、Artifact Registry に保存して、Cloud Run にデプロイします。

次のコマンドを実行してデプロイします。

gcloud run deploy embedding-gemma \
  --source . \
  --region europe-west1 \
  --concurrency 4 \
  --cpu 8 \
  --set-env-vars OLLAMA_NUM_PARALLEL=4 \
  --gpu 1 \
  --gpu-type nvidia-l4 \
  --max-instances 1 \
  --memory 32Gi \
  --no-allow-unauthenticated \
  --no-cpu-throttling \
  --no-gpu-zonal-redundancy \
  --timeout=600 \
  --labels dev-tutorial=codelab-embedding-gemma

構成について

  • --source . は、現在のディレクトリをビルドのソースとして指定します。
  • --region europe-west1 Cloud Run で GPU をサポートするリージョンを使用します。
  • --concurrency 4 は、環境変数 OLLAMA_NUM_PARALLEL の値と一致するように設定されています。
  • --gpu-type nvidia-l4--gpu 1 は、サービスのすべての Cloud Run インスタンスに 1 つの NVIDIA L4 GPU を割り当てます。
  • --max-instances 1 には、スケーリングするインスタンスの最大数を指定します。プロジェクトの NVIDIA L4 GPU 割り当て以下にする必要があります。
  • --no-allow-unauthenticated は、サービスへの未認証アクセスを制限します。サービスを非公開にすることで、サービス間通信に Cloud Run の組み込み Identity and Access Management(IAM)認証を使用できます。
  • GPU を有効にするには --no-cpu-throttling が必要です。
  • --no-gpu-zonal-redundancy は、ゾーン フェイルオーバーの要件と使用可能な割り当てに応じて、ゾーン冗長オプションを設定します。

リージョンに関する考慮事項

Cloud Run の GPU は特定のリージョンで利用できます。サポートされているリージョンについては、ドキュメントをご覧ください。

デプロイの出力

数分後、デプロイが完了し、次のようなメッセージが表示されます。

Service [embedding-gemma] revision [embedding-gemma-12345-abc] has been deployed and is serving 100 percent of traffic.
Service URL: https://embedding-gemma-123456789012.europe-west1.run.app

5. Deployment をテストする

サービスは --no-allow-unauthenticated でデプロイされているため、公開 URL を単純に curl することはできません。まず、サービスにアクセスしてリクエストで認証トークンを使用する権限を自分に付与する必要があります。

  1. サービスを呼び出す権限をユーザー アカウントに付与します。
    gcloud projects add-iam-policy-binding $GOOGLE_CLOUD_PROJECT \
        --member=user:$(gcloud config get-value account) \
        --role='roles/run.invoker'
    
  2. リクエストで使用するために、Google Cloud 認証情報とプロジェクト番号を環境変数に保存します。
    export PROJECT_NUMBER=$(gcloud projects describe $GOOGLE_CLOUD_PROJECT --format="value(projectNumber)")
    export ID_TOKEN=$(gcloud auth print-identity-token)
    
  3. 次のコマンドを実行して、「Sample text」のエンベディングを生成します。
    curl -X POST "https://embedding-gemma-$PROJECT_NUMBER.europe-west1.run.app/api/embed" \
        -H "Authorization: Bearer $ID_TOKEN" \
        -H "Content-Type: application/json" \
        -d '{
            "model": "embeddinggemma",
            "input": "Sample text"
        }'
    

embedding フィールドにベクトル(長い数値のリスト)を含む JSON レスポンスが表示されます。これで、サーバーレス GPU ベースのエンベディング モデルが機能していることが確認できました。

レスポンスは次のようになります。EmbeddingGemma の Curl 出力

Python クライアント

Python を使用してサービスを操作することもできます。test_client.py という名前のファイルを作成します。

import urllib.request
import urllib.parse
import json
import os

# 1. Setup the URL and Payload
url = f"https://embedding-gemma-{os.environ['PROJECT_NUMBER']}.europe-west1.run.app/api/embed"
payload = {
    "model": "embeddinggemma",
    "input": "Sample text"
}

# 2. Create the Request object
# Note: Providing 'data' automatically makes this a POST request
req = urllib.request.Request(
    url,
    data=json.dumps(payload).encode("utf-8"),
    headers={
        "Authorization": f"Bearer {os.environ['ID_TOKEN']}",
        "Content-Type": "application/json"
    }
)

# 3. Execute and print the response
response = urllib.request.urlopen(req)
result = json.loads(response.read().decode("utf-8"))
print(result)

実行する:

python test_client.py

6. セマンティック検索アプリケーションを構築する

エンベディング サービスが動作するようになったので、簡単なセマンティック検索アプリケーションを構築しましょう。生成されたエンベディングを使用して、特定のクエリに最も関連性の高いドキュメントを見つけます。

依存関係

ベクトル データベースとして chromadb を使用し、クライアント ライブラリとして ollama を使用します。

uv init semantic-search --description "Semantic Search Application"
cd semantic-search
uv add chromadb ollama

検索アプリケーションを作成する

次のコードを含むファイルを semantic_search.py という名前で作成します。

import ollama
import chromadb
import os

# 1. Define our knowledge base
documents = [
    "Poland is a country located in Central Europe.",
    "The capital and largest city of Poland is Warsaw.",
    "Poland's official language is Polish, which is a West Slavic language.",
    "Marie Curie, the pioneering scientist who conducted groundbreaking research on radioactivity, was born in Warsaw, Poland.",
    "Poland is famous for its traditional dish called pierogi, which are filled dumplings.",
    "The Białowieża Forest in Poland is one of the last and largest remaining parts of the immense primeval forest that once stretched across the European Plain.",
]

print("Initializing Vector Database...")
client = chromadb.Client()
collection = client.create_collection(name="docs")

# Configure the client to point to our Cloud Run proxy
ollama_client = ollama.Client(
    host=f"https://embedding-gemma-{os.environ['PROJECT_NUMBER']}.europe-west1.run.app",
    headers={'Authorization': 'Bearer ' + os.environ['ID_TOKEN']}
)

print("Generating embeddings and indexing documents...")
# 2. Store each document in the vector database
for i, d in enumerate(documents):
    # This calls our Cloud Run service to get the embedding
    response = ollama_client.embed(model="embeddinggemma", input=d)
    embeddings = response["embeddings"]
    collection.add(ids=[str(i)], embeddings=embeddings, documents=[d])

print("Indexing complete.\n")

# 3. Perform a Semantic Search
question = "What is Poland's official language?"
print(f"Query: {question}")

# Generate an embedding for the question
response = ollama_client.embed(model="embeddinggemma", input=question)

# Query the database for the most similar document
results = collection.query(
    query_embeddings=[response["embeddings"][0]],
    n_results=1
)

best_match = results["documents"][0][0]
print(f"Best Match Document: {best_match}")

アプリケーションを実行する

このスクリプトを実行します。

uv run semantic_search.py

次のような出力が表示されます。

Initializing Vector Database...
Generating embeddings and indexing documents...
Indexing complete.

Query: What is Poland's official language?
Best Match Document: Poland's official language is Polish, which is a West Slavic language.

このスクリプトは、RAG システムの中核を示しています。サーバーレス EmbeddingGemma サービスを使用してドキュメントとクエリの両方をベクトルに変換し、ユーザーの質問に回答するために必要な正確な情報を見つけることができます。

7. クリーンアップ

Google Cloud アカウントに継続的に課金されないようにするには、この Codelab で作成したリソースを削除します。

Cloud Run サービスを削除する

gcloud run services delete embedding-gemma --region europe-west1 --quiet

コンテナ イメージを削除する

gcloud artifacts docker images delete \
    europe-west1-docker.pkg.dev/${GOOGLE_CLOUD_PROJECT}/cloud-run-source-deploy/embedding-gemma \
    --quiet

8. 完了

おめでとうございます!GPU を使用して Cloud RunEmbeddingGemma を正常にデプロイし、セマンティック検索アプリケーションの強化に使用しました。

これで、テキストの意味を理解する必要がある AI アプリケーションを構築するための、スケーラブルでサーバーレスの基盤ができました。

学習した内容

  • Docker を使用して Ollama モデルをコンテナ化する方法。
  • GPU 対応サービスを Cloud Run にデプロイする方法。
  • デプロイされたモデルをセマンティック検索(RAG)に使用する方法。

次のステップ

  • Gemma ファミリーの他のモデルを確認する。
  • Cloud Run GPU の詳細を確認する。
  • 他の Cloud Run Codelab を確認する。
  • この検索ステップを生成モデルに接続して、完全な RAG パイプラインを構築します。

リファレンス ドキュメント