انتقل إلى المحتوى الرئيسي

إدارة عمليات PM2 لـ Node.js في الإنتاج

إدارة عملية PM2 لإنتاج Node.js: تكوين ملف النظام البيئي، وإعادة التشغيل بدون توقف، وإدارة السجل، والمراقبة، والتجميع، وسير عمل النشر.

E
ECOSIRE Research and Development Team
|19 مارس 202610 دقائق قراءة2.0k كلمات|

عندما يتعطل تطبيق Node.js الخاص بك في الساعة 2 صباحًا، فإن PM2 هو الفرق بين إعادة تشغيله تلقائيًا ورؤية المستخدمين لديك لصفحة فارغة حتى تستيقظ.

PM2 هو مدير عمليات تم اختباره في المعركة ويتعامل مع عمليات إعادة التشغيل التلقائية، والتجميع للاستخدام متعدد النواة، وتجميع السجلات، وعمليات النشر بدون توقف - كل ذلك باستخدام ملف تكوين واحد موجود في مستودعك.

يغطي هذا الدليل إعداد PM2 الإنتاجي الذي يدير 5 عمليات Node.js في وقت واحد: Next.js (الواجهة الأمامية)، وNestJS (API)، وDocusaurus (docs)، وموقعين للعلامات التجارية. تنطبق الأنماط بالتساوي على عمليات النشر ذات العملية الواحدة.

الوجبات الرئيسية

  • الملف ecosystem.config.cjs (CommonJS، وليس .js) يعمل مع كل من مشروعي ESModule وCommonJS
  • علامة --update-env مطلوبة عند إعادة التشغيل لالتقاط متغيرات البيئة الجديدة
  • لا تستخدم pm2 restart all مطلقًا بدون --update-env بعد تحديث .env.local
  • watch: false في الإنتاج - تؤدي مشاهدة الملفات إلى حلقات إعادة تشغيل لا حصر لها مع مخرجات البناء
  • max_memory_restart يوفر حماية تلقائية من تسرب الذاكرة دون إنهاء العملية بشكل دائم
  • node_args: '--max-old-space-size=4096' يمنع تعطل OOM في العمليات كثيفة الاستهلاك للذاكرة
  • تدور سجلات PM2 باستخدام الوحدة pm2-logrotate - قم بتثبيتها مباشرة بعد PM2 نفسها
  • pm2 save وpm2 startup يستمران في قائمة العمليات الخاصة بك عبر عمليات إعادة تشغيل الخادم

التثبيت

# Install PM2 globally
npm install -g pm2

# Install the log rotation module immediately
pm2 install pm2-logrotate

# Configure log rotation
pm2 set pm2-logrotate:max_size 50M
pm2 set pm2-logrotate:retain 7
pm2 set pm2-logrotate:compress true
pm2 set pm2-logrotate:dateFormat YYYY-MM-DD

ملف تكوين النظام البيئي

يحدد الملف ecosystem.config.cjs (تنسيق CommonJS للعمل مع مشروعي ESM وCJS) جميع عملياتك:

