實驗室 3:從原型到正式版 - 將 ADK 代理程式部署至 Cloud Run (使用 GPU)

1. 簡介

總覽

在本實驗室中,您將部署可供正式環境使用的 Agent Development Kit (ADK) 代理程式,並搭配 GPU 加速的 Gemma 後端。本實驗室著重於重要的部署模式:設定啟用 GPU 的 Cloud Run 服務、將模型後端與 ADK 代理程式整合,以及觀察負載下的自動調整行為。

學習內容

在本實驗室中,您將著重於重要的正式環境部署層面:

  1. 將 Gemma 部署至 Cloud Run (含 GPU) - 設定高效能的 Gemma 模型後端
  2. 將 Gemma 部署作業與 ADK 代理程式整合 - 將代理程式連線至 GPU 加速模型
  3. 使用 ADK 網頁介面進行測試:驗證對話式代理程式是否正常運作
  4. 執行負載測試 - 觀察 Cloud Run 執行個體在負載下如何自動調度資源

重點在於生產部署模式,而非廣泛的代理程式開發。

課程內容

  • 將 GPU 加速的 Gemma 模型部署至 Cloud Run,以用於實際工作環境
  • 將外部模型部署項目與 ADK 代理程式整合
  • 設定及測試可供正式環境使用的 AI 代理部署作業
  • 瞭解負載情況下的 Cloud Run 自動調度資源行為
  • 觀察多個 Cloud Run 執行個體在流量尖峰期間的協調方式
  • 進行負載測試,驗證效能和自動調度資源功能

2. 專案設定

  1. 如果沒有 Google 帳戶,請務必建立 Google 帳戶
    • 請改用個人帳戶,而非公司或學校帳戶。公司和學校帳戶可能設有限制,導致您無法啟用本實驗室所需的 API。
  2. 登入 Google Cloud 控制台
  3. 在 Cloud 控制台中啟用帳單
    • 完成本實驗室的 Cloud 資源費用應不到 $1 美元。
    • 您可以按照實驗室結尾的步驟刪除資源,避免產生額外費用。
    • 新使用者可享有價值 $300 美元的免費試用期
  4. 建立新專案,或選擇重複使用現有專案。

3. 開啟 Cloud Shell 編輯器

  1. 按一下這個連結,直接前往 Cloud Shell 編輯器
  2. 如果系統在今天任何時間提示您授權,請點選「授權」繼續操作。按一下即可授權 Cloud Shell
  3. 如果畫面底部未顯示終端機,請開啟終端機:
    • 按一下「查看」
    • 按一下「終端機」在 Cloud Shell 編輯器中開啟新終端機
  4. 在終端機中,使用下列指令設定專案:
    • 格式:
      gcloud config set project [PROJECT_ID]
      
    • 範例:
      gcloud config set project lab-project-id-example
      
    • 如果忘記專案 ID,請按照下列步驟操作:
      • 您可以使用下列指令列出所有專案 ID:
        gcloud projects list | awk '/PROJECT_ID/{print $2}'
        
      在 Cloud Shell 編輯器終端機中設定專案 ID
  5. 您應該會看到下列訊息:
    Updated property [core/project].
    
    如果看到 WARNING 並收到 Do you want to continue (Y/n)? 提示,可能是專案 ID 輸入有誤。按下 n 鍵和 Enter 鍵,然後再次嘗試執行 gcloud config set project 指令。

4. 啟用 API 並設定預設區域

如要部署支援 GPU 的 Cloud Run 服務,請先啟用必要的 Google Cloud API,並設定專案。

  1. 在終端機中啟用 API:
gcloud services enable \
  run.googleapis.com \
  artifactregistry.googleapis.com \
  cloudbuild.googleapis.com \
  aiplatform.googleapis.com

如果系統要求您授權,請點按「授權」繼續操作。按一下即可授權 Cloud Shell

這個指令可能需要幾分鐘才能完成,但最終應該會產生類似這樣的成功訊息:

Operation "operations/acf.p2-73d90d00-47ee-447a-b600" finished successfully.
  1. 設定預設 Cloud Run 地區。
gcloud config set run/region europe-west1

