أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

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

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول
DevOps بالعربي

Blue-Green على VM واحدة: نشر Node.js بدون downtime طويل

📅 ٢٦ أبريل ٢٠٢٦⏱ 5 دقائق قراءة
Blue-Green على VM واحدة: نشر Node.js بدون downtime طويل

Blue-Green على VM واحدة: نشر Node.js بدون downtime طويل

هتقلل توقف نشر Node.js من حوالي 45 ثانية إلى ثانية أو ثانيتين، بدون Kubernetes وبدون منصة deploy كبيرة. الفكرة: تشغل إصدارين على نفس الـ VM، وتخلي NGINX يحوّل الترافيك للإصدار السليم فقط.

مستوى القارئ: متوسط

المشكلة باختصار

الطريقة الشائعة على VM صغيرة هي: اسحب الكود، أوقف الخدمة، ابنِ النسخة الجديدة، شغّل الخدمة. الطريقة دي بتفشل لما عندك مستخدمين حقيقيين وقت النشر. أي طلب ييجي أثناء الإيقاف ياخد 502 أو timeout.

الافتراض هنا إن عندك تطبيق Node.js واحد، VM واحدة، NGINX قدامه، وحجم ترافيك متوسط مثل 20K إلى 80K زيارة يوميًا. مش محتاج cluster. محتاج فقط منفذين: واحد للإصدار الحالي وواحد للإصدار الجديد.

مخطط NGINX يحول الترافيك بين إصدار blue وإصدار green على VM واحدة بعد فحص الصحة

الفكرة بمثال بسيط

ركز في المثال ده. عندك مطعم بباب واحد للزبائن، ومطبخين خلف الباب. المطبخ الأزرق شغال الآن. أنت تجهز المطبخ الأخضر في الخلف، تختبر الأكل، ثم تقول للباب: وجّه الطلبات للمطبخ الأخضر. الزبون لم يرَ النقل.

ده بالظبط Blue-Green Deploy. الـ blue هو الإصدار الحالي على port 3001. الـ green هو الإصدار الجديد على port 3002. NGINX هو الباب. فحص /health هو اختبار إن المطبخ الجديد جاهز.

علميًا، Blue-Green Deploy هو نمط نشر يشغل بيئتين متشابهتين. واحدة تستقبل الترافيك، والثانية تتجهز وتُختبر. التحويل يتم بتغيير الـ routing ثم reload خفيف للـ proxy. في NGINX، reload يقرأ الإعداد الجديد بدون قتل الاتصالات الجارية غالبًا، بشرط إن الإعداد صحيح.

إعداد NGINX للتبديل بين الإصدارين

اعمل ملف upstream منفصل. ده يخلي سكربت النشر يغيّر سطر واحد بدل ما يلمس ملف NGINX كامل.

