À propos de cet atelier de programmation
1. Introduction
Présentation
Dans cet atelier de programmation, vous allez apprendre à héberger un modèle gemma3:4b dans un sidecar pour une fonction Cloud Run. Lorsqu'un fichier est importé dans un bucket Cloud Storage, la fonction Cloud Run est déclenchée. La fonction envoie le contenu du fichier à Gemma 3 dans le sidecar pour la synthèse.
Points abordés
- Effectuer une inférence à l'aide d'une fonction Cloud Run et d'un LLM hébergé dans un sidecar à l'aide de GPU
- Utiliser la configuration de sortie VPC directe pour un GPU Cloud Run afin d'accélérer l'importation et le traitement du modèle
- Utiliser genkit pour interagir avec votre modèle ollama hébergé
2. Avant de commencer
Pour utiliser la fonctionnalité GPU, vous devez demander une augmentation de quota pour une région compatible. Le quota requis est nvidia_l4_gpu_allocation_no_zonal_redundancy, qui se trouve dans l'API Cloud Run Admin. Voici le lien direct pour demander un quota.
3. Préparation
Définissez les variables d'environnement qui seront utilisées tout au long de cet atelier de programmation.
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
Créez le compte de service en exécutant la commande suivante:
gcloud iam service-accounts create $SERVICE_ACCOUNT \
--display-name="SA for codelab crf sidecar with gpu"
Nous utiliserons ce même compte de service utilisé comme identité de la fonction Cloud Run comme compte de service pour le déclencheur Eventarc afin d'appeler la fonction Cloud Run. Si vous le souhaitez, vous pouvez créer une autre SA pour Eventarc.
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role=roles/run.invoker
Accordez également au compte de service l'accès pour recevoir des événements Eventarc.
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_ADDRESS" \
--role="roles/eventarc.eventReceiver"
Créez un bucket qui hébergera votre modèle affiné. Cet atelier de programmation utilise un bucket régional. Vous pouvez également utiliser un bucket multirégional.
gsutil mb -l $REGION gs://$BUCKET_GEMMA_NAME
Accordez ensuite à l'administrateur de service l'accès au bucket.
gcloud storage buckets add-iam-policy-binding gs://$BUCKET_GEMMA_NAME \
--member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role=roles/storage.objectAdmin
Créez maintenant un bucket régional qui stockera les documents que vous souhaitez résumer. Vous pouvez également utiliser un bucket multirégional, à condition de mettre à jour le déclencheur Eventarc en conséquence (voir la fin de cet atelier de programmation).
gsutil mb -l $REGION gs://$BUCKET_DOCS_NAME
Accordez ensuite à l'administrateur de service l'accès au bucket Gemma 3.
gcloud storage buckets add-iam-policy-binding gs://$BUCKET_GEMMA_NAME \
--member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role=roles/storage.objectAdmin
et le bucket "Docs".
gcloud storage buckets add-iam-policy-binding gs://$BUCKET_DOCS_NAME \
--member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role=roles/storage.objectAdmin
Créer un dépôt Artifact Registry pour l'image Ollama qui sera utilisée dans le sidecar
gcloud artifacts repositories create $AR_REPO \
--repository-format=docker \
--location=$REGION \
--description="codelab for CR function and gpu sidecar" \
--project=$PROJECT_ID
4. Télécharger le modèle Gemma 3
Commencez par télécharger le modèle Gemma 3 4b sur ollama. Pour ce faire, installez ollama, puis exécutez le modèle gemma3:4b en local.
curl -fsSL https://ollama.com/install.sh | sh
ollama serve
Dans une autre fenêtre de terminal, exécutez la commande suivante pour télécharger le modèle. Si vous utilisez Cloud Shell, vous pouvez ouvrir une fenêtre de terminal supplémentaire en cliquant sur l'icône Plus dans la barre de menu supérieure droite.
ollama run gemma3:4b
Une fois ollama en cours d'exécution, n'hésitez pas à poser des questions au modèle, par exemple :
"why is the sky blue?"
Une fois la discussion avec ollama terminée, vous pouvez quitter le chat en exécutant
/bye
Ensuite, dans la première fenêtre de terminal, exécutez la commande suivante pour arrêter de diffuser ollama en local :
# on Linux / Cloud Shell press Ctrl^C or equivalent for your shell
Vous trouverez ici l'emplacement où Ollama télécharge les modèles en fonction de votre système d'exploitation.
https://github.com/ollama/ollama/blob/main/docs/faq.md#where-are-models-stored
Si vous utilisez Cloud Workstations, vous pouvez télécharger les modèles ollama ici : /home/$USER/.ollama/models
Vérifiez que vos modèles sont hébergés ici:
ls /home/$USER/.ollama/models
Déplacez maintenant le modèle gemma3:4b vers votre bucket GCS.
gsutil cp -r /home/$USER/.ollama/models gs://$BUCKET_GEMMA_NAME
5. Créer la fonction Cloud Run
Créez un dossier racine pour votre code source.
mkdir codelab-crf-sidecar-gpu &&
cd codelab-crf-sidecar-gpu &&
mkdir cr-function &&
mkdir ollama-gemma3 &&
cd cr-function
Créez un sous-dossier nommé "src". Dans le dossier, créez un fichier nommé index.ts.
mkdir src &&
touch src/index.ts
Remplacez le fichier index.ts par le code suivant:
//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);
}
});
Dans le répertoire racine crf-sidecar-gpu
, créez un fichier nommé package.json
et contenant le contenu suivant:
{
"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"
}
}
Créez également un fichier tsconfig.json
au niveau du répertoire racine avec le contenu suivant:
{
"compileOnSave": true,
"include": [
"src"
],
"compilerOptions": {
"module": "commonjs",
"noImplicitReturns": true,
"outDir": "lib",
"sourceMap": true,
"strict": true,
"target": "es2017",
"skipLibCheck": true,
"esModuleInterop": true
}
}
6. Déployer la fonction
À cette étape, vous allez déployer la fonction Cloud Run en exécutant la commande suivante.
Remarque: Le nombre maximal d'instances doit être inférieur ou égal à votre quota de 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. Créer le sidecar
Pour en savoir plus sur l'hébergement d'Ollama dans un service Cloud Run, consultez la page https://cloud.google.com/run/docs/tutorials/gpu-gemma-with-ollama.
Accédez au répertoire de votre sidecar:
cd ../ollama-gemma3
Créez un fichier Dockerfile
avec le contenu suivant :
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"]
Créer l'image
gcloud builds submit \
--tag $REGION-docker.pkg.dev/$PROJECT_ID/$AR_REPO/ollama-gemma3 \
--machine-type e2-highcpu-32
8. Mettre à jour la fonction avec le side-car
Pour ajouter un sidecar à un service, un job ou une fonction existants, vous pouvez modifier le fichier YAML pour qu'il contienne le sidecar.
Récupérez le fichier YAML de la fonction Cloud Run que vous venez de déployer en exécutant la commande suivante:
gcloud run services describe $FUNCTION_NAME --format=export > add-sidecar-service.yaml
Ajoutez maintenant le side-car au CRf en mettant à jour le fichier YAML comme suit:
- insérez le fragment YAML suivant juste au-dessus de la ligne
runtimeClassName: run.googleapis.com/linux-base-image-update
. Le-image
doit correspondre à l'élément de conteneur d'entrée-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
- Exécutez la commande suivante pour mettre à jour le fragment YAML avec vos variables d'environnement:
sed -i "s|YOUR_IMAGE_SIDECAR|$IMAGE_SIDECAR|; s|YOUR_BUCKET_GEMMA_NAME|$BUCKET_GEMMA_NAME|" add-sidecar-service.yaml
Votre fichier YAML final devrait se présenter comme suit:
##############################################
# 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
##############################################
Mettez à jour la fonction avec le sidecar en exécutant la commande suivante.
gcloud run services replace add-sidecar-service.yaml
Enfin, créez le déclencheur Eventarc pour la fonction. Cette commande l'ajoute également à la fonction.
Remarque: Si vous avez créé un bucket multirégional, vous devez modifier le paramètre --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. Tester votre fonction
Importez un fichier de texte brut à résumer. Vous ne savez pas quoi résumer ? Demandez à Gemini une description rapide de 1 à 2 pages sur l'histoire des chiens. Importez ensuite ce fichier au format texte brut dans votre bucket $BUCKET_DOCS_NAME
pour que le modèle Gemma3:4b écrive un résumé dans les journaux de la fonction.
Dans les journaux, vous verrez un résultat semblable à ceci:
---------------
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. Dépannage
Voici quelques erreurs de frappe que vous pouvez rencontrer:
- Si vous recevez une erreur
PORT 8080 is in use
, assurez-vous que votre fichier Dockerfile pour votre sidecar Ollama utilise le port 11434. Assurez-vous également d'utiliser la bonne image de sidecar au cas où vous auriez plusieurs images Ollama dans votre dépôt AR. La fonction Cloud Run est diffusée sur le port 8080. Si vous avez utilisé une autre image Ollama comme sidecar qui est également diffusée sur le port 8080, vous rencontrerez cette erreur. - Si vous obtenez l'erreur
failed to build: (error ID: 7485c5b6): function.js does not exist
, assurez-vous que vos fichiers package.json et tsconfig.json se trouvent au même niveau que le répertoire src. - Si l'erreur
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.
s'affiche, dans votre fichier YAML, remplacezautoscaling.knative.dev/maxScale: '100'
par 1 ou par une valeur inférieure ou égale à votre quota de GPU.