5. 準備 Python 專案

讓我們設定啟動程式碼,其中包含 Gemma 後端和 ADK 代理程式服務的基本結構。

  1. 複製範例存放區:
    cd ~
    git clone https://github.com/amitkmaraj/accelerate-ai-lab3-starter.git
    cd accelerate-ai-lab3-starter
    
  2. 檢查專案結構:
    ls -R
    
    您應該會看到下列起始結構:
    accelerate-ai-lab3-starter/
    ├── README.md                    # Project overview
    ├── ollama-backend/              # Ollama backend (separate deployment)
    │   └── Dockerfile               # Backend container (🚧 to implement)
    └── adk-agent/                   # ADK agent (separate deployment)
        ├── pyproject.toml           # Python dependencies (✅ completed)
        ├── server.py                # FastAPI server (🚧 to implement)
        ├── Dockerfile               # Container config (🚧 to implement)
        ├── load_test.py             # Load testing (🚧 to implement)
        └── production_agent/        # Agent implementation
            ├── __init__.py         # Package init (✅ completed)
            └── agent.py            # Agent logic (🚧 to implement)
    

6. 架構總覽

實作之前,請先瞭解雙服務架構:

實驗室 3:雙服務架構

重要洞察:在負載測試期間,您會發現這兩項服務會獨立擴充:GPU 後端 (瓶頸服務) 會擴充至 1 到 3 個執行個體,以處理推論負載,而 ADK 代理程式則會維持在 1 個執行個體,以處理要求。

7. 將 Gemma 後端部署至 Cloud Run (含 GPU)

實驗室 3:Gemma 服務

首先,請部署 GPU 加速的 Gemma 模型,做為 ADK 代理程式的「大腦」。在需要個別微調模型或獨立調度資源的架構中,解耦並部署 LLM 可能會比較有利。

  1. 前往 Ollama 後端目錄:
    cd ollama-backend
    
  2. 開啟並實作 Ollama Dockerfile:
    cloudshell edit Dockerfile
    
    將 TODO 註解替換為:
    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 gemma3:270m
    RUN ollama serve & sleep 5 && ollama pull $MODEL
    
    # Start Ollama
    ENTRYPOINT ["ollama", "serve"]
    
    🔧 這項功能的作用:
    • 以官方 Ollama 映像檔為基礎
    • OLLAMA_HOST 設為接受來自任何 IP 位址的連線
    • 公開通訊埠 8080
  3. 部署支援 GPU 的 Gemma 後端:
gcloud run deploy ollama-gemma3-270m-gpu \
  --source . \
  --region europe-west1 \
  --concurrency 4 \
  --cpu 8 \
  --set-env-vars OLLAMA_NUM_PARALLEL=4 \
  --gpu 1 \
  --gpu-type nvidia-l4 \
  --max-instances 3 \
  --memory 16Gi \
  --allow-unauthenticated \
  --no-cpu-throttling \
  --no-gpu-zonal-redundancy \
  --timeout 600 \
  --labels dev-tutorial=codelab-agent-gpu

如果您收到「Deploying from source requires an Artifact Registry Docker repository to store built containers. 系統會顯示「將在 [europe-west1] 區域中建立名為 [cloud-run-source-deploy] 的存放區。」訊息,請繼續操作。

⚙️ 重要設定說明:

  • GPU:選擇 NVIDIA L4 是因為其推論工作負載的性價比極高。L4 提供 24 GB 的 GPU 記憶體,並可執行最佳化張量運算,因此非常適合 Gemma 等 2.7 億參數模型
  • 記憶體:16 GB 系統記憶體,用於處理模型載入、CUDA 作業和 Ollama 的記憶體管理
  • CPU:8 核心,可處理最佳 I/O 處理和前置處理工作
  • 並行:每個執行個體 4 個要求,可平衡輸送量與 GPU 記憶體用量
  • 逾時:600 秒,可容納初始模型載入和容器啟動