# /etc/nginx/conf.d/app-upstream.conf
upstream app_backend {
    server 127.0.0.1:3001;
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://app_backend;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

ابدأ بـ blue على 3001. الإصدار التالي يشتغل على 3002. بعد فحص الصحة، السكربت يبدّل upstream إلى 3002 ثم يعمل nginx -s reload.

خدمتان systemd بدل خدمة واحدة

بدل خدمة واحدة اسمها app.service، اعمل template واحد يخدم blue وgreen. المكسب إنك تقدر تشغل الإصدار الجديد بدون لمس القديم.

# /etc/systemd/system/myapp@.service
[Unit]
Description=MyApp %i instance
After=network.target

[Service]
WorkingDirectory=/opt/myapp/%i/current
Environment=NODE_ENV=production
EnvironmentFile=/opt/myapp/%i/.env
ExecStart=/usr/bin/node server.js
Restart=always
RestartSec=3
User=myapp
Group=myapp

[Install]
WantedBy=multi-user.target

شغّل blue مرة أولى:

Bash
sudo systemctl daemon-reload
sudo systemctl enable --now myapp@blue
curl -fsS http://127.0.0.1:3001/health

سكربت النشر العملي

السكربت التالي يفترض إن blue على 3001 وgreen على 3002. هو يعرف النشط من ملف صغير، يبني الإصدار الجديد في المجلد الآخر، يشغله، يفحص الصحة، ثم يبدّل NGINX.

Bash
#!/usr/bin/env bash
set -euo pipefail

ACTIVE_FILE=/opt/myapp/active-slot
ACTIVE=$(cat "$ACTIVE_FILE" 2>/dev/null || echo blue)
if [ "$ACTIVE" = "blue" ]; then
  NEXT=green
  NEXT_PORT=3002
else
  NEXT=blue
  NEXT_PORT=3001
fi

RELEASE_DIR="/opt/myapp/$NEXT/current"
REPO="git@github.com:company/myapp.git"

sudo -u myapp rm -rf "$RELEASE_DIR"
sudo -u myapp git clone --depth 1 "$REPO" "$RELEASE_DIR"
cd "$RELEASE_DIR"
sudo -u myapp npm ci --omit=dev

sudo systemctl restart "myapp@$NEXT"
for i in {1..20}; do
  if curl -fsS "http://127.0.0.1:$NEXT_PORT/health" >/dev/null; then
    break
  fi
  sleep 1
  if [ "$i" = 20 ]; then
    echo "health check failed for $NEXT"
    sudo journalctl -u "myapp@$NEXT" -n 80 --no-pager
    exit 1
  fi
done

sudo sed -i "s/server 127.0.0.1:[0-9]*;/server 127.0.0.1:$NEXT_PORT;/" /etc/nginx/conf.d/app-upstream.conf
sudo nginx -t
sudo nginx -s reload

echo "$NEXT" | sudo tee "$ACTIVE_FILE" >/dev/null
sudo systemctl stop "myapp@$ACTIVE" || true

echo "deployed $NEXT on port $NEXT_PORT"

سيناريو واقعي: تطبيق SaaS صغير عنده 50K زيارة يوميًا. النشر التقليدي كان يسبب 35 إلى 60 ثانية من 502 أثناء npm ci وrestart. بعد الفصل بين build والتبديل، التوقف العملي بقى زمن reload فقط، غالبًا أقل من ثانيتين في قياس داخلي بسيط.

رسم أعمدة يقارن توقف 45 ثانية في نشر إيقاف ثم تشغيل مقابل 1.2 ثانية في Blue Green Deploy

الـ trade-off هنا

المكسب واضح: نشر أسرع، rollback أبسط، وطلبات أقل تفشل وقت النشر. لو green فشل في /health، blue يفضل شغال.

التكلفة: استهلاك ذاكرة مضاعف وقت النشر. لو تطبيقك يستخدم 350MB RAM، هتحتاج تقريبًا 700MB أثناء فترة التبديل. كمان لازم تنتبه للـ migrations. أي migration تكسر التوافق بين الإصدارين ممكن تخلي blue يقع بعد ما green يغيّر شكل قاعدة البيانات.

أفضل طريقة مع قواعد البيانات: اجعل التغيير backward-compatible. أضف العمود أولًا، انشر الكود الذي يقرأ القديم والجديد، ثم احذف القديم في نشر لاحق. بدل ما تعمل rename مباشر في migration واحدة.

متى لا تستخدم هذه الطريقة

لا تستخدمها لو التطبيق stateful جدًا ويحفظ جلسات داخل الذاكرة فقط. انقل الجلسات إلى Redis أولًا. لا تستخدمها أيضًا لو عندك WebSocket طويل العمر وتحتاج draining محسوب؛ هنا تحتاج إعدادات graceful shutdown وتوقيت أطول قبل إيقاف الإصدار القديم.

ولو عندك أكثر من 3 أو 4 خدمات مترابطة، VM واحدة هتبقى عنق زجاجة. وقتها فكر في orchestrator أو منصة deploy تدعم health checks وrollbacks بشكل مركزي.

مصادر مهمة

  • توثيق NGINX لإشارات التحكم وreload.
  • توثيق systemd service units.
  • The Twelve-Factor App: processes.

الخطوة التالية

افتح إعداد NGINX الحالي عندك، وافصل الـ upstream في ملف مستقل. بعد كده شغّل نسخة ثانية من التطبيق على port مختلف، ولا تبدّل الترافيك إلا بعد ما curl /health يرجع نجاح 20 مرة متتالية.

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة