วิธีโฮสต์ LLM ในไซด์คาร์สำหรับฟังก์ชัน Cloud Run

วิธีโฮสต์ LLM ในไซด์คาร์สำหรับฟังก์ชัน Cloud Run

เกี่ยวกับ Codelab นี้

subjectอัปเดตล่าสุดเมื่อ มี.ค. 27, 2025
account_circleเขียนโดย Googler

1 บทนำ

ภาพรวม

ในโค้ดแล็บนี้ คุณจะได้เรียนรู้วิธีโฮสต์โมเดล Gemma3:4b ในไซด์คาร์สำหรับฟังก์ชัน Cloud Run เมื่อมีการอัปโหลดไฟล์ไปยังที่เก็บข้อมูล Cloud Storage ระบบจะทริกเกอร์ฟังก์ชัน Cloud Run ฟังก์ชันนี้จะส่งเนื้อหาของไฟล์ไปยัง Gemma 3 ในไฟล์แนบเพื่อสรุป

สิ่งที่คุณจะได้เรียนรู้

  • วิธีทําการอนุมานโดยใช้ฟังก์ชัน Cloud Run และ LLM ที่โฮสต์ในไซด์คาร์โดยใช้ GPU
  • วิธีใช้การกำหนดค่าการส่งออก VPC โดยตรงสำหรับ GPU ของ Cloud Run เพื่ออัปโหลดและแสดงโมเดลได้เร็วขึ้น
  • วิธีใช้ genkit เพื่อเชื่อมต่อกับโมเดล Ollama ที่โฮสต์

2 ก่อนเริ่มต้น

หากต้องการใช้ฟีเจอร์ GPU คุณต้องขอเพิ่มโควต้าสำหรับภูมิภาคที่รองรับ โควต้าที่จําเป็นคือ nvidia_l4_gpu_allocation_no_zonal_redundancy ซึ่งอยู่ภายใต้ Cloud Run Admin API ลิงก์โดยตรงเพื่อขอโควต้า

3 การตั้งค่าและข้อกําหนด

ตั้งค่าตัวแปรสภาพแวดล้อมที่จะใช้ในโค้ดแล็บนี้

PROJECT_ID=<YOUR_PROJECT_ID>
REGION
=<YOUR_REGION>

AR_REPO
=codelab-crf-sidecar-gpu
FUNCTION_NAME
=crf-sidecar-gpu
BUCKET_GEMMA_NAME
=$PROJECT_ID-codelab-crf-sidecar-gpu-gemma3
BUCKET_DOCS_NAME
=$PROJECT_ID-codelab-crf-sidecar-gpu-docs
SERVICE_ACCOUNT
="crf-sidecar-gpu"
SERVICE_ACCOUNT_ADDRESS
=$SERVICE_ACCOUNT@$PROJECT_ID.iam.gserviceaccount.com
IMAGE_SIDECAR
=$REGION-docker.pkg.dev/$PROJECT_ID/$AR_REPO/ollama-gemma3

สร้างบัญชีบริการโดยเรียกใช้คําสั่งนี้

gcloud iam service-accounts create $SERVICE_ACCOUNT \
 
--display-name="SA for codelab crf sidecar with gpu"

เราจะใช้บัญชีบริการเดียวกันนี้ซึ่งใช้เป็นข้อมูลประจำตัวของฟังก์ชัน Cloud Run เป็นบัญชีบริการสำหรับทริกเกอร์ Eventarc เพื่อเรียกใช้ฟังก์ชัน Cloud Run คุณสร้าง SA อื่นสําหรับ Eventarc ได้หากต้องการ

gcloud projects add-iam-policy-binding $PROJECT_ID \
   
--member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
   
--role=roles/run.invoker

รวมถึงให้สิทธิ์เข้าถึงบัญชีบริการเพื่อรับเหตุการณ์ Eventarc ด้วย

gcloud projects add-iam-policy-binding $PROJECT_ID \
   
--member="serviceAccount:$SERVICE_ACCOUNT_ADDRESS" \
   
--role="roles/eventarc.eventReceiver"

สร้างที่เก็บข้อมูลที่จะโฮสต์โมเดลที่ปรับแต่งแล้ว Codelab นี้ใช้ที่เก็บข้อมูลระดับภูมิภาค คุณใช้ที่เก็บข้อมูลแบบหลายภูมิภาคได้ด้วย

gsutil mb -l $REGION gs://$BUCKET_GEMMA_NAME

จากนั้นให้สิทธิ์ SA เข้าถึงที่เก็บข้อมูล

gcloud storage buckets add-iam-policy-binding gs://$BUCKET_GEMMA_NAME \
--member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role=roles/storage.objectAdmin

ตอนนี้ให้สร้างที่เก็บข้อมูลระดับภูมิภาคที่จะจัดเก็บเอกสารที่ต้องการสรุป คุณใช้ที่เก็บข้อมูลระดับภูมิภาคหลายแห่งได้เช่นกัน ตราบใดที่คุณอัปเดตทริกเกอร์ Eventarc ให้สอดคล้องกัน (แสดงที่ส่วนท้ายของโค้ดแล็บนี้)

gsutil mb -l $REGION gs://$BUCKET_DOCS_NAME

จากนั้นให้สิทธิ์ SA เข้าถึงที่เก็บข้อมูล Gemma 3

gcloud storage buckets add-iam-policy-binding gs://$BUCKET_GEMMA_NAME \
--member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role=roles/storage.objectAdmin

และที่เก็บข้อมูลเอกสาร

gcloud storage buckets add-iam-policy-binding gs://$BUCKET_DOCS_NAME \
--member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role=roles/storage.objectAdmin

สร้างที่เก็บข้อมูลรีจิสทรีอาร์ติแฟกต์สำหรับรูปภาพ Ollama ที่จะใช้ในไซด์คาร์

gcloud artifacts repositories create $AR_REPO \
   
--repository-format=docker \
   
--location=$REGION \
   
--description="codelab for CR function and gpu sidecar" \
   
--project=$PROJECT_ID

4 ดาวน์โหลดโมเดล Gemma 3

ก่อนอื่น คุณต้องดาวน์โหลดโมเดล Gemma 3 4b จาก ollama ซึ่งทำได้โดยการติดตั้ง Ollama แล้วเรียกใช้โมเดล Gemma3:4b ในเครื่อง

curl -fsSL https://ollama.com/install.sh | sh
ollama serve

ตอนนี้ในหน้าต่างเทอร์มินัลแยกต่างหาก ให้เรียกใช้คําสั่งต่อไปนี้เพื่อดึงโมเดลลง หากใช้ Cloud Shell อยู่ คุณสามารถเปิดหน้าต่างเทอร์มินัลเพิ่มเติมได้โดยคลิกไอคอนเครื่องหมายบวกในแถบเมนูด้านขวาบน

ollama run gemma3:4b

เมื่อ Ollama ทำงานอยู่ คุณสามารถถามคำถามบางอย่างกับโมเดลได้ เช่น

"why is the sky blue?"

เมื่อแชทกับ ollama เสร็จแล้ว คุณสามารถออกจากแชทได้โดยเรียกใช้

/bye

จากนั้นในหน้าต่างเทอร์มินัลแรก ให้เรียกใช้คำสั่งต่อไปนี้เพื่อหยุดแสดง Ollama ในเครื่อง

# on Linux / Cloud Shell press Ctrl^C or equivalent for your shell

คุณดูตำแหน่งที่ Ollama ดาวน์โหลดโมเดลตามระบบปฏิบัติการของคุณได้ที่นี่

https://github.com/ollama/ollama/blob/main/docs/faq.md#where-are-models-stored

หากใช้ Cloud Workstations คุณจะเห็นโมเดล Ollama ที่ดาวน์โหลดได้ที่นี่ /home/$USER/.ollama/models