💰 費用考量:GPU 執行個體比僅含 CPU 的執行個體貴上許多 (每小時約 $2 至 $4 美元,僅含 CPU 的執行個體則為每小時約 $0.10 美元)。--max-instances 1 設定可避免不必要的 GPU 執行個體擴充,有助於控管費用。

  1. 等待部署作業完成,並記下服務網址:
    export OLLAMA_URL=$(gcloud run services describe ollama-gemma3-270m-gpu \
        --region=europe-west1 \
        --format='value(status.url)')
    
    echo "🎉 Gemma backend deployed at: $OLLAMA_URL"
    

8. 導入 ADK 代理程式整合

現在來建立可連線至已部署 Gemma 後端的 ADK 代理程式。

  1. 前往 ADK 代理程式目錄:
    cd ../adk-agent
    
  2. 開啟並實作代理程式設定:
    cloudshell edit production_agent/agent.py
    
    將所有 TODO 註解替換為這個最簡單的實作項目:
    import os
    from pathlib import Path
    
    from dotenv import load_dotenv
    from google.adk.agents import Agent
    from google.adk.models.lite_llm import LiteLlm
    import google.auth
    
    # Load environment variables
    root_dir = Path(__file__).parent.parent
    dotenv_path = root_dir / ".env"
    load_dotenv(dotenv_path=dotenv_path)
    
    # Configure Google Cloud
    try:
        _, project_id = google.auth.default()
        os.environ.setdefault("GOOGLE_CLOUD_PROJECT", project_id)
    except Exception:
        pass
    
    os.environ.setdefault("GOOGLE_CLOUD_LOCATION", "europe-west1")
    
    # Configure model connection
    gemma_model_name = os.getenv("GEMMA_MODEL_NAME", "gemma3:270m")
    
    # Production Gemma Agent - GPU-accelerated conversational assistant
    gemma_agent = Agent(
       model=LiteLlm(model=f"ollama_chat/{gemma_model_name}"),
       name="gemma_agent",
       description="A production-ready conversational assistant powered by GPU-accelerated Gemma.",
       instruction="""You are 'Gem', a friendly, knowledgeable, and enthusiastic zoo tour guide.
       Your main goal is to make a zoo visit more fun and educational for guests by answering their questions.
    
       You can provide general information and interesting facts about different animal species, such as:
       - Their natural habitats and diet. 🌲🍓
       - Typical lifespan and behaviors.
       - Conservation status and unique characteristics.
    
       IMPORTANT: You do NOT have access to any tools. This means you cannot look up real-time, specific information about THIS zoo. You cannot provide:
       - The names or ages of specific animals currently at the zoo.
       - The exact location or enclosure for an animal.
       - The daily schedule for feedings or shows.
    
       Always answer based on your general knowledge about the animal kingdom. Keep your tone cheerful, engaging, and welcoming for visitors of all ages. 🦁✨""",
       tools=[],  # Gemma focuses on conversational capabilities
    )
    
    # Set as root agent
    root_agent = gemma_agent
    
    🔧 這項功能的作用:
    • 透過 LiteLlm 連線至已部署的 Gemma 後端
    • 建立簡單的對話型代理
    • 設定 Google Cloud 整合
  3. 開啟並實作 FastAPI 伺服器:
    cloudshell edit server.py
    
    將所有 TODO 註解替換為:
    import os
    from dotenv import load_dotenv
    from fastapi import FastAPI
    from google.adk.cli.fast_api import get_fast_api_app
    
    # Load environment variables
    load_dotenv()
    
    AGENT_DIR = os.path.dirname(os.path.abspath(__file__))
    app_args = {"agents_dir": AGENT_DIR, "web": True}
    
    # Create FastAPI app with ADK integration
    app: FastAPI = get_fast_api_app(**app_args)
    
    # Update app metadata
    app.title = "Production ADK Agent - Lab 3"
    app.description = "Gemma agent with GPU-accelerated backend"
    app.version = "1.0.0"
    
    @app.get("/health")
    def health_check():
        return {"status": "healthy", "service": "production-adk-agent"}
    
    @app.get("/")
    def root():
        return {
            "service": "Production ADK Agent - Lab 3",
            "description": "GPU-accelerated Gemma agent",
            "docs": "/docs",
            "health": "/health"
        }
    
    if __name__ == "__main__":
        import uvicorn
        uvicorn.run(app, host="0.0.0.0", port=8080, log_level="info")
    
    🔧 這項功能的作用:
    • 建立整合 ADK 的 FastAPI 伺服器
    • 啟用網頁介面以進行測試
    • 提供健康狀態檢查端點
  4. 開啟並實作 Dockerfile:
    cloudshell edit Dockerfile
    
    將所有 TODO 註解替換為:
    FROM python:3.13-slim
    
    # Copy uv from the official image
    COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
    
    # Install system dependencies
    RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
    
    # Set working directory
    WORKDIR /app
    
    # Copy all files
    COPY . .
    
    # Install Python dependencies
    RUN uv sync
    
    # Expose port
    EXPOSE 8080
    
    # Run the application
    CMD ["uv", "run", "uvicorn", "server:app", "--host", "0.0.0.0", "--port", "8080"]
    
    技術選擇說明:
    • uv:新式 Python 套件管理員,速度比 pip 快 10 到 100 倍。這項工具使用全域快取和平行下載功能,大幅縮短容器建構時間
    • Python 3.13-slim:最新 Python 版本,系統依附元件最少,可縮減容器大小和攻擊面
    • 多階段建構:從官方映像檔複製 uv,確保我們取得最新的最佳化二進位檔

