Informazioni su questo codelab
1. Introduzione
Panoramica
In questo codelab, imparerai a ospitare un modello gemma3:4b in un sidecar per una funzione Cloud Run. Quando un file viene caricato in un bucket Cloud Storage, viene attivata la funzione Cloud Run. La funzione invierà i contenuti del file a Gemma 3 nel sidecar per il riassunto.
Cosa imparerai a fare
- Come eseguire l'inferenza utilizzando una funzione Cloud Run e un modello LLM ospitato in un sidecar con GPU
- Come utilizzare la configurazione di uscita VPC diretto per una GPU Cloud Run per caricare e pubblicare il modello più velocemente
- Come utilizzare genkit per interagire con il modello Ollama ospitato
2. Prima di iniziare
Per utilizzare la funzionalità GPU, devi richiedere un aumento della quota per una regione supportata. La quota necessaria è nvidia_l4_gpu_allocation_no_zonal_redundancy, che si trova nell'API Cloud Run Admin. Ecco il link diretto per richiedere la quota.
3. Configurazione e requisiti
Imposta le variabili di ambiente che verranno utilizzate durante questo codelab.
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
Crea l'account di servizio eseguendo questo comando:
gcloud iam service-accounts create $SERVICE_ACCOUNT \
--display-name="SA for codelab crf sidecar with gpu"
Utilizzeremo lo stesso account di servizio utilizzato come identità della funzione Cloud Run come account di servizio per l'attivatore eventarc per richiamare la funzione Cloud Run. Se preferisci, puoi creare un'altra entità di amministratore per Eventarc.
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role=roles/run.invoker
Concedi inoltre all'account di servizio l'accesso per ricevere gli eventi Eventarc.
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_ADDRESS" \
--role="roles/eventarc.eventReceiver"
Crea un bucket che ospiterà il modello ottimizzato. Questo codelab utilizza un bucket regionale. Puoi anche utilizzare un bucket multiregionale.
gsutil mb -l $REGION gs://$BUCKET_GEMMA_NAME
Quindi concedi all'amministratore delegato l'accesso al bucket.
gcloud storage buckets add-iam-policy-binding gs://$BUCKET_GEMMA_NAME \
--member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role=roles/storage.objectAdmin
Ora crea un bucket regionale in cui verranno archiviati i documenti che vuoi riepilogare. Puoi utilizzare anche un bucket multiregionale, a condizione che tu aggiorni di conseguenza l'attivatore Eventarc (mostrato alla fine di questo codelab).
gsutil mb -l $REGION gs://$BUCKET_DOCS_NAME
Quindi concedi all'amministratore delegato l'accesso al bucket Gemma 3.
gcloud storage buckets add-iam-policy-binding gs://$BUCKET_GEMMA_NAME \
--member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role=roles/storage.objectAdmin
e il bucket Documenti.
gcloud storage buckets add-iam-policy-binding gs://$BUCKET_DOCS_NAME \
--member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role=roles/storage.objectAdmin
Crea un repository Artifact Registry per l'immagine Ollama che verrà utilizzata nel sidecar
gcloud artifacts repositories create $AR_REPO \
--repository-format=docker \
--location=$REGION \
--description="codelab for CR function and gpu sidecar" \
--project=$PROJECT_ID
4. Scarica il modello Gemma 3
Per prima cosa, devi scaricare il modello Gemma 3 4b da ollama. Per farlo, installa ollama ed esegui il modello gemma3:4b localmente.
curl -fsSL https://ollama.com/install.sh | sh
ollama serve
Ora, in una finestra del terminale separata, esegui il seguente comando per scaricare il modello. Se utilizzi Cloud Shell, puoi aprire un'altra finestra del terminale facendo clic sull'icona Più nella barra dei menu in alto a destra.
ollama run gemma3:4b
Una volta avviato ollama, non esitare a porre alcune domande al modello, ad esempio
"why is the sky blue?"
Al termine della chat con ollama, puoi uscire eseguendo
/bye
Quindi, nella prima finestra del terminale, esegui il seguente comando per interrompere il servizio ollama in locale
# on Linux / Cloud Shell press Ctrl^C or equivalent for your shell
Qui puoi trovare dove Ollama scarica i modelli a seconda del sistema operativo.
https://github.com/ollama/ollama/blob/main/docs/faq.md#where-are-models-stored
Se utilizzi Cloud Workstations, puoi trovare i modelli ollama scaricati qui /home/$USER/.ollama/models
Verifica che i tuoi modelli siano ospitati qui:
ls /home/$USER/.ollama/models
Ora sposta il modello gemma3:4b nel tuo bucket GCS
gsutil cp -r /home/$USER/.ollama/models gs://$BUCKET_GEMMA_NAME
5. Crea la funzione Cloud Run
Crea una cartella principale per il codice sorgente.
mkdir codelab-crf-sidecar-gpu &&
cd codelab-crf-sidecar-gpu &&
mkdir cr-function &&
mkdir ollama-gemma3 &&
cd cr-function
Crea una sottocartella denominata src. All'interno della cartella, crea un file denominato index.ts
mkdir src &&
touch src/index.ts
Aggiorna index.ts con il seguente codice:
//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);
}
});
Ora, nella directory principale crf-sidecar-gpu
, crea un file denominato package.json
con i seguenti contenuti:
{
"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"
}
}
Crea un tsconfig.json
anche a livello di directory principale con i seguenti contenuti:
{
"compileOnSave": true,
"include": [
"src"
],
"compilerOptions": {
"module": "commonjs",
"noImplicitReturns": true,
"outDir": "lib",
"sourceMap": true,
"strict": true,
"target": "es2017",
"skipLibCheck": true,
"esModuleInterop": true
}
}
6. esegui il deployment della funzione
In questo passaggio, eseguirai il deployment della funzione Cloud Run eseguendo il seguente comando.
Nota: il numero massimo di istanze deve essere impostato su un numero inferiore o uguale alla quota di 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. Crea il sidecar
Per scoprire di più sull'hosting di Ollama in un servizio Cloud Run, visita la pagina https://cloud.google.com/run/docs/tutorials/gpu-gemma-with-ollama
Vai alla directory del sidecar:
cd ../ollama-gemma3
Crea un file Dockerfile
con i seguenti contenuti:
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"]
Crea l'immagine
gcloud builds submit \
--tag $REGION-docker.pkg.dev/$PROJECT_ID/$AR_REPO/ollama-gemma3 \
--machine-type e2-highcpu-32
8. Aggiorna la funzione con il sidecar
Per aggiungere un contenitore a un servizio, a un job o a una funzione esistenti, puoi aggiornare il file YAML in modo che contenga il contenitore.
Recupera il file YAML per la funzione Cloud Run appena di cui hai eseguito il deployment eseguendo:
gcloud run services describe $FUNCTION_NAME --format=export > add-sidecar-service.yaml
Ora aggiungi il sidecar al file CRf aggiornando il file YAML come segue:
- Inserisci il seguente frammento YAML direttamente sopra la riga
runtimeClassName: run.googleapis.com/linux-base-image-update
.-image
deve essere allineato all'elemento contenitore di importazione-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
- Esegui il seguente comando per aggiornare il frammento YAML con le variabili di ambiente:
sed -i "s|YOUR_IMAGE_SIDECAR|$IMAGE_SIDECAR|; s|YOUR_BUCKET_GEMMA_NAME|$BUCKET_GEMMA_NAME|" add-sidecar-service.yaml
Il file YAML completo dovrebbe avere il seguente aspetto:
##############################################
# 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
##############################################
Ora aggiorna la funzione con il sidecar eseguendo il seguente comando.
gcloud run services replace add-sidecar-service.yaml
Infine, crea l'attivatore Eventarc per la funzione. Questo comando lo aggiunge anche alla funzione.
Nota: se hai creato un bucket su più regioni, devi modificare il parametro --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. Testa la funzione
Carica un file di testo normale per il riassunto. Non sai cosa riassumere? Chiedi a Gemini una breve descrizione di 1-2 pagine sulla storia dei cani. Carica poi il file di testo nel bucket $BUCKET_DOCS_NAME
per il modello Gemma3:4b per scrivere un riepilogo nei log della funzione.
Nei log viene visualizzato un messaggio simile al seguente:
---------------
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. Risoluzione dei problemi
Ecco alcuni errori ortografici che potresti riscontrare:
- Se ricevi un errore
PORT 8080 is in use
, assicurati che il Dockerfile per il sidecar Ollama utilizzi la porta 11434. Inoltre, assicurati di utilizzare l'immagine sidecar corretta nel caso in cui tu abbia più immagini Ollama nel tuo repository AR. La funzione Cloud Run viene pubblicata sulla porta 8080 e, se hai utilizzato un'immagine Ollama diversa come contenitore aggiuntivo che viene pubblicata anche sulla porta 8080, riscontrerai questo errore. - Se ricevi l'errore
failed to build: (error ID: 7485c5b6): function.js does not exist
, assicurati che i file package.json e tsconfig.json si trovino allo stesso livello della directory src. - Se ricevi l'errore
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.
, nel file YAML modificaautoscaling.knative.dev/maxScale: '100'
in 1 o in un valore inferiore o uguale alla tua quota GPU.