زیرساخت بدونتوقف: خطوط لوله استقرار کانتینری، پایش و محیطهای 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
پایش درباره داشبوردها نیست. درباره تشخیص تخریب قبل از تجربه شکست توسط کاربران است.
استک تلمتری
| لایه | ابزار | هدف |
|---|---|---|
| متریکهای کانتینر | cAdvisor | CPU، حافظه، I/O به ازای هر کانتینر |
| متریکهای Node | node-exporter | استفاده از منابع در سطح سیستم |
| متریکهای اپلیکیشن | کلاینت Prometheus | متریکهای سفارشی کسبوکار/عملکرد |
| تصویرسازی | Grafana | داشبوردها و هشدارهای آستانه |
| تجمیع لاگ | Loki + Promtail | جستجوی لاگ متمرکز |
| Probing Uptime | quptime / Uptime Kuma | Health 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 و استقرارهای کانتینری را بازتاب میدهد. برای همکاری در معماری بدونتوقف، از طریق صفحه تماس با من در ارتباط باشید.