9. 設定環境並部署代理程式

接著,我們將設定 ADK 代理程式,連線至已部署的 Gemma 後端,並將其部署為 Cloud Run 服務。包括設定環境變數,以及使用正確的設定部署服務專員。

  1. 設定環境設定:
    cat << EOF > .env
    GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project)
    GOOGLE_CLOUD_LOCATION=europe-west1
    GEMMA_MODEL_NAME=gemma3:270m
    OLLAMA_API_BASE=$OLLAMA_URL
    EOF
    

瞭解 Cloud Run 中的環境變數

環境變數是鍵/值組合,可在執行階段設定應用程式。特別適合用於:

  • API 端點和服務網址 (例如 Ollama 後端)
  • 在不同環境 (開發、預先發布、正式發布) 之間變更的設定
  • 不應硬式編碼的機密資料

部署 ADK 代理程式:

export PROJECT_ID=$(gcloud config get-value project)

gcloud run deploy production-adk-agent \
   --source . \
   --region europe-west1 \
   --allow-unauthenticated \
   --memory 4Gi \
   --cpu 2 \
   --max-instances 1 \
   --concurrency 10 \
   --timeout 300 \
   --set-env-vars GOOGLE_CLOUD_PROJECT=$PROJECT_ID \
   --set-env-vars GOOGLE_CLOUD_LOCATION=europe-west1 \
   --set-env-vars GEMMA_MODEL_NAME=gemma3:270m \
   --set-env-vars OLLAMA_API_BASE=$OLLAMA_URL \
   --labels dev-tutorial=codelab-agent-gpu

⚙️ 主要設定:

  • 自動調度資源:固定為 1 個執行個體 (輕量要求處理)
  • 並行:每個執行個體 10 個要求
  • 記憶體:ADK 代理程式需要 4 GB
  • 環境:連線至 Gemma 後端

🔒 安全性注意事項:為求簡單起見,本實驗室使用 --allow-unauthenticated。在實際工作環境中,請使用下列方式實作適當的驗證機制:

  • 使用服務帳戶進行 Cloud Run 服務對服務驗證
  • 身分與存取權管理 (IAM) 政策
  • 外部存取適用的 API 金鑰或 OAuth
  • 考慮使用 gcloud run services add-iam-policy-binding 控制存取權

取得代理程式服務網址:

export AGENT_URL=$(gcloud run services describe production-adk-agent \
    --region=europe-west1 \
    --format='value(status.url)')

echo "🎉 ADK Agent deployed at: $AGENT_URL"

✅ 根據 Cloud Run 環境變數說明文件,瞭解環境變數最佳做法:

  1. 避免使用保留變數:請勿設定 PORT (Cloud Run 會自動設定此變數),或以 X_GOOGLE_ 開頭的變數
  2. 使用描述性名稱:為變數加上前置字串,避免發生衝突 (例如 GEMMA_MODEL_NAME,而非 MODEL)
  3. 逸出逗號:如果值包含逗號,請使用其他分隔符號:--set-env-vars "^@^KEY1=value1,value2@KEY2=..."
  4. 更新與取代:使用 --update-env-vars 新增/變更特定變數,而不影響其他變數

