Come ospitare Ollama come pool di worker per l'inferenza

1. Introduzione

Panoramica

In questo codelab imparerai a creare una pipeline di elaborazione AI asincrona basata su eventi. Esegui il deployment di un modello open source utilizzando Ollama in un pool di worker Cloud Run. Il pool di worker estrae i messaggi da un argomento Pub/Sub e li elabora utilizzando un modello gemma3:4b.

Obiettivi didattici

  • Utilizzare i pool di worker con una sottoscrizione pull Pub/Sub
  • Come utilizzare Ollama per eseguire l'inferenza come pool di worker

2. Prima di iniziare

Abilita API

Prima di poter iniziare a utilizzare questo codelab, abilita le seguenti API eseguendo:

gcloud services enable run.googleapis.com \
    cloudbuild.googleapis.com \
    artifactregistry.googleapis.com \
    pubsub.googleapis.com \
    storage.googleapis.com

3. Configurazione e requisiti

Per configurare le risorse richieste:

  1. Imposta le variabili di ambiente per questo codelab:
export PROJECT_ID=<YOUR_PROJECT_ID>
export REGION=<YOUR_REGION>

export BUCKET_NAME=$PROJECT_ID-gemma3-4b
export SERVICE_ACCOUNT_NAME=ollama-worker-sa
export SERVICE_ACCOUNT_EMAIL=${SERVICE_ACCOUNT_NAME}@${PROJECT_ID}.iam.gserviceaccount.com
export TOPIC_NAME=ollama-prompts
export SUBSCRIPTION_NAME=ollama-prompts-sub
export AR_REPO_NAME=ollama-worker-repo
export PULL_MSG_IMAGE_NAME=pubsub-pull-msg
export OLLAMA_IMAGE_NAME=ollama-coordinator
  1. Crea un service account per il pool di worker
gcloud iam service-accounts create ${SERVICE_ACCOUNT_NAME} \
  --display-name="Ollama Worker Service Account"
  1. Concedi al service account l'accesso a Pub/Sub
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member="serviceAccount:${SERVICE_ACCOUNT_EMAIL}" \
  --role="roles/pubsub.subscriber"
  1. Crea un repository AR per l'immagine del pool di worker
gcloud artifacts repositories create ${AR_REPO_NAME} \
  --repository-format=docker \
  --location=${REGION}
  1. Crea l'argomento e la sottoscrizione Pub/Sub
gcloud pubsub topics create $TOPIC_NAME
gcloud pubsub subscriptions create $SUBSCRIPTION_NAME --topic $TOPIC_NAME

4. Scaricare e ospitare il modello su GCS

Anziché estrarre il modello direttamente all'interno del container durante il processo di build, che può essere lento e inefficiente, lo estrarremo in una macchina locale utilizzando la CLI Ollama e poi caricheremo i file del modello in un bucket GCS. Il pool di worker monterà questo bucket per accedere al modello.

  1. Installa Ollama sulla tua macchina locale:

Esegui questo comando per installare Ollama su Linux. Per altri sistemi operativi, consulta il sito web di Ollama.

curl -fsSL https://ollama.com/install.sh | sh
  1. Avvia il servizio Ollama ed estrai il modello:

Per prima cosa, avvia il servizio Ollama in background.

ollama serve &
ollama pull gemma3:4b
  1. Crea un bucket GCS:

Crea il bucket GCS utilizzando la variabile di ambiente BUCKET_NAME che hai impostato in precedenza.

gsutil mb gs://${BUCKET_NAME}
  1. Carica i file del modello nel bucket GCS:

Ollama archivia i file del modello nella directory ~/.ollama/models. Carica i contenuti di questa directory nel bucket GCS. Verranno copiati tutti i modelli che hai scaricato.

