Ollama: نشر الإنتاج

نشر الإنتاج هو القفزة النهائية للذكاء الاصطناعي المحلي من المختبر إلى ساحة المعركة — متاحًا بشكل عالٍ، وقابلًا للتوسع، ومقاومًا للكوارث.

💡 نصيحة: وكيل Nginx العكسي + موازنة الحمل هو البنية القياسية لنشر الإنتاج — Nginx يتولى إنهاء SSL ومصادقة API Key وتحديد المعدل وتوزيع الحركة، بينما تحتاج عقد Ollama فقط الربط بـ 127.0.0.1. لعنقات متعددة، استراتيجية least_conn (أقل اتصالات) مُوصى بها لأن مدة طلبات الاستنتاج تتفاوت بشكل كبير.

📋 المتطلبات المسبقة: يجب أن تتقن أولاً ما يلي

1. ما ستتعلمه


2. قصة حقيقية من رائدة أعمال SaaS

💡 نصيحة: في بيئات الإنتاج، يجب أن تستخدم موازنة حمل Nginx استراتيجية least_conn (أقل اتصالات) بدلاً من round_robin الافتراضي. مدة طلبات استنتاج Ollama تتفاوت بشكل كبير (الأسئلة القصيرة تستغرق ثانية واحدة، النصوص الطويلة تستغرق 30 ثانية)، وleast_conn يوزع الحمل بشكل أكثر تساويًا.

ℹ️ معلومة: Ollama لا يدعم وضع المجموعة بشكل أصلي (بدون مزامنة رئيسي-تابع، بدون استنتاج موزع). اتساق النماذج وارتباط الجلسة لمجموعات العقد المتعددة يجب تنفيذه في الطبقة العليا (Nginx/FastAPI). هذا ينطبق أيضًا على نشر Kubernetes.

(1) المشكلة: نقطة الفشل الواحدة تسبب انقطاع الخدمة الكلي

يعمل SupportBot الخاص بـ Alice على خادم Ollama واحد. صيانة الخادم أو OOM في GPU يتسبب في انقطاع الخدمة، مما يوقف نظام خدمة العملاء لمدة ساعتين ويؤثر على 500+ عميل.

(2) الحل: مجموعة عقد متعددة عالية التوافر

نشر مجموعة Ollama من 3 عقد + موازن حمل Nginx، مع تحويل تلقائي عند تعطل أي عقدة:

100%
flowchart TD
    A[موازن حمل Nginx] --> B[عقدة Ollama 1<br/>GPU 1]
    A --> C[عقدة Ollama 2<br/>GPU 2]
    A --> D[عقدة Ollama 3<br/>GPU 3]

3. تصميم بنية الإنتاج

⚠️ تحذير: في مجموعة العقد المتعددة، تدير كل عقدة Ollama نماذجها وحالتها بشكل مستقل — لا يوجد نسخ متماثل رئيسي-تابع كما في قواعد البيانات. النموذج المسحوب على العقدة A لن يتزامن تلقائيًا مع العقدة B. يجب ضمان اتساق النماذج عبر جميع العقد من خلال نص تهيئة موحد أو تخزين مشترك.

(1) عقدة واحدة مقابل عقد متعددة

البُعد عقدة واحدة مجموعة عقد متعددة
التوافر نقطة فشل واحدة تحمل N-1 أعطال
الإنتاجية محدودة توسع خطي
التكلفة منخفضة عالية (3x+)
تعقيد التشغيل منخفض متوسط
حالة الاستخدام تطوير/نطاق صغير الإنتاج

(2) نظرة عامة على بنية الإنتاج

100%
flowchart TD
    A[الإنترنت] --> B[WAF / CDN]
    B --> C[Nginx<br/>SSL + مصادقة + موازنة حمل]
    C --> D[عقدة Ollama 1<br/>GPU + نموذج]
    C --> E[عقدة Ollama 2<br/>GPU + نموذج]
    C --> F[عقدة Ollama 3<br/>GPU + نموذج]
    D --> G[NFS / S3<br/>تخزين نماذج مشترك]
    E --> G
    F --> G
    D --> H[مجموعة Chroma]
    E --> H
    F --> H
    I[Prometheus] --> D
    I --> E
    I --> F
    I --> J[لوحة Grafana]