如何在 Cloud Run 中設定變數

  • 從檔案gcloud run deploy SERVICE_NAME --env-vars-file .env --labels dev-tutorial codelab-adk (從檔案載入多個變數)
  • 多個標記:針對無法以半形逗號分隔的複雜值,請重複 --set-env-vars

10. 使用 ADK 網頁介面進行測試

部署這兩項服務後,即可驗證 ADK 代理程式是否能順利與 GPU 加速的 Gemma 後端通訊,並回應使用者查詢。

  1. 測試健康狀態端點:
    curl $AGENT_URL/health
    
    畫面應顯示如下:
    { "status": "healthy", "service": "production-adk-agent" }
    
  2. 在新的瀏覽器分頁中輸入 production-adk-agent 的網址,即可與代理互動。您應該會看到 ADK 網頁介面。
  3. 使用下列範例對話測試代理程式:
    • 「小熊貓在野外通常吃什麼?」
    • 「可以告訴我雪豹的有趣知識嗎?」
    • 「為什麼箭毒蛙的顏色這麼鮮豔?」
    • 「動物園裡的新生小袋鼠在哪裡?」
    👀 觀察重點:
    • 代理程式會使用您部署的 Gemma 模型回覆。您可以觀察已部署 Gemma 服務的記錄,確認這項資訊。我們會在下一節中執行這項操作
    • 回覆是由 GPU 加速的後端生成
    • 網頁介面提供簡潔的即時通訊體驗

實驗室 3 ADK 測試

11. 導入及執行負載測試

實驗室 3:負載測試

為瞭解生產環境部署作業如何處理實際流量,我們將實作全面的負載測試,在 ADK 代理程式和 GPU 後端服務中觸發自動調整規模。

  1. 開啟並實作負載測試指令碼:
    cloudshell edit load_test.py
    
    將 TODO 註解替換為:
    import random
    import uuid
    from locust import HttpUser, task, between
    
    class ProductionAgentUser(HttpUser):
        """Load test user for the Production ADK Agent."""
    
        wait_time = between(1, 3)  # Faster requests to trigger scaling
    
        def on_start(self):
            """Set up user session when starting."""
            self.user_id = f"user_{uuid.uuid4()}"
            self.session_id = f"session_{uuid.uuid4()}"
    
            # Create session for the Gemma agent using proper ADK API format
            session_data = {"state": {"user_type": "load_test_user"}}
    
            self.client.post(
                f"/apps/production_agent/users/{self.user_id}/sessions/{self.session_id}",
                headers={"Content-Type": "application/json"},
                json=session_data,
            )
    
        @task(4)
        def test_conversations(self):
            """Test conversational capabilities - high frequency to trigger scaling."""
            topics = [
                "What do red pandas typically eat in the wild?",
                "Can you tell me an interesting fact about snow leopards?",
                "Why are poison dart frogs so brightly colored?",
                "Where can I find the new baby kangaroo in the zoo?",
                "What is the name of your oldest gorilla?",
                "What time is the penguin feeding today?"
            ]
    
            # Use proper ADK API format for sending messages
            message_data = {
                "app_name": "production_agent",
                "user_id": self.user_id,
                "session_id": self.session_id,
                "new_message": {
                    "role": "user",
                    "parts": [{
                        "text": random.choice(topics)
                    }]
                }
            }
    
            self.client.post(
                "/run",
                headers={"Content-Type": "application/json"},
                json=message_data,
            )
    
        @task(1)
        def health_check(self):
            """Test the health endpoint."""
            self.client.get("/health")
    
    🔧 這項功能的作用:
    • 建立工作階段:使用適當的 ADK API 格式,透過 POST 傳送至 /apps/production_agent/users/{user_id}/sessions/{session_id}。建立 session_iduser_id 後,即可向代理程式提出要求。
    • 訊息格式:遵循 ADK 規格,包含 app_nameuser_idsession_id 和結構化 new_message 物件
    • 對話端點:使用 /run 端點一次收集所有事件 (建議用於負載測試)
    • 實際負載:建立對話負載,縮短等待時間,以觸發自動調度資源功能
    📚 如要進一步瞭解 ADK API 端點和測試模式,請參閱 ADK 測試指南
  2. 安裝依附元件:
    uv sync
    pip install locust
    
  3. Locust 是以 Python 為基礎的開放原始碼負載測試工具,專為網頁應用程式和其他系統的效能和負載測試而設計。其主要特點是使用標準 Python 程式碼定義測試情境和使用者行為,與依賴圖形使用者介面或特定領域語言的工具相比,具有高度彈性和表現力。我們會使用 Locust 模擬服務的使用者流量,並執行負載測試。
    # Run a load test to trigger autoscaling
    locust -f load_test.py \
       -H $AGENT_URL \
       --headless \
       -t 50s \
       -u 3 \
       -r 1
    
    嘗試變更測試中的參數,並觀察輸出內容。您會發現 ollama-gemma3-270m-gpu 尖峰值為 2 到 3 個執行個體。📊 負載測試參數:
    • 時間長度:50 秒
    • 使用者:3 位並行使用者
    • 產生率:每秒 1 位使用者
    • 目標:在兩項服務上觸發自動調度資源

