1. Wprowadzenie
Przegląd
Z tego ćwiczenia dowiesz się, jak hostować model gemma3:4b w kontenerze pomocniczym funkcji Cloud Run. Gdy plik zostanie przesłany do zasobnika Cloud Storage, uruchomi to funkcję Cloud Run. Funkcja wyśle zawartość pliku do modelu Gemma 3 w pliku pomocniczym w celu podsumowania.
Czego się nauczysz
- Jak przeprowadzać wnioskowanie za pomocą funkcji Cloud Run i LLM hostowanego w kontenerze pomocniczym z użyciem GPU
- Jak używać konfiguracji ruchu wychodzącego z bezpośredniej sieci VPC w przypadku GPU w Cloud Run, aby szybciej przesyłać i udostępniać model
- Jak używać genkit do komunikacji z hostowanym modelem ollama
2. Zanim zaczniesz
Aby korzystać z funkcji GPU, musisz poprosić o zwiększenie limitu w obsługiwanym regionie. Wymagany limit to nvidia_l4_gpu_allocation_no_zonal_redundancy, który znajduje się w Cloud Run Admin API. Oto bezpośredni link do prośby o zwiększenie limitu.
3. Konfiguracja i wymagania
Ustaw zmienne środowiskowe, które będą używane w tym module.
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
Utwórz konto usługi, uruchamiając to polecenie:
gcloud iam service-accounts create $SERVICE_ACCOUNT \
--display-name="SA for codelab crf sidecar with gpu"
Użyjemy tego samego konta usługi, które jest używane jako tożsamość funkcji Cloud Run, jako konta usługi aktywatora Eventarc do wywoływania funkcji Cloud Run. Jeśli chcesz, możesz utworzyć inną SA dla Eventarc.
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role=roles/run.invoker
Przyznaj też kontu usługi dostęp do otrzymywania zdarzeń Eventarc.
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_ADDRESS" \
--role="roles/eventarc.eventReceiver"
Utwórz zasobnik, w którym będzie przechowywany dostrojony model. W tym ćwiczeniu używamy zasobnika regionalnego. Możesz też użyć zasobnika wieloregionowego.
gsutil mb -l $REGION gs://$BUCKET_GEMMA_NAME
Następnie przyznaj SA dostęp do zasobnika.
gcloud storage buckets add-iam-policy-binding gs://$BUCKET_GEMMA_NAME \
--member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role=roles/storage.objectAdmin
Teraz utwórz regionalny zasobnik, w którym będą przechowywane dokumenty, które chcesz podsumować. Możesz też użyć zasobnika wieloregionowego, pod warunkiem że odpowiednio zaktualizujesz aktywator Eventarc (pokazany na końcu tego ćwiczenia).
gsutil mb -l $REGION gs://$BUCKET_DOCS_NAME
Następnie przyznaj SA dostęp do zasobnika Gemma 3.
gcloud storage buckets add-iam-policy-binding gs://$BUCKET_GEMMA_NAME \
--member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role=roles/storage.objectAdmin
i zasobnik Dokumentów.
gcloud storage buckets add-iam-policy-binding gs://$BUCKET_DOCS_NAME \
--member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role=roles/storage.objectAdmin
Utwórz repozytorium Artifact Registry dla obrazu Ollama, który będzie używany w kontenerze pomocniczym.
gcloud artifacts repositories create $AR_REPO \
--repository-format=docker \
--location=$REGION \
--description="codelab for CR function and gpu sidecar" \
--project=$PROJECT_ID
4. Pobieranie modelu Gemma 3
Najpierw pobierz model Gemma 3 4b z ollama. Aby to zrobić, zainstaluj ollama, a potem uruchom lokalnie model gemma3:4b.
curl -fsSL https://ollama.com/install.sh | sh
ollama serve
Teraz w osobnym oknie terminala uruchom to polecenie, aby pobrać model. Jeśli korzystasz z Cloud Shell, możesz otworzyć dodatkowe okno terminala, klikając ikonę plusa na pasku menu w prawym górnym rogu.
ollama run gemma3:4b
Gdy ollama będzie działać, możesz zadać modelowi pytania, np.
"why is the sky blue?"
Gdy skończysz czatować z ollamą, możesz zamknąć czat, wpisując
/bye
Następnie w pierwszym oknie terminala uruchom to polecenie, aby zatrzymać lokalne udostępnianie usługi Ollama:
# on Linux / Cloud Shell press Ctrl^C or equivalent for your shell
Tutaj znajdziesz informacje o tym, gdzie Ollama pobiera modele w zależności od systemu operacyjnego.
https://github.com/ollama/ollama/blob/main/docs/faq.md#where-are-models-stored
Jeśli używasz Cloud Workstations, pobrane modele Ollama znajdziesz tutaj: /home/$USER/.ollama/models
Sprawdź, czy Twoje modele są hostowane tutaj:
ls /home/$USER/.ollama/models
teraz przenieś model gemma3:4b do zasobnika GCS.
gsutil cp -r /home/$USER/.ollama/models gs://$BUCKET_GEMMA_NAME
5. Tworzenie funkcji Cloud Run
Utwórz folder główny dla kodu źródłowego.
mkdir codelab-crf-sidecar-gpu &&
cd codelab-crf-sidecar-gpu &&
mkdir cr-function &&
mkdir ollama-gemma3 &&
cd cr-function
Utwórz podfolder o nazwie src. W folderze utwórz plik o nazwie index.ts.
mkdir src &&
touch src/index.ts
Zaktualizuj plik index.ts za pomocą tego kodu:
//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);
}
});
Teraz w katalogu głównym crf-sidecar-gpu utwórz plik o nazwie package.json z tą zawartością:
{
"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"
}
}
Utwórz plik tsconfig.json w katalogu głównym z następującą treścią:
{
"compileOnSave": true,
"include": [
"src"
],
"compilerOptions": {
"module": "commonjs",
"noImplicitReturns": true,
"outDir": "lib",
"sourceMap": true,
"strict": true,
"target": "es2017",
"skipLibCheck": true,
"esModuleInterop": true
}
}
6. Wdrażanie funkcji
W tym kroku wdrożysz funkcję Cloud Run, uruchamiając to polecenie.
Uwaga: maksymalna liczba instancji powinna być mniejsza lub równa limitowi 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. Tworzenie pliku pomocniczego
Więcej informacji o hostowaniu Ollamy w usłudze Cloud Run znajdziesz na stronie https://cloud.google.com/run/docs/tutorials/gpu-gemma-with-ollama
Przejdź do katalogu z aplikacją pomocniczą:
cd ../ollama-gemma3
Utwórz plik Dockerfile o tej zawartości:
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"]
Tworzenie obrazu
gcloud builds submit \
--tag $REGION-docker.pkg.dev/$PROJECT_ID/$AR_REPO/ollama-gemma3 \
--machine-type e2-highcpu-32
8. Aktualizowanie funkcji za pomocą kontenera dodatkowego
Aby dodać kontener pomocniczy do istniejącej usługi, zadania lub funkcji, możesz zaktualizować plik YAML, aby zawierał kontener pomocniczy.
Pobierz plik YAML wdrożonej właśnie funkcji Cloud Run, uruchamiając to polecenie:
gcloud run services describe $FUNCTION_NAME --format=export > add-sidecar-service.yaml
Teraz dodaj kontener dodatkowy do CRf, aktualizując plik YAML w ten sposób:
- wstaw ten fragment YAML bezpośrednio nad wierszem
runtimeClassName: run.googleapis.com/linux-base-image-update.-imagemusi być wyrównany z elementem kontenera Ingress-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
- Aby zaktualizować fragment YAML za pomocą zmiennych środowiskowych, uruchom to polecenie:
sed -i "s|YOUR_IMAGE_SIDECAR|$IMAGE_SIDECAR|; s|YOUR_BUCKET_GEMMA_NAME|$BUCKET_GEMMA_NAME|" add-sidecar-service.yaml
Ukończony plik YAML powinien wyglądać mniej więcej tak:
##############################################
# 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
##############################################
Teraz zaktualizuj funkcję za pomocą kontenera dodatkowego, uruchamiając to polecenie.
gcloud run services replace add-sidecar-service.yaml
Na koniec utwórz aktywator Eventarc dla funkcji. To polecenie dodaje też wartość do funkcji.
Uwaga: jeśli utworzysz zasobnik w wielu regionach, musisz zmienić parametr --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. Testowanie funkcji
Prześlij plik w formacie zwykłego tekstu do podsumowania. Nie wiesz, co streścić? Zapytaj Gemini o szybki opis historii psów na 1–2 stronach. Następnie prześlij ten plik tekstowy do zasobnika $BUCKET_DOCS_NAME, aby model Gemma3:4b zapisał podsumowanie w logach funkcji.
W dziennikach zobaczysz tekst podobny do tego:
---------------
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. Rozwiązywanie problemów
Oto niektóre błędy, które możesz napotkać:
- Jeśli pojawi się błąd
PORT 8080 is in use, upewnij się, że plik Dockerfile dla kontenera pomocniczego Ollama korzysta z portu 11434. Sprawdź też, czy używasz prawidłowego obrazu pomocniczego, jeśli w repozytorium AR masz kilka obrazów Ollamy. Funkcja Cloud Run działa na porcie 8080, a jeśli jako kontener dodatkowy używasz innego obrazu Ollamy, który też działa na porcie 8080, wystąpi ten błąd. - Jeśli zobaczysz błąd
failed to build: (error ID: 7485c5b6): function.js does not exist, sprawdź, czy pliki package.json i tsconfig.json znajdują się na tym samym poziomie co katalog src. - Jeśli pojawi się błąd
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., w pliku YAML zmień wartośćautoscaling.knative.dev/maxScale: '100'na 1 lub na wartość mniejszą lub równą limitowi GPU.