gsutil -m cp -r ~/.ollama/models/* gs://${BUCKET_NAME}/
  1. Concedi all'account di servizio l'accesso al bucket Cloud Storage
gcloud storage buckets add-iam-policy-binding gs://${BUCKET_NAME} \
     --member=serviceAccount:${SERVICE_ACCOUNT_EMAIL} \
     --role=roles/storage.objectViewer

5. Crea il job Cloud Run

Il job Cloud Run utilizza due container:

  • ollama-coordinator: per ospitare ollama e distribuire il modello Gemma 3 4B
  • pubsub-pull-msg: per eseguire il pull dalla sottoscrizione pubsub e passare il messaggio al contenitore ollama-coordinator

Per prima cosa, crea il container ollama-coordinator.

  1. Crea una directory principale per il codelab:
mkdir codelab-ollama-wp
cd codelab-ollama-wp
  1. Crea una directory per il container ollama-coordinator
mkdir ollama-coordinator
cd ollama-coordinator
  1. Crea un file Dockerfile con il seguente contenuto
# Use the official Ollama image as a base image
FROM ollama/ollama

# Expose the port that Ollama listens on
EXPOSE 11434

# Set the entrypoint to start the Ollama server
ENTRYPOINT ["ollama", "serve"]
  1. Crea il container ollama
gcloud builds submit --tag ${REGION}-docker.pkg.dev/${PROJECT_ID}/${AR_REPO_NAME}/${OLLAMA_IMAGE_NAME} --timeout=20m

Successivamente, creerai il container pubsub-pull-msg.

  1. Crea una directory per il container pubsub-pull-msg
cd ..
mkdir pubsub-pull-msg
cd pubsub-pull-msg
  1. Crea un Dockerfile
# Use the official Python image as a base image
FROM python:3.9-slim

# Set the working directory in the container
WORKDIR /app

# Copy the requirements file into the container
COPY requirements.txt .

# Install the required Python packages
RUN pip install --no-cache-dir -r requirements.txt

# Copy the Python script into the container
COPY main.py .

# Set the entrypoint to run the Python script
CMD ["python", "main.py"]
  1. Crea un file requirements.txt con il seguente contenuto
google-cloud-pubsub
requests
  1. Crea un file main.py con il seguente contenuto
import os
import sys
import requests
import json
from google.cloud import pubsub_v1

# --- Main Application Logic ---
print("--- Sidecar container script started ---")

# --- Environment and Configuration ---
project_id = os.environ.get("PROJECT_ID")
subscription_name = os.environ.get("SUBSCRIPTION_NAME")
ollama_api_url = "http://localhost:11434/api/generate"

if not project_id or not subscription_name:
    print("FATAL: PROJECT_ID and SUBSCRIPTION_NAME must be set.")
    sys.exit(1)

print(f"PROJECT_ID: {project_id}")
print(f"SUBSCRIPTION_NAME: {subscription_name}")

def callback(message):
    """Processes a single Pub/Sub message."""
    print(f"Received message ID: {message.message_id}")
    try:
        prompt = message.data.decode("utf-8")
        print(f"Decoded prompt: '{prompt}'")
        
        data = {"model": "gemma3:4b", "prompt": prompt, "stream": False}
        
        print("Sending request to Ollama...")
        response = requests.post(ollama_api_url, json=data, timeout=300)
        response.raise_for_status()
        
        print("Successfully received response from Ollama.")
        ollama_response = response.json()
        print(f"Ollama response: {json.dumps(ollama_response)[:200]}...")

        message.ack()
        print(f"Message {message.message_id} acknowledged.")

    except requests.exceptions.RequestException as e:
        print(f"Error calling Ollama API: {e}")
        message.nack()
        print(f"Message {message.message_id} not acknowledged.")
    except Exception as e:
        print(f"An unexpected error occurred in callback: {e}")
        message.nack()
        print(f"Message {message.message_id} not acknowledged.")

def main():
    """Starts the Pub/Sub subscriber."""
    subscriber = pubsub_v1.SubscriberClient()
    subscription_path = subscriber.subscription_path(project_id, subscription_name)
    
    streaming_pull_future = subscriber.subscribe(subscription_path, callback=callback)
    print(f"Subscribed to {subscription_path}. Listening for messages...")

    try:
        # .result() will block indefinitely.
        streaming_pull_future.result()
    except Exception as e:
        print(f"A fatal error occurred in the subscriber: {e}")
        streaming_pull_future.cancel()
        streaming_pull_future.result()

if __name__ == "__main__":
    main()
  1. Ora crea il container pubsub-pull-msg
gcloud builds submit --tag ${REGION}-docker.pkg.dev/${PROJECT_ID}/${AR_REPO_NAME}/${PULL_MSG_IMAGE_NAME}

6. Esegui il deployment ed esegui il job

In questo passaggio, creerai il job Cloud Run eseguendo il deployment di un file YAML.

Sposta nella cartella principale per creare il file YAML.

cd ..
  1. Crea un file worker-pool.template.yaml con il seguente contenuto
apiVersion: run.googleapis.com/v1
kind: WorkerPool
metadata:
  name: codelab-ollama-wp
  labels:
    cloud.googleapis.com/location: europe-west1
  annotations:
    run.googleapis.com/launch-stage: BETA
    run.googleapis.com/scalingMode: manual
    run.googleapis.com/manualInstanceCount: '1'
    run.googleapis.com/gcs-fuse-mounter-enabled: "true"
spec:
  template:
    metadata:
      annotations:
        run.googleapis.com/gpu: "1"
        run.googleapis.com/gpu-zonal-redundancy-disabled: 'true'        
    spec:
      serviceAccountName: ${SERVICE_ACCOUNT_EMAIL}
      nodeSelector:
        run.googleapis.com/accelerator: nvidia-l4
      volumes:
      - name: gcs-bucket
        csi:
          driver: gcsfuse.run.googleapis.com
          readOnly: true
          volumeAttributes: 
            bucketName: ${BUCKET_NAME}
      containers:
      - image: ${REGION}-docker.pkg.dev/${PROJECT_ID}/${AR_REPO_NAME}/${PULL_MSG_IMAGE_NAME}
        name: pubsub-pull-msg
        env:
        - name: PROJECT_ID
          value: ${PROJECT_ID}
        - name: SUBSCRIPTION_NAME
          value: "ollama-prompts-sub"
        - name: PYTHONUNBUFFERED
          value: "1"
        resources:
          limits:
            cpu: '1'
            memory: 1Gi
      - image: ${REGION}-docker.pkg.dev/${PROJECT_ID}/${AR_REPO_NAME}/${OLLAMA_IMAGE_NAME}
        name: ollama-coordinator
        env:
        - name: OLLAMA_MODELS
          value: /mnt/models
        volumeMounts:
        - name: gcs-bucket
          mountPath: /mnt/models
        resources:
          limits:
            cpu: '6'
            nvidia.com/gpu: '1'
            memory: 16Gi

Poi, definisci gli URL completi delle immagini e utilizza sed per sostituire le variabili nel file modello, creando il worker-pool.yaml finale.

sed -e "s|\${SERVICE_ACCOUNT_EMAIL}|${SERVICE_ACCOUNT_EMAIL}|g" \
     -e "s|\${BUCKET_NAME}|${BUCKET_NAME}|g" \
     -e "s|\${PULL_MSG_IMAGE_NAME}|${PULL_MSG_IMAGE_NAME}|g" \
     -e "s|\${OLLAMA_IMAGE_NAME}|${OLLAMA_IMAGE_NAME}|g" \
     -e "s|\${PROJECT_ID}|${PROJECT_ID}|g" \
     -e "s|\${REGION}|${REGION}|g" \
     -e "s|\${AR_REPO_NAME}|${AR_REPO_NAME}|g" \
     worker-pool.template.yaml > worker-pool.yaml

Ora puoi eseguire il deployment

gcloud beta run worker-pools replace worker-pool.yaml

E test

gcloud pubsub topics publish ${TOPIC_NAME} --message="What is 1 + 1?"

e poi visualizza i log. Potresti dover attendere un minuto oppure puoi andare alla pagina del pool di worker della console Cloud e guardare i log in tempo reale.

gcloud alpha run worker-pools logs read "codelab-ollama-wp" --limit 10

e dovresti vedere un messaggio che dice

Ollama response: {"model": "gemma3:4b", "created_at": "2025-11-06T23:48:39.572079369Z", "response": "1 + 1 = 2\n", ...

7. Complimenti!

Congratulazioni per aver completato il codelab.

Ti consigliamo di consultare la documentazione di Cloud Run.

Argomenti trattati

  • Come utilizzare i pool di worker Cloud Run con una sottoscrizione pull di Pub/Sub
  • Come utilizzare Ollama per eseguire l'inferenza come pool di worker Cloud Run

8. Esegui la pulizia

Per evitare che al tuo account Google Cloud vengano addebitati costi relativi alle risorse utilizzate in questo tutorial, elimina il progetto che contiene le risorse oppure mantieni il progetto ed elimina le singole risorse.

Elimina il progetto

Il modo più semplice per eliminare la fatturazione è eliminare il progetto creato per il tutorial.

Per eliminare il progetto:

  1. Nella console Google Cloud, vai alla pagina Gestisci risorse.
  2. Nell'elenco dei progetti, seleziona il progetto che vuoi eliminare, quindi fai clic su Elimina.
  3. Nella finestra di dialogo, digita l'ID progetto, quindi fai clic su Chiudi per eliminare il progetto.

Eliminazione di singole risorse

Per eliminare le singole risorse, esegui questi comandi:

  1. Elimina il pool di worker Cloud Run:
gcloud beta run worker-pools delete codelab-ollama-wp --region ${REGION}
  1. Elimina il bucket GCS:
gsutil -m rm -r gs://${BUCKET_NAME}
  1. Elimina l'argomento e la sottoscrizione Pub/Sub:
gcloud pubsub subscriptions delete ${SUBSCRIPTION_NAME}
gcloud pubsub topics delete ${TOPIC_NAME}
  1. Elimina il repository Artifact Registry:
gcloud artifacts repositories delete ${AR_REPO_NAME} --location=${REGION} --quiet
  1. Elimina il account di servizio:
gcloud iam service-accounts delete ${SERVICE_ACCOUNT_EMAIL} --quiet

Pulizia dei file locali

Per pulire i file locali:

  1. Arresta il servizio Ollama locale:se hai avviato Ollama con ollama serve &, puoi arrestarlo trovando il relativo ID processo (PID) e utilizzando il comando kill.
    # Find the process ID of the Ollama server
    pgrep ollama
    
    # Replace <PID> with the actual process ID obtained from the previous command
    kill <PID>
    
  2. Elimina i modelli scaricati:
rm -rf ~/.ollama/models
  1. Disinstalla Ollama:

Segui le istruzioni sul sito web di Ollama per disinstallare Ollama dalla tua macchina locale.