ยืนยันว่าโมเดลของคุณโฮสต์อยู่ที่นี่

ls /home/$USER/.ollama/models

ตอนนี้ให้ย้ายโมเดล gemma3:4b ไปยังที่เก็บข้อมูล GCS

gsutil cp -r /home/$USER/.ollama/models gs://$BUCKET_GEMMA_NAME

5 สร้างฟังก์ชัน Cloud Run

สร้างโฟลเดอร์รูทสำหรับซอร์สโค้ด

mkdir codelab-crf-sidecar-gpu &&
cd codelab
-crf-sidecar-gpu &&
mkdir cr
-function &&
mkdir ollama
-gemma3 &&
cd cr
-function

สร้างโฟลเดอร์ย่อยชื่อ src สร้างไฟล์ชื่อ index.ts ในโฟลเดอร์

mkdir src &&
touch src
/index.ts

อัปเดต index.ts ด้วยโค้ดต่อไปนี้

//import util from 'util';
import { cloudEvent, CloudEvent } from "@google-cloud/functions-framework";
import { StorageObjectData } from "@google/events/cloud/storage/v1/StorageObjectData";
import { Storage } from "@google-cloud/storage";

// Initialize the Cloud Storage client
const storage = new Storage();

import { genkit } from 'genkit';
import { ollama } from 'genkitx-ollama';

const ai = genkit({
   
plugins: [
       
ollama({
           
models: [
               
{
                   
name: 'gemma3:4b',
                   
type: 'generate', // type: 'chat' | 'generate' | undefined
               
},
           
],
           
serverAddress: 'http://127.0.0.1:11434', // default local address
       
}),
   
],
});


// Register a CloudEvent callback with the Functions Framework that will
// be triggered by Cloud Storage.

//functions.cloudEvent('helloGCS', await cloudEvent => {
cloudEvent("gcs-cloudevent", async (cloudevent: CloudEvent<StorageObjectData>) => {
   
console.log("---------------\nProcessing for ", cloudevent.subject, "\n---------------");

   
if (cloudevent.data) {

       
const data = cloudevent.data;

       
if (data && data.bucket && data.name) {
           
const bucketName = cloudevent.data.bucket;
           
const fileName = cloudevent.data.name;
           
const filePath = `${cloudevent.data.bucket}/${cloudevent.data.name}`;

           
console.log(`Attempting to download: ${filePath}`);

           
try {
               
// Get a reference to the bucket
               
const bucket = storage.bucket(bucketName!);

               
// Get a reference to the file
               
const file = bucket.file(fileName!);

               
// Download the file's contents
               
const [content] = await file.download();

               
// 'content' is a Buffer. Convert it to a string.
               
const fileContent = content.toString('utf8');

               
console.log(`Sending file to Gemma 3 for summarization`);
               
const { text } = await ai.generate({
                   
model: 'ollama/gemma3:4b',
                   
prompt: `Summarize the following document in just a few sentences ${fileContent}`,
               
});

               
console.log(text);

           
} catch (error: any) {

               
console.error('An error occurred:', error.message);
           
}
       
} else {
           
console.warn("CloudEvent bucket name is missing!", cloudevent);
       
}
   
} else {
       
console.warn("CloudEvent data is missing!", cloudevent);
   
}
});

ตอนนี้ในไดเรกทอรีรูท crf-sidecar-gpu ให้สร้างไฟล์ชื่อ package.json ที่มีเนื้อหาต่อไปนี้

