LLM in einem Sidecar für eine Cloud Run-Funktion hosten

LLM in einem Sidecar für eine Cloud Run-Funktion hosten

Informationen zu diesem Codelab

subjectZuletzt aktualisiert: März 27, 2025
account_circleVerfasst von einem Google-Mitarbeiter

1. Einführung

In diesem Codelab erfahren Sie, wie Sie ein gemma3:4b-Modell in einem Sidecar für eine Cloud Run-Funktion hosten. Wenn eine Datei in einen Cloud Storage-Bucket hochgeladen wird, wird die Cloud Run-Funktion ausgelöst. Die Funktion sendet den Inhalt der Datei zur Zusammenfassung an Gemma 3 im Sidecar.

  • Inferenzen mit einer Cloud Run-Funktion und einem LLM ausführen, das in einem Sidecar mit GPUs gehostet wird
  • Direct VPC-Ausgangskonfiguration für eine Cloud Run-GPU verwenden, um das Modell schneller hochzuladen und bereitzustellen
  • Genkit zum Interagieren mit Ihrem gehosteten Ollama-Modell verwenden

2. Hinweis

Wenn Sie die GPU-Funktion verwenden möchten, müssen Sie eine Kontingenterhöhung für eine unterstützte Region beantragen. Das erforderliche Kontingent ist „nvidia_l4_gpu_allocation_no_zonal_redundancy“ und wird über die Cloud Run Admin API verwaltet. Hier finden Sie den direkten Link zum Anfordern eines Kontingents.

3. Einrichtung und Anforderungen

Legen Sie Umgebungsvariablen fest, die in diesem Codelab verwendet werden.

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

Erstellen Sie das Dienstkonto mit dem folgenden Befehl:

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

Wir verwenden dasselbe Dienstkonto, das als Identität der Cloud Run-Funktion verwendet wird, als Dienstkonto für den Eventarc-Trigger, um die Cloud Run-Funktion aufzurufen. Sie können auch eine andere Service-Ansicht für Eventarc erstellen.

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

Gewähren Sie dem Dienstkonto außerdem Zugriff auf das Empfangen von Eventarc-Ereignissen.

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

Erstellen Sie einen Bucket, in dem das optimierte Modell gehostet wird. In diesem Codelab wird ein regionaler Bucket verwendet. Sie können auch einen multiregionalen Bucket verwenden.

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

Gewähren Sie der SA dann Zugriff auf den Bucket.

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

Erstellen Sie nun einen regionalen Bucket, in dem die Dokumente gespeichert werden, die Sie zusammenfassen möchten. Sie können auch einen multiregionalen Bucket verwenden, sofern Sie den Eventarc-Trigger entsprechend aktualisieren (siehe Ende dieses Codelabs).

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

Gewähren Sie der SA dann Zugriff auf den Gemma 3-Bucket.

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

und den Docs-Bucket.

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

Erstellen Sie ein Artifact Registry-Repository für das Ollama-Image, das im Sidecar verwendet wird.

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-Modell herunterladen

Laden Sie zuerst das Gemma 3 4b-Modell von ollama herunter. Dazu müssen Sie ollama installieren und das Modell „gemma3:4b“ lokal ausführen.

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

Führen Sie nun in einem separaten Terminalfenster den folgenden Befehl aus, um das Modell herunterzuladen. Wenn Sie Cloud Shell verwenden, können Sie ein zusätzliches Terminalfenster öffnen, indem Sie oben rechts in der Menüleiste auf das Pluszeichen klicken.

ollama run gemma3:4b

Sobald ollama läuft, können Sie dem Modell Fragen stellen, z.B.:

"why is the sky blue?"

Wenn Sie mit dem Chatten mit ollama fertig sind, können Sie den Chat beenden, indem Sie

/bye

Führen Sie dann im ersten Terminalfenster den folgenden Befehl aus, um das lokale Bereitstellen von ollama zu beenden:

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

Hier finden Sie Informationen dazu, wo Ollama die Modelle je nach Betriebssystem herunterlädt.

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

Wenn Sie Cloud Workstations verwenden, finden Sie die heruntergeladenen Ollama-Modelle hier: /home/$USER/.ollama/models

Prüfen Sie, ob Ihre Modelle hier gehostet werden:

ls /home/$USER/.ollama/models

Verschieben Sie das Modell „gemma3:4b“ jetzt in Ihren GCS-Bucket.

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

5. Cloud Run-Funktion erstellen

Erstellen Sie einen Stammordner für Ihren Quellcode.

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

Erstellen Sie einen Unterordner mit dem Namen „src“. Erstellen Sie im Ordner eine Datei mit dem Namen „index.ts“.

