Loading
Alireza Shokrani

Digital Architect

AI Solutions Engineer

Founder @ CoreBiz ERP

GenAI & RAG Specialist

Full-Stack Systems Developer

Alireza Shokrani

Digital Architect

AI Solutions Engineer

Founder @ CoreBiz ERP

GenAI & RAG Specialist

Full-Stack Systems Developer

Solution

زیرساخت بدون‌توقف: خطوط لوله استقرار کانتینری، پایش و محیط‌های Linux با دسترسی بالا

زیرساخت بدون‌توقف: خطوط لوله استقرار کانتینری، پایش و محیط‌های Linux با دسترسی بالا

زیرساخت مدرن به نقطه‌ای رسیده است که توقف یک انتخاب است، نه یک محدودیت. با این حال بسیاری از سیستم‌های تولیدی هنوز با پنجره‌های نگهداری مستقر می‌شوند، هنوز تحت بار بی‌صدا شکست می‌خورند و هنوز زمانی که یک کانتینر ساعت ۳ بامداد سقوط می‌کند، مداخله انسانی می‌طلبند.

دیسیپلین‌های مهندسی که این شکست‌ها را حذف می‌کنند، تثبیت شده‌اند — اما نیازمند تصمیمات معماری آگاهانه هستند:

  • خطوط لوله استقرار کانتینری که نمونه‌های در حال اجرا را بدون قطع یک اتصال جایگزین می‌کنند

  • Reverse Proxy خودکار که ترافیک را فقط به بک‌اندهای سالم مسیریابی می‌کند

  • Failover خودکار پشتیبان‌گیری که بدون مداخله انسانی از دست دادن داده بازیابی می‌شود

  • تلمتری فعال Uptime که تخریب را قبل از توجه کاربران تشخیص می‌دهد

این مقاله راهنمای مهندسی آن معماری است:

چگونه زیرساخت بدون‌توقف روی میزبانی مدرن بسازیم — جایی که استقرارها ایمن، شکست‌ها خود-شفا و دسترسی یک خاصیت قابل اندازه‌گیری سیستم است.

بخش ۱ — استقرار بدون‌توقف: الگوهای اصلی

بدون‌توقف در عمل سه چیز معنا می‌دهد: درخواست‌های موجود کامل می‌شوند، درخواست‌های جدید همیشه یک بک‌اند سالم پیدا می‌کنند و Rollbackها سریع هستند. معماری‌ای که این را ارائه می‌دهد روی استراتژی‌های اثبات‌شده استقرار ساخته شده است.

طیف استراتژی‌های استقرار

استراتژیالگوی ترافیکسرعت Rollbackهزینه منابعبهترین برای
Rolling Updateجایگزینی نمونه‌ها در دسته‌هامتوسط۱.۲۵xپیش‌فرض، سرویس‌های Stateless
Blue-Greenتغییر کل ترافیک به محیط جدیدخیلی سریع۲xاپ‌های حیاتی، Rollback فوری
Canaryمسیریابی درصد کمی به نسخه جدیدسریع۱x + Canaryکاهش ریسک، Rollout تدریجی

Rolling Update: انتخاب پیش‌فرض

Rolling Update نمونه‌ها را به صورت تدریجی جایگزین می‌کند و ظرفیت کامل را در طول استقرار حفظ می‌کند. برای سرویس‌های کانتینری، این رویکرد بومی است.

# Docker Compose با Rolling Update
deploy:
  replicas: 4
  update_config:
    order: start-first        # شروع جدید قبل از توقف قدیمی
    parallelism: 1            # یک Replica در هر زمان
    delay: 10s                # انتظار بین Replicaها
    failure_action: rollback  # Rollback خودکار در صورت شکست

اصل کلیدی: ترتیب start-first تضمین می‌کند که کانتینرهای جدید سالم هستند قبل از اینکه قدیمی‌ها حذف شوند. ترافیک هرگز به بک‌اند خالی نمی‌رسد.

Blue-Green: تضمین Rollback فوری

Blue-Green دو محیط یکسان را نگه می‌دارد. ترافیک فقط بعد از عبور محیط جدید از Health Check، به صورت اتمی تغییر می‌کند.