{
    "main": "lib/index.js",
    "name": "ingress-crf-genkit",
    "version": "1.0.0",
    "scripts": {
        "build": "tsc"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "description": "",
    "dependencies": {
        "@google-cloud/functions-framework": "^3.4.0",
        "@google-cloud/storage": "^7.0.0",
        "genkit": "^1.1.0",
        "genkitx-ollama": "^1.1.0",
        "@google/events": "^5.4.0"
    },
    "devDependencies": {
        "typescript": "^5.5.2"
    }
}

สร้าง tsconfig.json ที่ระดับไดเรกทอรีรูทด้วย โดยมีเนื้อหาดังต่อไปนี้

{
 
"compileOnSave": true,
 
"include": [
   
"src"
 
],
 
"compilerOptions": {
   
"module": "commonjs",
   
"noImplicitReturns": true,
   
"outDir": "lib",
   
"sourceMap": true,
   
"strict": true,
   
"target": "es2017",
   
"skipLibCheck": true,
   
"esModuleInterop": true
 
}
}

6 ทำให้ฟังก์ชันใช้งานได้

ในขั้นตอนนี้ คุณจะทําให้ฟังก์ชัน Cloud Run ใช้งานได้โดยเรียกใช้คําสั่งต่อไปนี้

หมายเหตุ: ควรตั้งค่าอินสแตนซ์สูงสุดเป็นตัวเลขที่น้อยกว่าหรือเท่ากับโควต้า GPU

gcloud beta run deploy $FUNCTION_NAME \
  --region $REGION \
  --function gcs-cloudevent \
  --base-image nodejs22 \
  --source . \
  --no-allow-unauthenticated \
  --max-instances 2 # this should be less than or equal to your GPU quota

7 สร้างไซด์คาร์

ดูข้อมูลเพิ่มเติมเกี่ยวกับการโฮสต์ Ollama ภายในบริการ Cloud Run ได้ที่ https://cloud.google.com/run/docs/tutorials/gpu-gemma-with-ollama

ย้ายไปยังไดเรกทอรีของอุปกรณ์เสริม

cd ../ollama-gemma3

สร้างไฟล์ Dockerfile ที่มีเนื้อหาต่อไปนี้

FROM ollama/ollama:latest

# Listen on all interfaces, port 11434
ENV OLLAMA_HOST 0.0.0.0:11434

# 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:4b
RUN ollama serve & sleep 5 && ollama pull $MODEL

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

สร้างอิมเมจ

gcloud builds submit \
   --tag $REGION-docker.pkg.dev/$PROJECT_ID/$AR_REPO/ollama-gemma3 \
   --machine-type e2-highcpu-32

8 อัปเดตฟังก์ชันด้วยไซด์คาร์

หากต้องการเพิ่มไฟล์ช่วยเหลือลงในบริการ งาน หรือฟังก์ชันที่มีอยู่ คุณสามารถอัปเดตไฟล์ YAML ให้รวมไฟล์ช่วยเหลือได้

เรียกข้อมูล YAML สำหรับฟังก์ชัน Cloud Run ที่คุณเพิ่งทำให้ใช้งานได้โดยเรียกใช้คำสั่งต่อไปนี้

gcloud run services describe $FUNCTION_NAME --format=export > add-sidecar-service.yaml

จากนั้นเพิ่มไฟล์แนบไปกับ CRf โดยอัปเดต YAML ดังนี้

  1. แทรกข้อมูล YAML ต่อไปนี้เหนือบรรทัด runtimeClassName: run.googleapis.com/linux-base-image-update โดยตรง -image ควรสอดคล้องกับรายการคอนเทนเนอร์ขาเข้า -image
    - image: YOUR_IMAGE_SIDECAR:latest
        name
: gemma-sidecar
        env
:
       
- name: OLLAMA_FLASH_ATTENTION
          value
: '1'
        resources
:
          limits
:
            cpu
: 6000m
            nvidia
.com/gpu: '1'
            memory
: 16Gi
        volumeMounts
:
       
- name: gcs-1
          mountPath
: /root/.ollama
        startupProbe
:
          failureThreshold
: 2
          httpGet
:
            path
: /
            port: 11434
          initialDelaySeconds: 60
          periodSeconds: 60
          timeoutSeconds: 60
      nodeSelector:
        run.googleapis.com/
accelerator: nvidia-l4
      volumes
:
       
- csi:
            driver
: gcsfuse.run.googleapis.com
            volumeAttributes
:
              bucketName
: YOUR_BUCKET_GEMMA_NAME
          name
: gcs-1
  1. เรียกใช้คําสั่งต่อไปนี้เพื่ออัปเดตข้อมูลโค้ด YAML โดยใช้ตัวแปรสภาพแวดล้อม
sed -i "s|YOUR_IMAGE_SIDECAR|$IMAGE_SIDECAR|; s|YOUR_BUCKET_GEMMA_NAME|$BUCKET_GEMMA_NAME|" add-sidecar-service.yaml

ไฟล์ YAML ที่เสร็จสมบูรณ์ควรมีลักษณะดังนี้

##############################################
# DO NOT COPY - For illustration purposes only
##############################################

apiVersion
: serving.knative.dev/v1
kind
: Service
metadata
:
  annotations
:    
    run
.googleapis.com/build-base-image: us-central1-docker.pkg.dev/serverless-runtimes/google-22/runtimes/nodejs22
    run
.googleapis.com/build-enable-automatic-updates: 'true'
    run
.googleapis.com/build-function-target: gcs-cloudevent
    run
.googleapis.com/build-id: f0122905-a556-4000-ace4-5c004a9f9ec6
    run
.googleapis.com/build-image-uri:<YOUR_IMAGE_CRF>
    run
.googleapis.com/build-name: <YOUR_BUILD_NAME>
    run
.googleapis.com/build-source-location: <YOUR_SOURCE_LOCATION>
    run
.googleapis.com/ingress: all
    run
.googleapis.com/ingress-status: all
    run
.googleapis.com/urls: '["<YOUR_CLOUD_RUN_FUNCTION_URLS"]'
  labels
:
    cloud
.googleapis.com/location: <YOUR_REGION>
  name
: <YOUR_FUNCTION_NAME>
 
namespace: '392295011265'
spec
:
 
template:
    metadata
:
      annotations
:
        autoscaling
.knative.dev/maxScale: '4'
        run
.googleapis.com/base-images: '{"":"us-central1-docker.pkg.dev/serverless-runtimes/google-22/runtimes/nodejs22"}'
        run
.googleapis.com/client-name: gcloud
        run
.googleapis.com/client-version: 514.0.0
        run
.googleapis.com/startup-cpu-boost: 'true'
      labels
:
        client
.knative.dev/nonce: hzhhrhheyd
        run
.googleapis.com/startupProbeType: Default
    spec
:
      containerConcurrency
: 80
      containers
:
     
- image: <YOUR_FUNCTION_IMAGE>
        ports
:
       
- containerPort: 8080
          name
: http1
        resources
:
          limits
:
            cpu
: 1000m
            memory
: 512Mi
        startupProbe
:
          failureThreshold
: 1
          periodSeconds
: 240
          tcpSocket
:
            port
: 8080
          timeoutSeconds
: 240
     
- image: <YOUR_SIDECAR_IMAGE>:latest
        name
: gemma-sidecar
        env
:
       
- name: OLLAMA_FLASH_ATTENTION
          value
: '1'
        resources
:
          limits
:
            cpu
: 6000m
            nvidia
.com/gpu: '1'
            memory
: 16Gi
        volumeMounts
:
       
- name: gcs-1
          mountPath
: /root/.ollama
        startupProbe
:
          failureThreshold
: 2
          httpGet
:
            path
: /
            port: 11434
          initialDelaySeconds: 60
          periodSeconds: 60
          timeoutSeconds: 60
      nodeSelector:
        run.googleapis.com/
accelerator: nvidia-l4
      volumes
:
       
- csi:
            driver
: gcsfuse.run.googleapis.com
            volumeAttributes
:
              bucketName
: <YOUR_BUCKET_NAME>
          name
: gcs-1
      runtimeClassName
: run.googleapis.com/linux-base-image-update
      serviceAccountName
: <YOUR_SA_ADDRESS>
      timeoutSeconds
: 300
  traffic
:
 
- latestRevision: true
    percent
: 100

##############################################
# DO NOT COPY - For illustration purposes only
##############################################

ตอนนี้อัปเดตฟังก์ชันด้วยไซด์คาร์โดยเรียกใช้คำสั่งต่อไปนี้

gcloud run services replace add-sidecar-service.yaml

สุดท้าย ให้สร้างทริกเกอร์ Eventarc สําหรับฟังก์ชัน คำสั่งนี้จะเพิ่มค่าลงในฟังก์ชันด้วย

หมายเหตุ: หากคุณสร้างที่เก็บข้อมูลระดับภูมิภาคหลายแห่ง คุณจะต้องเปลี่ยนพารามิเตอร์ --location

gcloud eventarc triggers create my-crf-summary-trigger  \
    --location=$REGION \
    --destination-run-service=$FUNCTION_NAME  \
    --destination-run-region=$REGION \
    --event-filters="type=google.cloud.storage.object.v1.finalized" \
    --event-filters="bucket=$BUCKET_DOCS_NAME" \
    --service-account=$SERVICE_ACCOUNT_ADDRESS

9 ทดสอบฟังก์ชัน

อัปโหลดไฟล์ข้อความธรรมดาเพื่อสรุป หากไม่รู้จะสรุปอะไร ขอให้ Gemini อธิบายประวัติสุนัขสั้นๆ 1-2 หน้า จากนั้นอัปโหลดไฟล์ข้อความธรรมดานั้นไปยังที่เก็บข้อมูล $BUCKET_DOCS_NAME สำหรับโมเดล Gemma3:4b เพื่อเขียนสรุปลงในบันทึกของฟังก์ชัน

ในบันทึก คุณจะเห็นข้อมูลคล้ายกับต่อไปนี้

---------------
Processing for objects/dogs.txt
---------------
Attempting to download: <YOUR_PROJECT_ID>-codelab-crf-sidecar-gpu-docs/dogs.txt
Sending file to Gemma 3 for summarization
...
Here's a concise summary of the document "Humanity's Best Friend":
The dog's domestication, beginning roughly 20,000-40,000 years ago, represents a unique, deeply intertwined evolutionary partnership with humans, predating the domestication of any other animal
<
...>
solidifying their long-standing role as humanity's best friend.

10 การแก้ปัญหา

ข้อผิดพลาดที่เกิดจากการพิมพ์ผิดที่คุณอาจพบมีดังนี้

  1. หากได้รับข้อผิดพลาด PORT 8080 is in use ให้ตรวจสอบว่า Dockerfile สําหรับ Sidecar ของ Ollama ใช้พอร์ต 11434 นอกจากนี้ โปรดตรวจสอบว่าคุณใช้รูปภาพ Sidecar ที่ถูกต้องในกรณีที่คุณมีรูปภาพ Ollama หลายรูปในรีโป AR ฟังก์ชัน Cloud Run จะแสดงในพอร์ต 8080 และหากคุณใช้รูปภาพ Ollama อื่นเป็นไซด์คาร์ที่แสดงใน 8080 ด้วย คุณจะได้รับข้อผิดพลาดนี้
  2. หากพบข้อผิดพลาด failed to build: (error ID: 7485c5b6): function.js does not exist ให้ตรวจสอบว่าไฟล์ package.json และ tsconfig.json อยู่ในระดับเดียวกับไดเรกทอรี src
  3. หากได้รับข้อผิดพลาด ERROR: (gcloud.run.services.replace) spec.template.spec.node_selector: Max instances must be set to 4 or fewer in order to set GPU requirements. ให้เปลี่ยน autoscaling.knative.dev/maxScale: '100' เป็น 1 หรือเป็นค่าที่น้อยกว่าหรือเท่ากับโควต้า GPU ในไฟล์ YAML