איך מארחים LLM ב-sidecar של פונקציית Cloud Run

איך מארחים LLM ב-sidecar של פונקציית Cloud Run

מידע על Codelab זה

subjectהעדכון האחרון: מרץ 27, 2025
account_circleנכתב על ידי גוגלר

1.‏ מבוא

ב-codelab הזה תלמדו איך לארח מודל gemma3:4b ב-sidecar של פונקציית Cloud Run. כשקובץ יועלה לקטגוריה של Cloud Storage, הוא יפעיל את הפונקציה של Cloud Run. הפונקציה תשלח את תוכן הקובץ אל Gemma 3 ב-sidecar לצורך סיכום.

  • איך מבצעים הסקת מסקנות באמצעות פונקציית Cloud Run ו-LLM שמתארח ב-sidecar באמצעות מעבדי GPU
  • איך משתמשים בהגדרת תעבורת נתונים יוצאת (egress) ישירה ב-VPC ל-GPU ב-Cloud Run כדי להעלות את המודל ולספק אותו מהר יותר
  • איך משתמשים ב-genkit כדי ליצור ממשק עם מודל ollama המתארח

2.‏ לפני שמתחילים

כדי להשתמש בתכונה של המעבדים הגרפיים, צריך לבקש הגדלה של המכסה באזור נתמך. המכסה הנדרשת היא 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. אם אתם מעדיפים, אתם יכולים ליצור חשבון משתמש אחר ב-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"

יוצרים קטגוריה שתארחת את המודל שעבר שינוי ותכונה. בקודלאב הזה נעשה שימוש בקטגוריה אזורית. אפשר גם להשתמש בקטגוריה בכמה אזורים.

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

ואת הקטגוריה Docs.

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

יצירת מאגר ב-Artifact Registry לקובץ האימג' של Ollama שישמש ב-sidecar

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

אחרי שהמודל יפעל, תוכלו לשאול אותו שאלות, למשל:

"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

יוצרים תיקיית root לקוד המקור.

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 על ידי הפעלת הפקודה הבאה.

הערה: הערך של max instances צריך להיות מספר שקטן ממכסת ה-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.‏ יצירת ה-sidecar

מידע נוסף על אירוח Ollama בשירות Cloud Run זמין בכתובת https://cloud.google.com/run/docs/tutorials/gpu-gemma-with-ollama

עוברים לספרייה של ה-sidecar:

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

עכשיו מוסיפים את ה-sidecar ל-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, צריך לוודא שבקובץ Docker של ה-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., בקובץ ה-YAML משנים את הערך של autoscaling.knative.dev/maxScale: '100' ל-1 או למספר שקטן ממכסת ה-GPU או שווה לה.