┌─────────────┐         ┌─────────────┐
│   آبی       │         │   سبز       │
│   (زنده)    │         │   (Staging) │
│   v1.0      │         │   v2.0      │
└──────┬──────┘         └──────┬──────┘
       │                       │
       │    Health Check       │
       │◀──────────────────────│
       │                       │
       ▼                       │
┌─────────────┐                │
│   ROUTER    │                │
│  (Nginx)    │                │
└──────┬──────┘                │
       │                       │
       └───────────┬───────────┘
                   │
                   ▼
            ┌─────────────┐
            │   ترافیک    │
            │   تغییر    │
            │   به سبز   │
            └─────────────┘

پیاده‌سازی: Reload Upstream در Nginx پس از موفقیت Health Check ترافیک را اتمی تغییر می‌دهد. Rollback تنها یک بازگشت پیکربندی است — بازیابی زیر یک ثانیه.

Canary: مواجهه ریسک کنترل‌شده

برای سیستم‌های پرترافیک، استقرارهای Canary درصد کوچکی از ترافیک واقعی را در معرض نسخه جدید قرار می‌دهند قبل از Rollout کامل.

# مسیریابی Canary در Nginx
upstream app {
    server 10.0.0.1:8080 weight=95;  # پایدار
    server 10.0.0.2:8080 weight=5;   # Canary
}

دروازه پایش: لغو در صورت افزایش ۲۰٪ تأخیر p95 یا فراتر رفتن نرخ خطا از ۰.۵٪.

بخش ۲ — ارکستراسیون کانتینری: Docker در تولید

Docker Compose اغلب به عنوان “فقط توسعه” رد می‌شود. با الگوهای درست، برای استقرارهای تک-سروری آماده تولید است.

الگوی Docker Compose تولیدی

# docker-compose-deploy.yml
services:
  app:
    image: ghcr.io/org/app:${VERSION}
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 30s
    depends_on:
      db:
        condition: service_healthy
    deploy:
      update_config:
        order: start-first
        failure_action: rollback

  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

اصول مهندسی:

  • Health Checkها آمادگی را تعریف می‌کنند — ترافیک فقط به کانتینرهای سالم مسیریابی می‌شود

  • ترتیب start-first جایگزینی بدون‌توقف را تضمین می‌کند

  • restart: unless-stopped خود-شفایی برای کانتینرهای سقوط‌کرده فراهم می‌کند

Kamal: جایگزین سبک

برای تیم‌هایی که استقرار Docker بدون‌توقف بدون پیچیدگی Kubernetes می‌خواهند، Kamal (توسط DHH/37signals) یک راه‌حل آماده تولید ارائه می‌دهد.

# config/deploy.yml
service: myapp
image: your-registry/myapp

servers:
  web:
    - 203.0.113.1

proxy:
  ssl: true
  host: example.com

registry:
  server: ghcr.io
  username: your-user
  password:
    - KAMAL_REGISTRY_PASSWORD

دستورات اصلی:

kamal setup      # اولین استقرار: نصب Docker، Proxy، استقرار App
kamal deploy     # استقرارهای بدون‌توقف بعدی
kamal rollback   # بازگشت به نسخه قبلی

Proxy داخلی Kamal به صورت خودکار SSL را از طریق Let’s Encrypt مدیریت می‌کند و Rolling Restart بدون توقف انجام می‌دهد.

یکپارچگی CI/CD: GitHub Actions

Pipelineهای استقرار خودکار، خطای انسانی را از انتشارها حذف می‌کنند.

# .github/workflows/deploy.yml
name: Deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Build و Push
        run: |
          docker build -t ghcr.io/${{ github.repository }}:${{ github.sha }} .
          docker push ghcr.io/${{ github.repository }}:${{ github.sha }}
      
      - name: استقرار از طریق SSH
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USER }}
          key: ${{ secrets.SSH_KEY }}
          script: |
            cd /opt/app
            export VERSION=${{ github.sha }}
            docker compose -f docker-compose-deploy.yml up -d

اصل مهندسی: Artifactهای تغییرناپذیر (Imageهای Docker نسخه‌بندی‌شده) «یک بار بساز، همه جا اجرا کن» را تضمین می‌کنند.