12. 觀察 AutoScaling 行為

執行負載測試時,您會看到 Cloud Run 的自動調度資源功能運作。您可以在這裡瞭解將 ADK 代理程式與 GPU 後端分開的主要架構優勢。

在負載測試期間,請在控制台中監控這兩項 Cloud Run 服務的擴充情形。

  1. 在 Cloud 控制台中,前往:
    • Cloud Run → production-adk-agent → 指標
    • Cloud Run → ollama-gemma3-270m-gpu → 指標

👀 觀察重點:

🤖 ADK 代理程式服務:

  • 流量增加時,應維持 1 個執行個體
  • 流量高峰期間 CPU 和記憶體用量暴增
  • 有效處理工作階段管理和要求轉送

🎮 Gemma 後端服務 (瓶頸):

  • 根據推論需求,將執行個體數量從 1 個擴充至 3 個
  • 負載增加時,GPU 使用率大幅提升
  • 由於模型推論需要大量 GPU 資源,這項服務會成為瓶頸
  • 由於 GPU 加速,模型推論時間維持不變

💡 重要洞察:

  • GPU 後端是瓶頸,且會更積極地調度資源 (1 到 3 個執行個體)
  • ADK 代理程式保持一致
  • 這兩項服務會根據各自的負載特性獨立調度資源
  • 自動調整資源配置功能可協助您在不同負載條件下維持效能

13. 結語

恭喜!您已成功部署可用於正式環境的 ADK 代理程式,並觀察自動調度資源的行為。這個代理程式採用 GPU 加速的 Gemma 後端。

✅ 達成的成就

  • ✅ 在 Cloud Run 上部署 GPU 加速的 Gemma 模型後端
  • ✅ 建立及部署與 Gemma 後端整合的 ADK 代理
  • ✅ 使用 ADK 網頁介面測試代理程式
  • ✅ 觀察兩個協調式 Cloud Run 服務的自動調度資源行為

💡 本實驗室的重要洞察

  1. 🎮 GPU 加速:NVIDIA L4 GPU 可大幅提升模型推論效能
  2. 🔗 服務協調:兩個 Cloud Run 服務可以順暢地協同運作
  3. 📈 獨立調度資源:每項服務都會根據個別負載特性調度資源
  4. 🚀 適用於正式環境:架構可有效處理實際流量模式

🔄 後續步驟

  • 實驗不同的負載模式,並觀察縮放行為
  • 嘗試不同大小的 Gemma 模型 (視情況調整記憶體和 GPU)
  • 為正式環境部署作業實作監控和快訊功能
  • 探索多區域部署,確保全球可用性

🧹 清理

為避免產生費用,請在完成後刪除資源:

gcloud run services delete production-adk-agent --region=europe-west1
gcloud run services delete ollama-gemma3-270m-gpu --region=europe-west1

📖 資源