Informacje o tym ćwiczeniu (w Codelabs)
1. Wprowadzenie
Omówienie
Z tego Codelab dowiesz się, jak hostować model gemma3:4b w kontenerze pomocniczym dla funkcji Cloud Run. Gdy plik zostanie przesłany do zasobnika Cloud Storage, uruchomi funkcję Cloud Run. Funkcja wyśle zawartość pliku do Gemma 3 w ramach podsumowania.
Czego się nauczysz
- Jak wykonywać wnioskowanie za pomocą funkcji Cloud Run i LLM hostowanego w kontenerze pomocniczym z wykorzystaniem GPU
- Jak użyć konfiguracji bezpośredniego wyjścia sieci VPC w usłudze Cloud Run GPU, aby przyspieszyć przesyłanie i udostępnianie modelu
- Jak używać genkit do interfejsu z hostowanym modelem ollama
2. Zanim zaczniesz
Aby korzystać z funkcji GPU, musisz poprosić o zwiększenie limitu w obsługiwanym regionie. Potrzebna jest kwota nvidia_l4_gpu_allocation_no_zonal_redundancy, która znajduje się w Cloud Run Admin API. Oto bezpośredni link do strony z prośbą o przydzielenie limitu.
3. Konfiguracja i wymagania
Ustaw zmienne środowiskowe, których będziesz używać w tym ćwiczeniu.
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 dla aktywatora eventarc, aby wywołać funkcję Cloud Run. Jeśli chcesz, możesz utworzyć inną umowę partnerską 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 zdarzeń Eventarc.
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_ADDRESS" \
--role="roles/eventarc.eventReceiver"
Utwórz zasobnik, który będzie hostować Twój model dostrojony. Ten warsztat programistyczny korzysta z zasobnika regionalnego. Możesz też użyć zasobnika z wieloma regionami.
gsutil mb -l $REGION gs://$BUCKET_GEMMA_NAME
Następnie przyznaj dostęp do zasobnika użytkownikowi SA.
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ć puli wieloregionalnej, pod warunkiem że zaktualizujesz odpowiednio odpowiednik Eventarc (jak pokazano na końcu tego Codelab).
gsutil mb -l $REGION gs://$BUCKET_DOCS_NAME
Następnie przyznaj 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 pobocznym.
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 witryny ollama. Aby to zrobić, zainstaluj narzędzie ollama, a następnie uruchom model gemma3:4b lokalnie.
curl -fsSL https://ollama.com/install.sh | sh
ollama serve
W osobnym oknie terminala uruchom to polecenie, aby pobrać model. Jeśli używasz 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 już uruchomisz narzędzie, możesz zadać modelowi kilka pytań, np.
"why is the sky blue?"
Gdy skończysz czatowanie z ollama, możesz zamknąć czat, uruchamiając
/bye
Następnie w pierwszym oknie terminala uruchom to polecenie, aby zatrzymać lokalną obsługę ollama
# on Linux / Cloud Shell press Ctrl^C or equivalent for your shell
Tutaj możesz sprawdzić, skąd 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, możesz pobrać modele ollama 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 na kod źródłowy.
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
na poziomie katalogu głównego z taką zawartoś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ć równa lub mniejsza od limitu 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 kontenera
Więcej informacji o hostowaniu Ollama w usłudze Cloud Run znajdziesz na stronie https://cloud.google.com/run/docs/tutorials/gpu-gemma-with-ollama.
Przejdź do katalogu sidecar:
cd ../ollama-gemma3
Utwórz plik Dockerfile
z tą 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. Zaktualizuj funkcję za pomocą sidecara
Aby dodać plik pomocniczy do istniejącej usługi, zadania lub funkcji, możesz zaktualizować plik YAML, aby zawierał plik pomocniczy.
Aby pobrać plik YAML funkcji Cloud Run, którą właśnie wdrożyłeś, wykonaj te czynności:
gcloud run services describe $FUNCTION_NAME --format=export > add-sidecar-service.yaml
Teraz dodaj do formularza CRf dokument pomocniczy, aktualizując plik YAML w ten sposób:
- wstaw bezpośrednio nad wierszem
runtimeClassName: run.googleapis.com/linux-base-image-update
podany niżej fragment kodu YAML. Element-image
powinien być zgodny z elementem kontenera napływowego-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
Gotowy 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ą sidecara, wykonując to polecenie.
gcloud run services replace add-sidecar-service.yaml
Na koniec utwórz dla funkcji odpowiedni aktywator Eventarc. To polecenie również dodaje je do funkcji.
Uwaga: jeśli utworzysz zasób wieloregionalny, 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 tekstowy do podsumowania. Nie wiesz, co podsumować? Poproś Gemini o krótki opis 1–2-stronicowego tekstu o historii psów. Następnie prześlij ten plik tekstowy do zasobnika $BUCKET_DOCS_NAME
dla modelu Gemma3:4b, aby zapisać podsumowanie w logach funkcji.
W dziennikach zobaczysz komunikat 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 związane z błędami w pisaniu, które możesz napotkać:
- Jeśli pojawi się błąd
PORT 8080 is in use
, sprawdź, czy plik Dockerfile dla podprogramu Ollama używa portu 11434. Sprawdź też, czy używasz prawidłowego obrazu sidecar, jeśli masz w repozytorium AR wiele obrazów Ollama. Funkcja Cloud Run działa na porcie 8080, a jeśli jako kontener pomocniczy użyjesz innego obrazu Ollama, który również działa na porcie 8080, wystąpi błąd. - Jeśli pojawi się 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 wystąpi 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ą kwocie dostępnej kwoty na GPU.