بخش ۳ — Reverse Proxy خودکار: Nginx و Traefik

Reverse Proxy کنترل‌کننده ترافیک است. باید فقط به بک‌اندهای سالم مسیریابی کند و ترافیک را در طول استقرار به صورت اتمی جابجا کند.

Nginx به عنوان Proxy آگاه از استقرار

upstream app {
    server 127.0.0.1:3100;  # آبی
    server 127.0.0.1:3101;  # سبز
    keepalive 32;
}

server {
    listen 443 ssl http2;
    server_name example.com;
    
    location / {
        proxy_pass http://app;
        proxy_next_upstream error timeout http_502 http_503;
        proxy_next_upstream_tries 3;
        
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

دستور کلیدی: proxy_next_upstream تضمین می‌کند که بک‌اندهای شکست‌خورده به صورت خودکار دور زده می‌شوند — ترافیک بدون مداخله دستی به نمونه‌های سالم مسیریابی می‌شود.

Traefik: Service Discovery خودکار

برای محیط‌های Docker، Traefik Reverse Proxy خودکار با Let’s Encrypt SSL ارائه می‌دهد.

# docker-compose.yml
services:
  traefik:
    image: traefik:v3
    command:
      - "--providers.docker=true"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "[email protected]"
      - "--certificatesresolvers.le.acme.storage=/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro

  app:
    image: ghcr.io/org/app:${VERSION}
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.app.rule=Host(`example.com`)"
      - "traefik.http.routers.app.tls.certresolver=le"
      - "traefik.http.services.app.loadbalancer.healthcheck.path=/health"

تأثیر مهندسی: افزودن یک سرویس جدید، آن را به صورت خودکار با Proxy ثبت می‌کند. گواهی‌های SSL بدون مداخله دستی صادر و تمدید می‌شوند.

بخش ۴ — پشتیبان‌گیری خودکار و Failover

از دست دادن داده، تنها شکستی است که با Redeployment قابل بازیابی نیست. خودکارسازی پشتیبان‌گیری اختیاری نیست.

استراتژی پشتیبان‌گیری پایگاه داده

# GitHub Actions: پشتیبان‌گیری زمان‌بندی‌شده PostgreSQL
name: پشتیبان‌گیری پایگاه داده
on:
  schedule:
    - cron: '0 2 * * *'  # روزانه ساعت ۲ بامداد

jobs:
  backup:
    runs-on: ubuntu-latest
    steps:
      - name: پشتیبان‌گیری از پایگاه داده
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USER }}
          key: ${{ secrets.SSH_KEY }}
          script: |
            TIMESTAMP=$(date +%Y%m%d_%H%M%S)
            docker exec postgres pg_dump -U postgres mydb | gzip > /backups/db_$TIMESTAMP.sql.gz
            
            # آپلود به ذخیره‌سازی خارج از سایت (S3، Backblaze و...)
            aws s3 cp /backups/db_$TIMESTAMP.sql.gz s3://backups-bucket/
            
            # نگهداری ۳۰ روز به صورت محلی
            find /backups -name "*.sql.gz" -mtime +30 -delete

اصول مهندسی:

  • خودکار، زمان‌بندی‌شده — نه دستی

  • ذخیره‌سازی خارج از سایت — پشتیبان‌های محلی با سرور می‌میرند

  • سیاست نگهداری — ۳۰ روز یک پیش‌فرض معقول است

معماری Failover

برای دسترسی بالا، معماری باید شکست مؤلفه را زنده بماند:

┌─────────────────────────────────────────────────────────────┐
│                    LOAD BALANCER                            │
│                    (Upstream با Health Check)               │
└──────────────┬──────────────────────────────┬───────────────┘
               │                              │
               ▼                              ▼
┌─────────────────────────┐    ┌─────────────────────────┐
│      سرور A             │    │      سرور B             │
│  ┌─────────────────┐    │    │  ┌─────────────────┐    │
│  │  کانتینر App    │    │    │  │  کانتینر App    │    │
│  └─────────────────┘    │    │  └─────────────────┘    │
│  ┌─────────────────┐    │    │  ┌─────────────────┐    │
│  │  Replica DB     │◀───┼────┼──│  Primary DB     │    │
│  └─────────────────┘    │    │  └─────────────────┘    │
└─────────────────────────┘    └─────────────────────────┘
               │                              │
               └──────────────┬───────────────┘
                              ▼
                    ┌─────────────────┐
                    │  ذخیره‌سازی    │
                    │  مشترک          │
                    │  (S3 / Volume)  │
                    └─────────────────┘