// ecosystem.config.cjs
module.exports = {
  apps: [
    // ─── Next.js Frontend ────────────────────────────────────────────
    {
      name: 'ecosire-web',
      script: 'node_modules/.bin/next',
      args: 'start',
      cwd: '/opt/ecosire/app/apps/web',
      instances: 1,        // Single instance — Next.js handles its own multi-threading
      exec_mode: 'fork',
      env: {
        NODE_ENV: 'production',
        PORT: 3000,
      },
      // Memory management
      max_memory_restart: '1G',
      node_args: '--max-old-space-size=1024',
      // Logging
      out_file: '/var/log/pm2/ecosire-web.out.log',
      error_file: '/var/log/pm2/ecosire-web.err.log',
      merge_logs: true,
      log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
      // Restart behavior
      watch: false,
      restart_delay: 3000,
      max_restarts: 10,
      min_uptime: '30s',   // Must stay up 30s to count as successful start
      autorestart: true,
      // Graceful shutdown
      kill_timeout: 30000, // 30 seconds to shut down gracefully
      wait_ready: true,    // Wait for process.send('ready')
      listen_timeout: 60000,
    },

    // ─── NestJS API ──────────────────────────────────────────────────
    {
      name: 'ecosire-api',
      script: 'dist/main.js',
      cwd: '/opt/ecosire/app/apps/api',
      instances: 2,         // Cluster mode for multi-core utilization
      exec_mode: 'cluster',
      env: {
        NODE_ENV: 'production',
        PORT: 3001,
      },
      max_memory_restart: '512M',
      node_args: '--max-old-space-size=512',
      out_file: '/var/log/pm2/ecosire-api.out.log',
      error_file: '/var/log/pm2/ecosire-api.err.log',
      merge_logs: true,
      log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
      watch: false,
      restart_delay: 2000,
      max_restarts: 10,
      min_uptime: '20s',
      autorestart: true,
      kill_timeout: 15000,
      // Graceful cluster reload support
      listen_timeout: 30000,
    },

    // ─── Docusaurus Docs ─────────────────────────────────────────────
    {
      name: 'ecosire-docs',
      script: 'node_modules/.bin/docusaurus',
      args: 'serve',
      cwd: '/opt/ecosire/app/apps/docs',
      instances: 1,
      exec_mode: 'fork',
      env: {
        NODE_ENV: 'production',
        PORT: 3002,
      },
      max_memory_restart: '256M',
      out_file: '/var/log/pm2/ecosire-docs.out.log',
      error_file: '/var/log/pm2/ecosire-docs.err.log',
      merge_logs: true,
      log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
      watch: false,
      restart_delay: 3000,
      max_restarts: 5,
      min_uptime: '30s',
      autorestart: true,
      kill_timeout: 10000,
    },

    // ─── Brand Site: Odovation ───────────────────────────────────────
    {
      name: 'odovation-web',
      script: 'node_modules/.bin/next',
      args: 'start',
      cwd: '/opt/ecosire/app/apps/odovation',
      instances: 1,
      exec_mode: 'fork',
      env: {
        NODE_ENV: 'production',
        PORT: 3010,
      },
      max_memory_restart: '512M',
      out_file: '/var/log/pm2/odovation-web.out.log',
      error_file: '/var/log/pm2/odovation-web.err.log',
      merge_logs: true,
      log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
      watch: false,
      restart_delay: 3000,
      max_restarts: 10,
      min_uptime: '30s',
      autorestart: true,
    },

    // ─── Brand Site: MuhammadAmir ────────────────────────────────────
    {
      name: 'muhammadamir-web',
      script: 'node_modules/.bin/next',
      args: 'start',
      cwd: '/opt/ecosire/app/apps/muhammadamir',
      instances: 1,
      exec_mode: 'fork',
      env: {
        NODE_ENV: 'production',
        PORT: 3020,
      },
      max_memory_restart: '512M',
      out_file: '/var/log/pm2/muhammadamir-web.out.log',
      error_file: '/var/log/pm2/muhammadamir-web.err.log',
      merge_logs: true,
      log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
      watch: false,
      restart_delay: 3000,
      max_restarts: 10,
      min_uptime: '30s',
      autorestart: true,
    },
  ],
};

أوامر PM2 الأساسية

# Start all processes from ecosystem file
pm2 start ecosystem.config.cjs

# Restart all (with updated environment variables)
pm2 restart ecosystem.config.cjs --update-env

# Graceful reload (zero-downtime for cluster mode)
pm2 reload ecosystem.config.cjs

# Stop all processes
pm2 stop all

# Delete all processes from PM2 registry
pm2 delete all

# Individual process management
pm2 restart ecosire-api
pm2 stop ecosire-docs
pm2 logs ecosire-web --lines 100

# Real-time monitoring dashboard
pm2 monit

# Status overview
pm2 status
pm2 list

