Как разместить Ollama в качестве рабочего пула для вывода

1. Введение

Обзор

В этом практическом занятии вы научитесь создавать асинхронный конвейер обработки данных в рамках искусственного интеллекта, управляемый событиями. Вы развернете модель с открытым исходным кодом, используя Ollama, в пуле рабочих процессов Cloud Run. Пул рабочих процессов получает сообщения из топика Pub/Sub и обрабатывает их с помощью модели gemma3:4b.

Что вы узнаете

  • Как использовать пулы рабочих процессов с подпиской Pub/Sub Pull
  • Как использовать Ollama для выполнения инференса в качестве пула рабочих процессов.

2. Прежде чем начать

Включить API

Прежде чем начать использовать этот практический пример, активируйте следующие API, выполнив следующие команды:

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

3. Настройка и требования

Для настройки необходимых ресурсов выполните следующие шаги:

  1. Установите переменные среды для этого практического занятия:
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. Создайте учетную запись службы для пула рабочих пользователей.
gcloud iam service-accounts create ${SERVICE_ACCOUNT_NAME} \
  --display-name="Ollama Worker Service Account"
  1. Предоставьте SA доступ к Pub/Sub
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member="serviceAccount:${SERVICE_ACCOUNT_EMAIL}" \
  --role="roles/pubsub.subscriber"
  1. Создайте репозиторий дополненной реальности для образа пула рабочих процессов.
gcloud artifacts repositories create ${AR_REPO_NAME} \
  --repository-format=docker \
  --location=${REGION}
  1. Создайте тему и подписку PubSub.
gcloud pubsub topics create $TOPIC_NAME
gcloud pubsub subscriptions create $SUBSCRIPTION_NAME --topic $TOPIC_NAME

4. Загрузите и разместите модель на GCS.

Вместо того чтобы загружать модель непосредственно в контейнер во время процесса сборки, что может быть медленно и неэффективно, мы загрузим модель на локальный компьютер с помощью CLI Ollama, а затем загрузим файлы модели в хранилище GCS. После этого пул рабочих узлов смонтирует это хранилище для доступа к модели.

  1. Установите Ollama на свой локальный компьютер:

Для установки Ollama на Linux выполните следующую команду. Информацию по другим операционным системам см. на веб-сайте Ollama .

curl -fsSL https://ollama.com/install.sh | sh
  1. Запустите службу Ollama и загрузите модель:

Сначала запустите службу Ollama в фоновом режиме.

ollama serve &
ollama pull gemma3:4b
  1. Создайте корзину GCS:

Создайте сегмент GCS, используя переменную среды BUCKET_NAME , которую вы установили ранее.

gsutil mb gs://${BUCKET_NAME}
  1. Загрузите файлы модели в свой бакет GCS:

Ollama хранит файлы моделей в каталоге ~/.ollama/models . Загрузите содержимое этого каталога в ваш GCS-хранилище. Это скопирует все загруженные вами модели.