منطق Failover: اگر سرور A Health Check را رد کند، Load Balancer کل ترافیک را به سرور B مسیریابی می‌کند. پایگاه داده Replica را به Primary ارتقا می‌دهد.

بخش ۵ — تلمتری فعال Uptime

پایش درباره داشبوردها نیست. درباره تشخیص تخریب قبل از تجربه شکست توسط کاربران است.

استک تلمتری

لایهابزارهدف
متریک‌های کانتینرcAdvisorCPU، حافظه، I/O به ازای هر کانتینر
متریک‌های Nodenode-exporterاستفاده از منابع در سطح سیستم
متریک‌های اپلیکیشنکلاینت Prometheusمتریک‌های سفارشی کسب‌وکار/عملکرد
تصویرسازیGrafanaداشبوردها و هشدارهای آستانه
تجمیع لاگLoki + Promtailجستجوی لاگ متمرکز
Probing Uptimequptime / Uptime KumaHealth Checkهای خارجی

نقاط پایانی Health Check

هر سرویس باید یک نقطه پایانی سلامت ارائه دهد که آمادگی واقعی را منعکس کند:

# نقطه پایانی سلامت FastAPI
@app.get("/health")
async def health():
    # بررسی اتصال پایگاه داده
    db_ok = await check_db_connection()
    # بررسی اتصال Redis
    redis_ok = await check_redis_connection()
    # بررسی وابستگی‌های خارجی
    external_ok = await check_external_services()
    
    status = "healthy" if all([db_ok, redis_ok, external_ok]) else "degraded"
    return {
        "status": status,
        "checks": {
            "database": db_ok,
            "redis": redis_ok,
            "external": external_ok
        },
        "timestamp": datetime.utcnow().isoformat()
    }

اصل مهندسی: Health Checkها باید وابستگی‌ها را تأیید کنند، نه فقط زنده بودن فرآیند. یک کانتینر در حال اجرا که نمی‌تواند به پایگاه داده‌اش برسد، سالم نیست.

قوانین هشدار

# قوانین هشدار Prometheus
groups:
  - name: uptime
    rules:
      - alert: ServiceDown
        expr: up == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "سرویس {{ $labels.instance }} از کار افتاده"

      - alert: HighErrorRate
        expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.01
        for: 2m
        labels:
          severity: warning

      - alert: HighLatency
        expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 0.5
        for: 5m
        labels:
          severity: warning

فلسفه هشدار: برای علائم هشدار دهید (نرخ خطا، تأخیر)، نه علل (CPU بالا). یک سیستم تحت بار یک مشکل نیست — سیستمای که درخواست‌ها را رد می‌کند مشکل است.

تجمیع لاگ

# پیکربندی Loki + Promtail
services:
  loki:
    image: grafana/loki:latest
    command: -config.file=/etc/loki/local-config.yaml

  promtail:
    image: grafana/promtail:latest
    volumes:
      - /var/log:/var/log:ro
      - /var/lib/docker/containers:/var/lib/docker/containers:ro
    command: -config.file=/etc/promtail/config.yml

اصل مهندسی: لاگ‌های متمرکز، همبستگی بین سرویس‌ها را ممکن می‌کنند. یک خطای 500 در API و یک Timeout در پایگاه داده یک داستان هستند، نه دو.

بخش ۶ — معماری کامل زیرساخت