بدء التشغيل عند إعادة تشغيل الخادم

بدون تكوين بدء التشغيل، سيتم فقدان جميع عمليات PM2 عند إعادة تشغيل الخادم:

# Generate and install the startup script for your init system
pm2 startup
# Copy the output command and run it (it looks like:)
# sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u ubuntu --hp /home/ubuntu

# Save the current process list
pm2 save
# This creates ~/.pm2/dump.pm2 — processes are restored on reboot

# Verify startup works
pm2 resurrect  # Manually restore from dump.pm2

في كل مرة تقوم فيها بإضافة أو إزالة العمليات، قم بتشغيل pm2 save مرة أخرى لتحديث ملف التفريغ.


عمليات النشر بدون توقف

بالنسبة إلى NestJS في وضع المجموعة، يدعم PM2 عمليات إعادة التحميل الحقيقية بدون توقف:

# Reload restarts workers one at a time (zero-downtime)
# Old workers handle requests while new ones start
pm2 reload ecosire-api

# vs restart — kills all workers simultaneously (brief downtime)
pm2 restart ecosire-api

بالنسبة إلى Next.js (الذي يعمل في وضع التفرع، مثيل واحد)، يتطلب وقت التوقف الصفري أسلوبًا مختلفًا. استخدم التكوين wait_ready + listen_timeout مع إشارة بدء التشغيل من تطبيقك:

// apps/web — this is handled automatically by Next.js
// But for NestJS, send the ready signal explicitly:

// apps/api/src/main.ts
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3001);

  // Signal PM2 that the process is ready
  if (process.send) {
    process.send('ready');
  }
}

bootstrap();

إدارة السجل

يمكن أن تملأ سجلات PM2 القرص الخاص بك إذا لم تتم إدارتها. تكوين دوران السجل على الفور:

# Install log rotation module
pm2 install pm2-logrotate

# Configuration
pm2 set pm2-logrotate:max_size 50M       # Rotate when log reaches 50MB
pm2 set pm2-logrotate:retain 7           # Keep 7 days of logs
pm2 set pm2-logrotate:compress true      # Gzip rotated logs
pm2 set pm2-logrotate:dateFormat YYYY-MM-DD
pm2 set pm2-logrotate:workerInterval 30  # Check rotation interval (seconds)
pm2 set pm2-logrotate:rotateInterval '0 0 * * *'  # Daily at midnight

أوامر السجل المفيدة:

# View all logs combined
pm2 logs

# View specific process logs
pm2 logs ecosire-api

# View with timestamps
pm2 logs --timestamp

# Flush all log files
pm2 flush

# Tail error logs only
pm2 logs ecosire-api --err --lines 200

المراقبة والمقاييس

يوفر PM2 Plus (المعروف سابقًا باسم Keymetrics) مراقبة قائمة على السحابة. للمراقبة ذاتية الاستضافة:

# Built-in terminal dashboard
pm2 monit

# Get JSON status for scripting/monitoring integration
pm2 jlist    # JSON process list
pm2 prettylist  # Formatted process list

# Integrate with your monitoring stack
pm2 set pm2-server-monit:interval 5  # Metrics collection interval

لمراقبة الإنتاج، قم بكشف مقاييس PM2 إلى Prometheus:

npm install -g pm2-prometheus-exporter
pm2 set pm2-prometheus-exporter:port 9209

# Scrape in Prometheus config:
# - job_name: pm2
#   static_configs:
#     - targets: ['localhost:9209']

تكامل البرنامج النصي للنشر

تسلسل النشر النموذجي:

#!/bin/bash
# scripts/deploy-production.sh

set -e

echo "=== Starting deployment ==="

# 1. Pull latest code
git pull origin main

# 2. Install dependencies
pnpm install --frozen-lockfile

# 3. Build all apps (with Turbo remote cache)
TURBO_TOKEN="$TURBO_TOKEN" TURBO_TEAM="$TURBO_TEAM" \
  npx turbo run build

