چگونه Ollama را به عنوان یک استخر کارگر برای استنتاج میزبانی کنیم؟

۱. مقدمه

نمای کلی

در این آزمایشگاه کد، شما یاد خواهید گرفت که چگونه یک خط لوله پردازش هوش مصنوعی ناهمزمان و مبتنی بر رویداد بسازید. شما یک مدل متن‌باز را با استفاده از Ollama روی یک Cloud Run Worker Pool مستقر خواهید کرد. Worker Pool پیام‌ها را از یک Pub/Sub topic دریافت کرده و آنها را با استفاده از یک مدل gemma3:4b پردازش می‌کند.

آنچه یاد خواهید گرفت

  • نحوه استفاده از استخرهای کارگری با اشتراک Pub/Sub Pull
  • نحوه استفاده از Ollama برای انجام استنتاج به عنوان یک worker pool

۲. قبل از شروع

فعال کردن APIها

قبل از اینکه بتوانید از این codelab استفاده کنید، API های زیر را با اجرای دستور زیر فعال کنید:

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

۳. تنظیمات و الزامات

برای تنظیم منابع مورد نیاز، مراحل زیر را دنبال کنید:

  1. متغیرهای محیطی را برای این 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. یک حساب کاربری سرویس برای مجموعه کارگران ایجاد کنید
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. یک مخزن AR برای تصویر استخر کارگران ایجاد کنید
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

۴. دانلود و میزبانی مدل روی GCS

به جای اینکه مدل را مستقیماً در طول فرآیند ساخت به داخل کانتینر منتقل کنیم، که می‌تواند کند و ناکارآمد باشد، مدل را با استفاده از رابط خط فرمان Ollama به یک دستگاه محلی منتقل می‌کنیم و سپس فایل‌های مدل را در یک سطل GCS آپلود می‌کنیم. سپس مخزن کارگران (worker pool) این سطل را برای دسترسی به مدل، سوار (mount) می‌کند.

  1. Ollama را روی دستگاه محلی خود نصب کنید:

برای نصب Ollama روی لینوکس، دستور زیر را اجرا کنید. برای سایر سیستم عامل‌ها، لطفاً به وب‌سایت Ollama مراجعه کنید.

curl -fsSL https://ollama.com/install.sh | sh
  1. سرویس Ollama را شروع کنید و مدل را دریافت کنید:

First, start the Ollama service in the background.

ollama serve &
ollama pull gemma3:4b
  1. یک سطل GCS ایجاد کنید:

با استفاده از متغیر محیطی BUCKET_NAME که قبلاً تنظیم کرده‌اید، سطل GCS را ایجاد کنید.

gsutil mb gs://${BUCKET_NAME}
  1. فایل‌های مدل را در سطل GCS خود آپلود کنید:

اولاما فایل‌های مدل را در دایرکتوری ~/.ollama/models ذخیره می‌کند. محتویات این دایرکتوری را در مخزن GCS خود آپلود کنید. این کار تمام مدل‌هایی را که دانلود کرده‌اید کپی می‌کند.

gsutil -m cp -r ~/.ollama/models/* gs://${BUCKET_NAME}/
  1. به SA دسترسی به فضای ذخیره‌سازی ابری (Cloud Storage) را اعطا کنید.
gcloud storage buckets add-iam-policy-binding gs://${BUCKET_NAME} \
     --member=serviceAccount:${SERVICE_ACCOUNT_EMAIL} \
     --role=roles/storage.objectViewer

۵. کار Cloud Run را ایجاد کنید

کار Cloud Run از 2 کانتینر استفاده می‌کند:

  • هماهنگ‌کننده اولاما - برای میزبانی اولاما و ارائه مدل جما ۳ ۴ب
  • pubsub-pull-msg - برای دریافت پیام از اشتراک pubsub و ارسال آن به کانتینر ollama-coordinator

ابتدا، کانتینر ollama-coordinator را ایجاد خواهید کرد.

  1. یک دایرکتوری والد برای codelab ایجاد کنید:
mkdir codelab-ollama-wp
cd codelab-ollama-wp
  1. Create a directory for the ollama-coordinator container
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}

۶. استقرار و اجرای کار

در این مرحله، با استقرار یک فایل yaml، کار Cloud Run را ایجاد خواهید کرد.

برای ایجاد فایل 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", ...

۷. تبریک می‌گویم!

تبریک می‌گویم که آزمایشگاه کد را تمام کردید!

توصیه می‌کنیم مستندات Cloud Run را بررسی کنید.

آنچه ما پوشش داده‌ایم

  • نحوه استفاده از استخرهای کاری Cloud Run با اشتراک Pub/Sub Pull
  • نحوه استفاده از Ollama برای انجام استنتاج به عنوان یک مخزن کارگر Cloud Run

۸. تمیز کردن

To avoid incurring charges to your Google Cloud account for the resources used in this tutorial, either delete the project that contains the resources, or keep the project and delete the individual resources.

حذف پروژه

ساده‌ترین راه برای حذف هزینه‌ها، حذف پروژه‌ای است که برای آموزش ایجاد کرده‌اید.

برای حذف پروژه:

  1. در کنسول گوگل کلود، به صفحه مدیریت منابع بروید.
  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. مخزن رجیستری Artifact را حذف کنید:
gcloud artifacts repositories delete ${AR_REPO_NAME} --location=${REGION} --quiet
  1. حذف حساب کاربری سرویس:
gcloud iam service-accounts delete ${SERVICE_ACCOUNT_EMAIL} --quiet

Cleaning up local files

برای پاک کردن فایل‌های محلی، موارد زیر را انجام دهید:

  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 را دنبال کنید.