mkdir src &&
touch src
/index.ts

Aktualisieren Sie index.ts mit dem folgenden Code:

//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);
   
}
});

Erstellen Sie nun im Stammverzeichnis crf-sidecar-gpu eine Datei namens package.json mit folgendem Inhalt:

{
    "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"
    }
}

Erstellen Sie auch auf Stammverzeichnisebene eine tsconfig.json mit folgendem Inhalt:

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

6. Funktion implementieren

In diesem Schritt stellen Sie die Cloud Run-Funktion bereit, indem Sie den folgenden Befehl ausführen.

Hinweis: Die maximale Anzahl von Instanzen sollte kleiner oder gleich Ihrem GPU-Kontingent sein.

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 erstellen

Weitere Informationen zum Hosten von Ollama in einem Cloud Run-Dienst finden Sie unter https://cloud.google.com/run/docs/tutorials/gpu-gemma-with-ollama.

Wechseln Sie in das Verzeichnis für Ihren Sidecar:

cd ../ollama-gemma3

Erstellen Sie eine Dockerfile-Datei mit folgendem Inhalt:

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"]

Image erstellen

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

8. Funktion mit Sidecar aktualisieren

Wenn Sie einem vorhandenen Dienst, Job oder einer vorhandenen Funktion einen Sidecar hinzufügen möchten, können Sie die YAML-Datei aktualisieren, damit sie den Sidecar enthält.

Rufen Sie die YAML-Datei für die gerade bereitgestellte Cloud Run-Funktion ab:

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

Fügen Sie nun den Sidecar dem CRf hinzu, indem Sie die YAML-Datei so aktualisieren:

  1. Fügen Sie das folgende YAML-Fragment direkt über die Zeile runtimeClassName: run.googleapis.com/linux-base-image-update ein. Der -image sollte mit dem Ingress-Containerelement -image übereinstimmen.
    - 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. Führen Sie den folgenden Befehl aus, um das YAML-Fragment mit Ihren Umgebungsvariablen zu aktualisieren:
sed -i "s|YOUR_IMAGE_SIDECAR|$IMAGE_SIDECAR|; s|YOUR_BUCKET_GEMMA_NAME|$BUCKET_GEMMA_NAME|" add-sidecar-service.yaml

Ihre fertige YAML-Datei sollte in etwa so aussehen:

##############################################
# 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
##############################################

Aktualisieren Sie die Funktion jetzt mit dem Sidecar, indem Sie den folgenden Befehl ausführen.

gcloud run services replace add-sidecar-service.yaml

Erstellen Sie abschließend den Eventarc-Trigger für die Funktion. Mit diesem Befehl wird er auch der Funktion hinzugefügt.

Hinweis: Wenn Sie einen Bucket mit mehreren Regionen erstellt haben, müssen Sie den Parameter --location ändern.

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. Funktion testen

Laden Sie eine Nur-Textdatei für die Zusammenfassung hoch. Sie wissen nicht, was Sie zusammenfassen sollen? Bitten Sie Gemini um eine kurze Beschreibung der Geschichte von Hunden auf einer bis zwei Seiten. Laden Sie diese Textdatei dann in Ihren $BUCKET_DOCS_NAME-Bucket für das Gemma3:4b-Modell hoch, um eine Zusammenfassung in die Funktionsprotokolle zu schreiben.

In den Protokollen wird in etwa Folgendes angezeigt:

---------------
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. Fehlerbehebung

Hier sind einige Fehler, die bei Tippfehlern auftreten können:

  1. Wenn Sie die Fehlermeldung PORT 8080 is in use erhalten, prüfen Sie, ob im Dockerfile für Ihr Ollama-Sidecar der Port 11434 verwendet wird. Achten Sie außerdem darauf, dass Sie das richtige Sidecar-Image verwenden, falls Sie mehrere Ollama-Images in Ihrem AR-Repository haben. Die Cloud Run-Funktion wird über Port 8080 bereitgestellt. Wenn Sie ein anderes Ollama-Image als Sidecar verwendet haben, das ebenfalls über 8080 bereitgestellt wird, tritt dieser Fehler auf.
  2. Wenn der Fehler failed to build: (error ID: 7485c5b6): function.js does not exist angezeigt wird, prüfen Sie, ob sich die Dateien „package.json“ und „tsconfig.json“ auf derselben Ebene wie das Verzeichnis „src“ befinden.
  3. Wenn Sie den Fehler 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. erhalten, ändern Sie in Ihrer YAML-Datei autoscaling.knative.dev/maxScale: '100' in „1“ oder in einen Wert, der kleiner oder gleich Ihrem GPU-Kontingent ist.