# 4. Run database migrations
pnpm --filter @ecosire/db db:migrate

# 5. Restart PM2 processes
# --update-env picks up changes in .env.local
pm2 restart ecosystem.config.cjs --update-env

# 6. Wait for processes to stabilize
sleep 10

# 7. Health checks
curl -f https://ecosire.com/ -o /dev/null -s || {
  echo "Web health check failed — rolling back"
  git revert HEAD --no-edit
  pm2 restart ecosystem.config.cjs --update-env
  exit 1
}

curl -f https://api.ecosire.com/api/health -o /dev/null -s || {
  echo "API health check failed — rolling back"
  git revert HEAD --no-edit
  pm2 restart ecosystem.config.cjs --update-env
  exit 1
}

# 8. Save process state
pm2 save

echo "=== Deployment complete ==="

المخاطر والحلول الشائعة

المأزق 1: نسيان --update-env

بعد تحديث .env.local، يؤدي تشغيل pm2 restart all بدون --update-env إلى إعادة تشغيل العمليات باستخدام متغيرات البيئة القديمة. استخدم دائمًا pm2 restart ecosystem.config.cjs --update-env.

المأزق 2: استخدام watch: true في الإنتاج

watch: true يعيد تشغيل العملية عند تغيير أي ملف. في الإنتاج، تتغير مخرجات البناء في كل عملية نشر - وهذا يؤدي إلى حلقات إعادة تشغيل لا حصر لها. قم دائمًا بتعيين watch: false.

المأزق 3: عدم التعامل مع SIGTERM لإيقاف التشغيل بسلاسة

يرسل PM2 SIGTERM عند إعادة التشغيل/الإيقاف. إذا لم يتعامل تطبيقك مع هذه المشكلة، فإن PM2 ينتظر kill_timeout مللي ثانية ويرسل SIGKILL - مما قد يتسبب في فقدان الطلبات. التعامل مع SIGTERM في NestJS:

// main.ts
const app = await NestFactory.create(AppModule);
await app.listen(3001);

// Graceful shutdown
process.on('SIGTERM', async () => {
  await app.close();
  process.exit(0);
});

المأزق 4: نفاد مساحة قرص سجل PM2

بدون pm2-logrotate، تنمو سجلات PM2 إلى أجل غير مسمى. يمكن لواجهة برمجة التطبيقات (API) التي يتم الاتجار بها بكثافة إنشاء غيغابايت من السجلات يوميًا. قم بتثبيت pm2-logrotate على الفور وقم بتعيين max_size (50 ميجابايت) وretain (7 أيام) بشكل معقول.

المأزق 5: فقدان العمليات بعد إعادة التشغيل

pm2 start لا يستمر في العمليات عبر عمليات إعادة التشغيل. قم دائمًا بتشغيل pm2 startup + pm2 save بعد الإعداد الأولي. إذا اختفت العمليات بعد إعادة التشغيل، قم بتشغيل pm2 resurrect للاستعادة من التفريغ المحفوظ.


الأسئلة المتداولة

متى يجب علي استخدام وضع المجموعة مقابل وضع الشوكة؟

استخدم وضع المجموعة لأحمال العمل المرتبطة بوحدة المعالجة المركزية (واجهات برمجة تطبيقات NestJS ذات العمليات الحسابية الثقيلة ومعالجة البيانات). يعمل الوضع العنقودي على توليد العمليات المنفذة instances وأرصدة تحميل PM2 فيما بينها - مع الاستفادة من جميع مراكز وحدة المعالجة المركزية. استخدم وضع التفرع لأحمال العمل المرتبطة بالإدخال/الإخراج (Next.js، خدمة الملفات الثابتة) أو عندما لا تدعم العملية التجميع (البرامج النصية ذات الترابط الواحد، خدمة Docusaurus). يتعامل Next.js مع مؤشرات الترابط العاملة الخاصة به داخليًا، لذا فإن وضع الشوكة مع instances: 1 هو الصحيح.