┌─────────────────────────────────────────────────────────────────────────────┐
│                              لایه لبه                                       │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │  DNS │ Load Balancer │ محافظت DDoS │ پایان‌دهی SSL                     ││
│  └─────────────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                         لایه Reverse Proxy                                  │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │  Nginx / Traefik │ مسیریابی آگاه از سلامت │ تغییر اتمی ترافیک          ││
│  └─────────────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                         لایه اپلیکیشن                                       │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐ │
│  │ App :3100   │  │ App :3101   │  │ App :3102   │  │ Canary :3200        │ │
│  │ (آبی)       │  │ (سبز)       │  │ (Replica)   │  │ (۵٪ ترافیک)         │ │
│  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                         لایه داده                                           │
│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────────────────┐  │
│  │ PostgreSQL      │  │ Redis           │  │ ذخیره‌سازی شیء (S3)         │  │
│  │ Primary+Replica │  │ Cache+Queue     │  │ پشتیبان+دارایی‌ها           │  │
│  └─────────────────┘  └─────────────────┘  └─────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                         لایه رصدپذیری                                       │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐ │
│  │ Prometheus  │  │ Grafana     │  │ Loki        │  │ quptime             │ │
│  │ (متریک‌ها)  │  │ (داشبوردها) │  │ (لاگ‌ها)    │  │ (Probeهای Uptime)   │ │
│  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘

قوانین معماری

قانونمنطق
هر استقرار اتمی استترافیک فقط بعد از تأیید سلامت جابجا می‌شود
هر کانتینر Health Check داردکانتینرهای ناسالم هرگز ترافیک دریافت نمی‌کنند
هر سرویس تغییرناپذیر استImageهای نسخه‌بندی‌شده، هرگز :latest
هر پشتیبان خودکار استپشتیبان‌های دستی پشتیبان نیستند
هر هشدار یک Runbook داردهشدارهای بدون رویه پاسخ، نویز هستند

بخش ۷ — چه زمانی زیرساخت بدون‌توقف بسازیم (و چه زمانی نه)

زیرساخت بدون‌توقف بسازید وقتی:

  • ✅ اپلیکیشن به کاربران واقعی با انتظارات Uptime خدمت می‌کند

  • ✅ استقرارها مکرراً اتفاق می‌افتند (روزانه، هفتگی)

  • ✅ از دست دادن داده غیرقابل قبول است (مالی، محتوای تولیدشده توسط کاربر)

  • ✅ بازیابی دستی برای الزامات کسب‌وکار خیلی کند است

  • ✅ تیم ظرفیت عملیاتی برای نگهداری پایش دارد

آن را نسازید وقتی:

  • ❌ اپلیکیشن یک نمونه اولیه یا ابزار داخلی است

  • ❌ فرکانس استقرار پایین است (ماهانه یا کمتر)

  • ❌ تیم تجربه عملیاتی ندارد (ریسک اضافه می‌کند)

  • ❌ هزینه پیچیدگی از هزینه توقف گاه‌به‌گاه فراتر می‌رود

بدون‌توقف یک سرمایه‌گذاری است. بازده خود را در قابلیت اطمینان و اعتماد عملیاتی می‌پردازد. سؤال این است که آیا اپلیکیشن این دیسیپلین را توجیه می‌کند.

نتیجه‌گیری — دسترسی به عنوان یک خاصیت معماری

زیرساخت بدون‌توقف با یک ابزار یا تکنیک واحد به دست نمی‌آید. یک خاصیت سیستم است — نتیجه تصمیمات آگاهانه در هر لایه:

  • استقرارها که نمونه‌ها را بدون قطع اتصالات جایگزین می‌کنند

  • Proxyهایی که فقط به بک‌اندهای سالم مسیریابی می‌کنند

  • پشتیبان‌هایی که داده را به صورت خودکار بازیابی می‌کنند

  • تلمتری که شکست را قبل از توجه کاربران تشخیص می‌دهد

این دیسیپلین مهندسی است که سیستم‌هایی که به کاربران خدمت می‌کنند را از سیستم‌هایی که آن‌ها را ناامید می‌کنند جدا می‌کند.

سؤال این نیست که آیا زیرساخت شما شکست می‌خورد. سؤال این است که آیا شکست یک حادثه است یا یک غیر-حادثه.

یادداشت نویسنده

این مقاله الگوهای زیرساختی توسعه‌یافته در هنگام مدیریت محیط‌های تولیدی Linux و استقرارهای کانتینری را بازتاب می‌دهد. برای همکاری در معماری بدون‌توقف، از طریق صفحه تماس با من در ارتباط باشید.

Tags:
Write a comment