gsutil -m cp -r ~/.ollama/models/* gs://${BUCKET_NAME}/
  1. Предоставьте администратору доступа к хранилищу Cloud Storage.
gcloud storage buckets add-iam-policy-binding gs://${BUCKET_NAME} \
     --member=serviceAccount:${SERVICE_ACCOUNT_EMAIL} \
     --role=roles/storage.objectViewer

5. Создайте задание Cloud Run.

В задании Cloud Run используются 2 контейнера:

  • ollama-coordinator - для проведения мероприятий ollama и обслуживания модели gemma 3 4B.
  • pubsub-pull-msg — для получения сообщений из подписки pubsub и передачи их в контейнер ollama-coordinator.

Сначала вам нужно создать контейнер ollama-coordinator.

  1. Создайте родительский каталог для практического задания:
mkdir codelab-ollama-wp
cd codelab-ollama-wp
  1. Создайте директорию для контейнера ollama-coordinator.
mkdir ollama-coordinator
cd ollama-coordinator
  1. Создайте Dockerfile со следующим содержимым.
# 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. Соберите контейнер для ламы.
gcloud builds submit --tag ${REGION}-docker.pkg.dev/${PROJECT_ID}/${AR_REPO_NAME}/${OLLAMA_IMAGE_NAME} --timeout=20m

Далее вам нужно будет создать контейнер pubsub-pull-msg.

  1. Создайте директорию для контейнера pubsub-pull-msg.
cd ..
mkdir pubsub-pull-msg
cd pubsub-pull-msg
  1. Создайте 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. Создайте файл requirements.txt со следующим содержимым.
google-cloud-pubsub
requests
  1. Создайте файл main.py со следующим содержимым.
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. Теперь создайте контейнер pubsub-pull-msg.
gcloud builds submit --tag ${REGION}-docker.pkg.dev/${PROJECT_ID}/${AR_REPO_NAME}/${PULL_MSG_IMAGE_NAME}

6. Развернуть и выполнить задание.

На этом шаге вы создадите задание Cloud Run, развернув файл YAML.

Перейдите в корневую папку, чтобы создать YAML-файл.

cd ..
  1. Создайте файл worker-pool.template.yaml со следующим содержимым.
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

Затем определите полные URL-адреса изображений и используйте sed для подстановки переменных в файл шаблона, создав в итоге файл worker-pool.yaml .

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

Теперь вы можете развернуть приложение.

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

И тест

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

Затем просмотрите журналы. Возможно, вам придётся подождать минуту, или вы можете перейти на страницу пула рабочих процессов в Cloud Console и посмотреть журналы в режиме реального времени.

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

и вы должны увидеть что-то, что гласит

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

7. Поздравляем!

Поздравляем с завершением практического занятия!

Мы рекомендуем ознакомиться с документацией Cloud Run .

Что мы рассмотрели

  • Как использовать пулы рабочих процессов Cloud Run с подпиской Pub/Sub Pull
  • Как использовать Ollama для выполнения инференса в качестве пула рабочих процессов Cloud Run

8. Уборка

Чтобы избежать списания средств с вашего аккаунта Google Cloud за ресурсы, используемые в этом руководстве, либо удалите проект, содержащий эти ресурсы, либо сохраните проект и удалите отдельные ресурсы.

Удаление проекта

Самый простой способ избежать выставления счетов — удалить проект, созданный для этого урока.

Чтобы удалить проект:

  1. В консоли Google Cloud перейдите на страницу «Управление ресурсами» .
  2. В списке проектов выберите проект, который хотите удалить, и нажмите кнопку «Удалить» .
  3. В диалоговом окне введите идентификатор проекта, а затем нажмите «Завершить» , чтобы удалить проект.

Удаление отдельных ресурсов

Для удаления отдельных ресурсов выполните следующие команды:

  1. Удалите пул рабочих процессов Cloud Run:
gcloud beta run worker-pools delete codelab-ollama-wp --region ${REGION}
  1. Удалите корзину GCS:
gsutil -m rm -r gs://${BUCKET_NAME}
  1. Удалите подписку Pub/Sub и тему:
gcloud pubsub subscriptions delete ${SUBSCRIPTION_NAME}
gcloud pubsub topics delete ${TOPIC_NAME}
  1. Удалите репозиторий реестра артефактов:
gcloud artifacts repositories delete ${AR_REPO_NAME} --location=${REGION} --quiet
  1. Удалите учетную запись службы:
gcloud iam service-accounts delete ${SERVICE_ACCOUNT_EMAIL} --quiet

Очистка локальных файлов

Для очистки локальных файлов выполните следующие действия:

  1. Остановите локальную службу Ollama: если вы запустили Ollama с помощью ollama serve & , вы можете остановить ее, найдя идентификатор процесса (PID) и используя команду 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. Удалите загруженные модели:
rm -rf ~/.ollama/models
  1. Удалите Ollama:

Чтобы удалить Ollama с вашего локального компьютера, следуйте инструкциям на веб-сайте Ollama .