كيف يمكنني تشغيل PM2 في حاوية Docker؟

يستخدم PM2 في Docker pm2-docker (أو pm2-runtime) بدلاً من pm2 للتعامل مع الإشارات بشكل صحيح. لا يتم إخفاء إصدار وقت التشغيل (مما قد يتسبب في خروج Docker)، ويعيد توجيه الإشارات بشكل صحيح إلى العمليات الفرعية، ويسجل الدخول إلى stdout/stderr بدلاً من الملفات. استخدم CMD ["pm2-runtime", "ecosystem.config.cjs"] في ملف Dockerfile الخاص بك.

كيف يمكنني مراقبة عمليات PM2 من جهاز بعيد؟

توفر PM2 Plus (الخدمة السحابية للدفع لكل عملية) لوحة معلومات ويب. بالنسبة للمراقبة ذاتية الاستضافة، قم بكشف مقاييس PM2 عبر مصدر Prometheus وتصورها في Grafana. للتحقق من الحالة البسيطة، يمكنك SSH وتشغيل pm2 status، أو الكشف عن المقاييس عبر نقطة نهاية HTTP التي يستقصيها نظام المراقبة الخاص بك.

ما الفرق بين إعادة تحميل PM2 وإعادة تشغيل PM2؟

يقوم pm2 restart بقتل جميع العمال في وقت واحد وإعادة تشغيلهم - هناك فترة قصيرة لا يوجد فيها عمال قيد التشغيل (وقت التوقف عن العمل). pm2 reload رائع: فهو يبدأ عمالًا جددًا، وينتظرهم حتى يصبحوا جاهزين، ثم يوقف تشغيل العمال القدامى - بدون أي توقف عن العمل. استخدم pm2 reload لعمليات نشر الإنتاج. ملاحظة: إعادة التحميل تعمل بشكل صحيح فقط في وضع المجموعة؛ يعود وضع الشوكة إلى إعادة تشغيل السلوك.

كيف أقوم بتعيين متغيرات بيئة مختلفة لعمليات مختلفة؟

كل عملية في ecosystem.config.cjs لها أقسام env وenv_production الخاصة بها. يتم استخدام القسم env_production عند تمرير --env production إلى أوامر PM2. بالنسبة للأسرار، لا تضعها أبدًا مباشرة في ملف النظام البيئي - ضعها في بيئة النظام أو ملف .env.local ودع PM2 يرثها. تضمن العلامة --update-env أن يقوم PM2 بإعادة قراءة متغيرات البيئة عند إعادة التشغيل.


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

يعد PM2 جزءًا أساسيًا من أي نشر لـ Node.js للإنتاج. يدير ECOSIRE 5 عمليات PM2 في الإنتاج — Next.js، وNestJS، وDocusaurus، وموقعين للعلامات التجارية — مع عمليات إعادة التشغيل التلقائية، وتدوير السجل، وعمليات النشر بدون توقف في كل مرة يتم فيها الضغط على المفتاح الرئيسي.

سواء كنت بحاجة إلى دعم DevOps الهندسي، أو بنية نشر الإنتاج، أو المساعدة في الترحيل إلى إعداد حاوية، استكشف خدماتنا لمعرفة كيف يمكننا مساعدتك.

E

بقلم

ECOSIRE Research and Development Team

الكتابة التقنية

يغطي فريق الكتابة التقنية في ECOSIRE موضوعات Odoo ERP والتجارة الإلكترونية عبر Shopify ووكلاء الذكاء الاصطناعي وتحليلات Power BI والأتمتة عبر GoHighLevel وأفضل ممارسات برمجيات المؤسسات. تساعد أدلتنا الشركات على اتخاذ قرارات تقنية مدروسة.

ECOSIRE

قم بتنمية أعمالك مع ECOSIRE

حلول المؤسسات عبر تخطيط موارد المؤسسات (ERP) والتجارة الإلكترونية والذكاء الاصطناعي والتحليلات والأتمتة.