(3) قائمة المكونات

المكون العدد المواصفات الغرض
موازن حمل Nginx 1 2 vCPU، 4 جيجابايت RAM موازنة الحمل + SSL
عقدة Ollama 3 8 vCPU، 16 جيجابايت RAM، 1x RTX 4090 استنتاج النموذج
Chroma 1 4 vCPU، 8 جيجابايت RAM، SSD التخزين المتجهي
NFS/S3 1 500 جيجابايت+ تخزين نماذج مشترك
Prometheus 1 2 vCPU، 4 جيجابايت RAM المراقبة

4. التوافر العالي

⚠️ ملاحظة: إعدادات شهادة SSL إلزامية للإنتاج — HTTPS لا يشفر النقل فحسب بل هو أيضًا شرط مسبق لمصادقة API Key (إرسال مفاتيح API بنص عادي عبر HTTP يعني عدم وجود أمان على الإطلاق). استخدم certbot لشهادات Let's Encrypt المجانية، أو Caddy لـ HTTPS التلقائي.

(1) فحوصات الصحة والتحويل

الآلية طريقة الفحص الفاصل المهلة
فحص Nginx السلبي وضع علامة غير متاح عند فشل الطلب كل طلب 5 ثوانٍ
فحص صحة نشط GET دوري /api/tags 10 ثوانٍ 3 ثوانٍ
فحص طبقة التطبيق اختبار استنتاج مخصص 30 ثانية 10 ثوانٍ

(1) ▶ مثال: إعدادات موازن حمل Nginx

NGINX
# /etc/nginx/conf.d/ollama-lb.conf

upstream ollama_cluster {
    least_conn;  # Route to least busy node

    server ollama-node1:11434 max_fails=3 fail_timeout=30s;
    server ollama-node2:11434 max_fails=3 fail_timeout=30s;
    server ollama-node3:11434 max_fails=3 fail_timeout=30s;
}

server {
    listen 443 ssl;
    server_name ai.example.com;

    ssl_certificate     /etc/ssl/certs/ai.example.com.crt;
    ssl_certificate_key /etc/ssl/private/ai.example.com.key;

    location /v1/ {
        # API Key authentication
        if ($http_x_api_key != "your-secret-key") {
            return 401 '{"error": "Unauthorized"}';
        }

        proxy_pass http://ollama_cluster;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_connect_timeout 5s;
        proxy_read_timeout 120s;

        # Rate limiting
        limit_req zone=api burst=20 nodelay;
    }

    # Health check endpoint
    location /health {
        proxy_pass http://ollama_cluster/api/tags;
    }
}

الإخراج:

TEXT
// Execution successful

(2) ▶ مثال: نص تحويل تلقائي

BASH
#!/bin/bash
# Ollama cluster health monitor

NODES=("ollama-node1:11434" "ollama-node2:11434" "ollama-node3:11434")
HEALTH_URL="/api/tags"
ALERT_EMAIL="ops@example.com"

check_node() {
    local node=$1
    if curl -sf --connect-timeout 3 "http://$node$HEALTH_URL" > /dev/null; then
        echo "UP"
    else
        echo "DOWN"
    fi
}

while true; do
    for node in "${NODES[@]}"; do
        status=$(check_node "$node")
        if [ "$status" = "DOWN" ]; then
            echo "ALERT: $node is DOWN at $(date)" >> /var/log/ollama_health.log
            # Send alert
            echo "Ollama node $node is DOWN" | mail -s "OLLAMA ALERT" "$ALERT_EMAIL" 2>/dev/null
        fi
    done
    sleep 10
done

الإخراج:

TEXT
NAME                    ID              SIZE    
llama3.2:latest        a80...          2.0 GB  
mistral:latest         61...           4.1 GB

5. استراتيجيات التوسع

(1) شروط تشغيل التوسع

المقياس عتبة التوسع للأعلى عتبة التوسع للأسفل وقت الانتظار
زمن انتقال الطلبات P95 > 5 ثوانٍ < 2 ثانية 5 دقائق
الاتصالات المتزامنة > 80% من السعة < 30% من السعة 5 دقائق
استخدام GPU > 85% < 40% 10 دقائق
طول الصف > 10 = 0 3 دقائق

(2) مقارنة طرق التوسع

الطريقة السرعة التكلفة التعقيد
توسع يدوي بطيء (ساعات) قابل للتحكم منخفض
نصوص تلقائية متوسط (دقائق) قابل للتحكم متوسط
K8s HPA سريع (ثوانٍ) تلقائي عالي

(3) ▶ مثال: توسع تلقائي قائم على زمن الانتقال

PYTHON
import subprocess
import time
from typing import Optional

class AutoScaler:
    def __init__(self, scale_up_threshold: float = 5.0,
                 scale_down_threshold: float = 2.0,
                 cooldown: int = 300):
        self.scale_up_threshold = scale_up_threshold
        self.scale_down_threshold = scale_down_threshold
        self.cooldown = cooldown
        self.last_scale_time = 0

    def get_avg_latency(self) -> Optional[float]:
        try:
            result = subprocess.run(
                ["curl", "-sf", "http://localhost:11434/api/chat", "-d",
                 '{"model":"qwen2.5","messages":[{"role":"user","content":"hi"}],"stream":false}'],
                capture_output=True, text=True, timeout=10
            )
            if result.returncode == 0:
                import json
                data = json.loads(result.stdout)
                duration_ns = data.get("total_duration", 0)
                return duration_ns / 1e9
        except Exception:
            pass
        return None

    def check_and_scale(self):
        latency = self.get_avg_latency()
        if latency is None:
            return

        now = time.time()
        if now - self.last_scale_time < self.cooldown:
            return

        if latency > self.scale_up_threshold:
            print(f"High latency ({latency:.1f}s), scaling up...")
            self._scale_up()
            self.last_scale_time = now
        elif latency < self.scale_down_threshold:
            print(f"Low latency ({latency:.1f}s), scaling down...")
            self._scale_down()
            self.last_scale_time = now

    def _scale_up(self):
        # Add new Ollama node (e.g., via Docker or cloud API)
        print("Adding Ollama node...")

    def _scale_down(self):
        # Remove idle Ollama node
        print("Removing idle Ollama node...")

# Usage
# scaler = AutoScaler()
# while True:
#     scaler.check_and_scale()
#     time.sleep(60)

الإخراج:

TEXT
Adding Ollama node...
Removing idle Ollama node...

6. نشر Kubernetes

(1) بنية نشر K8s

100%
flowchart TD
    A[Ingress<br/>nginx-ingress] --> B[Service<br/>ollama-svc]
    B --> C[Deployment<br/>ollama-deploy<br/>3 نسخ]
    C --> D[Pod 1<br/>GPU + نموذج]
    C --> E[Pod 2<br/>GPU + نموذج]
    C --> F[Pod 3<br/>GPU + نموذج]
    G[PVC<br/>model-storage] --> D
    G --> E
    G --> F

(2) إعدادات جدولة GPU الرئيسية

المورد الإعدادات الوصف
nvidia.com/gpu (requests) طلب المورد 1 GPU لكل Pod
nvidia.com/gpu (limits) حد المورد حد أقصى 1 GPU
nodeSelector اختيار عقدة GPU تحديد عقد GPU
tolerations تحمل تلوث GPU السماح بالجدولة على عقد GPU

(4) ▶ مثال: نشر Ollama على K8s

YAML
# ollama-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ollama
  labels:
    app: ollama
spec:
  replicas: 3
  selector:
    matchLabels:
      app: ollama
  template:
    metadata:
      labels:
        app: ollama
    spec:
      nodeSelector:
        gpu: "true"
      tolerations:
        - key: nvidia.com/gpu
          operator: Exists
          effect: NoSchedule
      containers:
        - name: ollama
          image: ollama/ollama
          ports:
            - containerPort: 11434
          resources:
            requests:
              nvidia.com/gpu: 1
              memory: "8Gi"
            limits:
              nvidia.com/gpu: 1
              memory: "16Gi"
          env:
            - name: OLLAMA_HOST
              value: "0.0.0.0:11434"
            - name: OLLAMA_NUM_PARALLEL
              value: "4"
            - name: OLLAMA_KEEP_ALIVE
              value: "30m"
          volumeMounts:
            - name: model-storage
              mountPath: /root/.ollama
          livenessProbe:
            httpGet:
              path: /api/tags
              port: 11434
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /api/tags
              port: 11434
            initialDelaySeconds: 10
            periodSeconds: 5
      volumes:
        - name: model-storage
          persistentVolumeClaim:
            claimName: ollama-models-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: ollama-svc
spec:
  selector:
    app: ollama
  ports:
    - port: 11434
      targetPort: 11434
  type: ClusterIP

الإخراج:

TEXT
K8s Deployment created successfully, Pods running normally

الإخراج:

TEXT
HPA auto-scaling configured successfully, cluster resource monitoring enabled

(5) ▶ مثال: توسع تلقائي HPA لـ K8s

YAML
# ollama-hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: ollama-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: ollama
  minReplicas: 2
  maxReplicas: 6
  metrics:
    - type: Resource
      resource:
        name: nvidia.com/gpu
        target:
          type: Utilization
          averageUtilization: 80
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
        - type: Pods
          value: 1
          periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Pods
          value: 1
          periodSeconds: 300

الإخراج:

TEXT
# HPA created successfully
kubectl get hpa ollama-hpa
# NAME          REFERENCE            TARGETS         MINPODS   MAXPODS   REPLICAS   AGE
# ollama-hpa   Deployment/ollama    45%/80%         2         6         3          5m

7. التعافي من الكوارث والاسترجاع

(1) استراتيجيات التعافي من الكوارث

الاستراتيجية RTO RPO التكلفة
نشط-سلبي 5-15 دقيقة 0 مضاعفة الأجهزة
نشط-نشط 0 (تحويل تلقائي) 0 3 أضعاف الأجهزة
استعداد بارد 1-4 ساعات مع فقدان 1x + تخزين

(2) البنية ككود

المورد طريقة إدارة الإصدارات
Modelfile مستودع Git
Docker Compose مستودع Git
K8s Manifests Git + ArgoCD
إعدادات Nginx مستودع Git
إصدارات النماذج وسوم Ollama + توثيق

8. مثال شامل: قائمة فحص نشر الإنتاج

PYTHON
# ============================================
# Comprehensive: Production deployment checklist
# Full verification before going live
# ============================================

PRODUCTION_CHECKLIST = {
    "infrastructure": [
        ("Ollama nodes: 3+ replicas running", "CRITICAL"),
        ("GPU drivers: installed and verified", "CRITICAL"),
        ("Model storage: shared NFS/S3 mounted", "CRITICAL"),
        ("Network: internal VLAN for Ollama traffic", "HIGH"),
        ("DNS: ai.example.com resolves to LB", "HIGH"),
    ],
    "security": [
        ("Ollama bound to 127.0.0.1 on each node", "CRITICAL"),
        ("Nginx SSL certificate valid", "CRITICAL"),
        ("API Key authentication enabled", "CRITICAL"),
        ("Rate limiting configured (60 req/min)", "HIGH"),
        ("WAF rules in place", "MEDIUM"),
    ],
    "high_availability": [
        ("Health checks configured (Nginx + app layer)", "CRITICAL"),
        ("Failover tested: kill 1 node, service continues", "CRITICAL"),
        ("Load balancer: least_conn algorithm", "HIGH"),
        ("Session affinity: disabled (stateless)", "HIGH"),
    ],
    "monitoring": [
        ("Prometheus scraping all nodes", "HIGH"),
        ("Grafana dashboards created", "HIGH"),
        ("Alert rules: GPU OOM, high latency, service down", "CRITICAL"),
        ("Log aggregation: Loki or ELK", "MEDIUM"),
    ],
    "disaster_recovery": [
        ("Config in Git (Modelfile, Compose, K8s)", "HIGH"),
        ("Model backup: GGUF files on S3/NFS", "HIGH"),
        ("Chroma data backup: daily snapshot", "HIGH"),
        ("Recovery drill: tested within last 30 days", "MEDIUM"),
    ],
    "performance": [
        ("Baseline benchmark recorded", "HIGH"),
        ("OLLAMA_NUM_PARALLEL configured", "HIGH"),
        ("OLLAMA_KEEP_ALIVE=30m set", "MEDIUM"),
        ("num_ctx per endpoint optimized", "MEDIUM"),
    ]
}

