Cách lưu trữ LLM trong một sidecar cho hàm Cloud Run

Cách lưu trữ LLM trong một sidecar cho hàm Cloud Run

Thông tin về lớp học lập trình này

subjectLần cập nhật gần đây nhất: thg 3 27, 2025
account_circleTác giả: Một nhân viên của Google

1. Giới thiệu

Tổng quan

Trong lớp học lập trình này, bạn sẽ tìm hiểu cách lưu trữ mô hình gemma3:4b trong một sidecar cho một hàm Cloud Run. Khi một tệp được tải lên bộ chứa trên Google Cloud Storage, tệp đó sẽ kích hoạt hàm Cloud Run. Hàm này sẽ gửi nội dung của tệp đến Gemma 3 trong sidecar để tóm tắt.

Kiến thức bạn sẽ học được

  • Cách dự đoán bằng cách sử dụng hàm Cloud Run và LLM được lưu trữ trong một sidecar bằng GPU
  • Cách sử dụng cấu hình đầu ra VPC trực tiếp cho GPU Cloud Run để tải lên và phân phát mô hình nhanh hơn
  • Cách sử dụng genkit để giao tiếp với mô hình ollama được lưu trữ

2. Trước khi bắt đầu

Để sử dụng tính năng GPU, bạn phải yêu cầu tăng hạn mức cho một khu vực được hỗ trợ. Hạn mức cần thiết là nvidia_l4_gpu_allocation_no_zonal_redundancy, thuộc Cloud Run Admin API. Dưới đây là đường liên kết trực tiếp để yêu cầu hạn mức.

3. Cách thiết lập và các yêu cầu

Thiết lập các biến môi trường sẽ được sử dụng trong suốt lớp học lập trình này.

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

Tạo tài khoản dịch vụ bằng cách chạy lệnh sau:

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

Chúng ta sẽ sử dụng chính tài khoản dịch vụ này làm danh tính của hàm Cloud Run làm tài khoản dịch vụ cho trình kích hoạt eventarc để gọi hàm Cloud Run. Bạn có thể tạo một SA khác cho Eventarc nếu muốn.

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

Ngoài ra, hãy cấp cho tài khoản dịch vụ quyền nhận các sự kiện Eventarc.

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

Tạo một bộ chứa sẽ lưu trữ mô hình được tinh chỉnh của bạn. Lớp học lập trình này sử dụng một bộ chứa theo khu vực. Bạn cũng có thể sử dụng bộ chứa nhiều khu vực.

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

Sau đó, cấp cho SA quyền truy cập vào bộ chứa.

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

Bây giờ, hãy tạo một bộ chứa theo khu vực để lưu trữ các tài liệu bạn muốn tóm tắt. Bạn cũng có thể sử dụng bộ chứa nhiều khu vực, miễn là bạn cập nhật trình kích hoạt Eventarc cho phù hợp (hiển thị ở cuối lớp học lập trình này).

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

Sau đó, cấp cho SA quyền truy cập vào bộ chứa Gemma 3.

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

và bộ chứa Tài liệu.

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

Tạo kho lưu trữ đăng ký cấu phần phần mềm cho hình ảnh Ollama sẽ được sử dụng trong 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ải mô hình Gemma 3 xuống

Trước tiên, bạn cần tải mô hình Gemma 3 4b xuống từ ollama. Bạn có thể thực hiện việc này bằng cách cài đặt ollama rồi chạy mô hình gemma3:4b trên máy.

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

Bây giờ, trong một cửa sổ dòng lệnh riêng, hãy chạy lệnh sau để tải mô hình xuống. Nếu đang sử dụng Cloud Shell, bạn có thể mở thêm một cửa sổ dòng lệnh bằng cách nhấp vào biểu tượng dấu cộng ở thanh trình đơn trên cùng bên phải.

ollama run gemma3:4b

Sau khi ollama chạy, bạn có thể hỏi mô hình một số câu hỏi, ví dụ:

"why is the sky blue?"

Sau khi trò chuyện xong với ollama, bạn có thể thoát khỏi cuộc trò chuyện bằng cách chạy

/bye

Sau đó, trong cửa sổ dòng lệnh đầu tiên, hãy chạy lệnh sau để ngừng phân phát ollama cục bộ

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

Bạn có thể tìm thấy nơi Ollama tải các mô hình xuống tuỳ thuộc vào hệ điều hành của bạn tại đây.

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

Nếu đang sử dụng Máy trạm trên đám mây, bạn có thể tìm thấy các mô hình ollama đã tải xuống tại đây /home/$USER/.ollama/models

Xác nhận rằng các mô hình của bạn được lưu trữ tại đây:

ls /home/$USER/.ollama/models

giờ hãy chuyển mô hình gemma3:4b sang bộ chứa GCS

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

5. Tạo hàm Cloud Run

Tạo thư mục gốc cho mã nguồn.

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

Tạo một thư mục con có tên là src. Bên trong thư mục, hãy tạo một tệp có tên index.ts

mkdir src &&
touch src
/index.ts

Cập nhật index.ts bằng mã sau:

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

Bây giờ, trong thư mục gốc crf-sidecar-gpu, hãy tạo một tệp có tên package.json với nội dung sau:

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

Tạo một tsconfig.json cũng ở cấp thư mục gốc với nội dung sau:

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

6. Triển khai hàm

Ở bước này, bạn sẽ triển khai hàm Cloud Run bằng cách chạy lệnh sau.

Lưu ý: bạn nên đặt số lượng thực thể tối đa thành một số nhỏ hơn hoặc bằng hạn mức 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. Tạo sidecar

Bạn có thể tìm hiểu thêm về cách lưu trữ Ollama trong dịch vụ Cloud Run tại https://cloud.google.com/run/docs/tutorials/gpu-gemma-with-ollama

Chuyển vào thư mục cho xe bên:

cd ../ollama-gemma3

Tạo tệp Dockerfile có nội dung sau:

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

Tạo hình ảnh

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

8. Cập nhật hàm bằng sidecar

Để thêm một tệp trợ giúp vào một dịch vụ, công việc hoặc hàm hiện có, bạn có thể cập nhật tệp YAML để chứa tệp trợ giúp đó.

Truy xuất tệp YAML cho hàm Cloud Run mà bạn vừa triển khai bằng cách chạy:

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

Bây giờ, hãy thêm sidecar vào CRf bằng cách cập nhật tệp YAML như sau:

  1. chèn mảnh YAML sau ngay phía trên dòng runtimeClassName: run.googleapis.com/linux-base-image-update. -image phải khớp với mục vùng chứa truy cập -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
  1. Chạy lệnh sau để cập nhật mảnh YAML bằng các biến môi trường:
sed -i "s|YOUR_IMAGE_SIDECAR|$IMAGE_SIDECAR|; s|YOUR_BUCKET_GEMMA_NAME|$BUCKET_GEMMA_NAME|" add-sidecar-service.yaml

Tệp YAML hoàn chỉnh sẽ có dạng như sau:

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

Bây giờ, hãy cập nhật hàm bằng sidecar bằng cách chạy lệnh sau.

gcloud run services replace add-sidecar-service.yaml

Cuối cùng, hãy tạo điều kiện kích hoạt eventarc cho hàm. Lệnh này cũng thêm biến đó vào hàm.

Lưu ý: nếu đã tạo một bộ chứa nhiều khu vực, bạn nên thay đổi thông số --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. Kiểm thử hàm

Tải tệp văn bản thuần tuý lên để tóm tắt. Bạn không biết nên tóm tắt nội dung nào? Yêu cầu Gemini cung cấp nội dung mô tả nhanh từ 1 đến 2 trang về lịch sử của loài chó! Sau đó, hãy tải tệp văn bản thuần tuý đó lên bộ chứa $BUCKET_DOCS_NAME để mô hình Gemma3:4b ghi nội dung tóm tắt vào nhật ký hàm.

Trong nhật ký, bạn sẽ thấy nội dung tương tự như sau:

---------------
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. Khắc phục sự cố

Dưới đây là một số lỗi chính tả mà bạn có thể gặp phải:

  1. Nếu bạn gặp lỗi PORT 8080 is in use, hãy đảm bảo tệp Dockerfile cho xe bên Ollama của bạn đang sử dụng cổng 11434. Ngoài ra, hãy đảm bảo bạn đang sử dụng đúng hình ảnh xe bên trong trường hợp bạn có nhiều hình ảnh Ollama trong kho lưu trữ AR. Hàm Cloud Run phân phát trên cổng 8080 và nếu bạn đã sử dụng một hình ảnh Ollama khác làm xe bên cũng phân phát trên 8080, thì bạn sẽ gặp lỗi này.
  2. Nếu bạn gặp lỗi failed to build: (error ID: 7485c5b6): function.js does not exist, hãy đảm bảo rằng các tệp package.json và tsconfig.json nằm ở cùng cấp với thư mục src.
  3. Nếu bạn gặp lỗi 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., trong tệp YAML, hãy thay đổi autoscaling.knative.dev/maxScale: '100' thành 1 hoặc thành một giá trị nhỏ hơn hoặc bằng hạn mức GPU của bạn.