def run_checklist() -> str:
    report = ["# Production Deployment Checklist\n"]
    total = 0
    checked = 0

    for category, items in PRODUCTION_CHECKLIST.items():
        report.append(f"\n## {category.replace('_', ' ').title()}")
        for item, priority in items:
            total += 1
            report.append(f"- [ ] [{priority}] {item}")

    report.append(f"\n---\nTotal items: {total}")
    return "\n".join(report)

print(run_checklist())

❓ أسئلة شائعة

س ما هو الحد الأدنى لوحدات GPU لمجموعة 3 عقد؟
ج الحد الأدنى 3 (واحدة لكل عقدة). إذا كان الميزانية محدودة، إعداد عقدتين (1 رئيسية + 1 احتياطية) يعمل، لكن الإنتاجية تنخفض للنصف أثناء التحويل.
س هل كل آلة تحتاج سحب ملفات النماذج؟
ج إذا استخدمت تخزينًا مشتركًا (NFS)، اسحب مرة واحدة فقط. إذا استخدمت تخزينًا محليًا، كل آلة تحتاج السحب. NFS مشترك + تخزين مؤقت محلي مُوصى به.
س هل أختار Kubernetes أم Docker Compose؟
ج Docker Compose لـ < 5 عقد (أبسط). Kubernetes لـ > 5 عقد أو عند الحاجة للتوسع التلقائي. Docker Compose يكفي لمعظم السيناريوهات.
س كيف أختبر التحويل؟
ج أوقف عقدة Ollama يدويًا (docker stop أو systemctl stop)، وتحقق أن Nginx يوجه الحركة تلقائيًا إلى العقد الأخرى بدون تأثير على المستخدم.
س هل لدى Ollama وضع مجموعة مدمج؟
ج لا. Ollama خدمة أحادية المثيل؛ المجموعات تتطلب تركيبة موازن حمل خارجي. كل مثيل Ollama يعمل بشكل مستقل؛ Nginx يتولى توزيع الطلبات.
س كيف أدير إصدارات النماذج؟
ج استخدم Modelfile + Git لإدارة الإعدادات. ملفات النماذج تستخدم وسومًا (qwen2.5:v1.0) لتحديد الإصدار. في الإنتاج، استخدم وسومًا ثابتة، وليس :latest.

📖 ملخص


📝 تمارين

  1. أساسي (⭐): صمم خطة نشر إنتاج بآلة واحدة — Nginx + عقدة Ollama واحدة + Chroma. ارسم مخطط بنية واكتب إعدادات Docker Compose.
  2. متوسط (⭐⭐): إعداد مجموعة Ollama من عقدتين + موازن حمل Nginx، واختبر التحويل اليدوي.
  3. متقدم (⭐⭐⭐): اكتب وثيقة نشر إنتاج كاملة — مخطط بنية، إعدادات Docker Compose/K8s، تعزيز الأمان، المراقبة والتنبيه، خطة التعافي من الكوارث — وأكمل تمرين تحويل.
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%