كيفية تشغيل تطبيق Node.js باستخدام PM2 خلف Nginx

كيفية تشغيل تطبيق Node.js باستخدام PM2 خلف Nginx

لتشغيل تطبيق Node.js في بيئة الإنتاج على Ubuntu، ثبّت إصدار Node.js LTS من NodeSource. بعد ذلك شغّل تطبيقك باستخدام PM2 حتى يُعاد تشغيله تلقائيًا عند تعطّله وعند إعادة تشغيل السيرفر (pm2 start، pm2 save، pm2 startup). ثم ضع Nginx في الواجهة كوكيل عكسي (reverse proxy) يمرّر الطلبات إلى 127.0.0.1:3000 ويقدّم HTTPS بشهادة Let's Encrypt مجانية. يشرح هذا الدليل كل خطوة على VPS سحابي أو سيرفر مخصص من Cloudesta يعمل بنظام Ubuntu 24.04 (وينطبق أيضًا على 22.04). كما يتناول متغيرات البيئة والسجلات والنشر دون توقف وأكثر الأخطاء شيوعًا.

المتطلبات الأساسية

  • سيرفر يعمل بنظام Ubuntu 24.04 أو 22.04 مع إمكانية الوصول عبر SSH بحساب root أو بصلاحيات sudo، مثل VPS سحابي من Cloudesta.
  • نطاق (مثل example.com) له سجل A يشير إلى عنوان IP الخاص بالسيرفر (مثل 203.0.113.10).
  • كود تطبيقك، بما في ذلك package.json وpackage-lock.json.
  • مستخدم غير root لديه صلاحيات sudo. سيشغّل PM2 التطبيق باسم هذا المستخدم.
على الاستضافة المشتركة بلوحة cPanel لا يمكنك تثبيت PM2 أو تعديل إعدادات Nginx. استخدم cPanel » Software » Setup Node.js App بدلًا من ذلك. هذا الدليل مخصص لسيرفرات VPS والسيرفرات المخصصة.

1. كيف أثبّت Node.js LTS على Ubuntu؟

غالبًا ما تكون حزمة nodejs الموجودة في مستودعات Ubuntu نفسها متأخرة بعدة إصدارات، لذا ثبّت Node.js من مستودع NodeSource. يتتبّع السكربت setup_lts.x خط إصدار LTS الحالي. لتثبيت إصدار رئيسي محدد، استخدم بدلًا منه سكربتًا مثل setup_22.x.

sudo apt update
sudo apt install -y curl ca-certificates
curl -fsSL https://deb.nodesource.com/setup_lts.x -o nodesource_setup.sh
sudo -E bash nodesource_setup.sh
sudo apt install -y nodejs
node -v
npm -v

2. كيف أنشر التطبيق في /var/www/app؟

أنشئ المجلد واجعل مستخدمك مالكًا له. ثم استنسخ كودك إليه أو ارفعه.

sudo mkdir -p /var/www/app
sudo chown -R $USER:$USER /var/www/app
cd /var/www/app
git clone https://github.com/youruser/yourrepo.git .
npm ci --omit=dev

يثبّت الأمر npm ci الإصدارات المحددة بالضبط في package-lock.json، ويفشل إذا كان هذا الملف غير موجود. وهذا يجعل عمليات النشر قابلة للتكرار بنفس النتيجة. إذا كان لتطبيقك خطوة بناء، كما في TypeScript أو Next.js، فشغّل npm ci بدون خيارات ثم npm run build بدلًا من ذلك.

تأكد من أن التطبيق يستمع على localhost فقط، إذ سيتولى Nginx التعامل مع الترافيك العام. على سبيل المثال، في Express:

const port = process.env.PORT || 3000;
app.listen(port, '127.0.0.1', () => console.log(`Listening on ${port}`));

3. كيف أُبقي تطبيق Node.js يعمل باستمرار باستخدام PM2؟

ثبّت PM2 على مستوى النظام (globally) وشغّل التطبيق باسم واضح:

sudo npm install -g pm2
cd /var/www/app
pm2 start app.js --name app
pm2 status

لجعل PM2 يبدأ تلقائيًا عند الإقلاع، أنشئ وحدة systemd. شغّل الأمر التالي بمستخدمك العادي، وليس باستخدام sudo:

pm2 startup systemd

سيطبع PM2 سطرًا يبدأ بـ sudo env PATH=.... انسخ هذا السطر كما هو تمامًا وشغّله. سيكون مشابهًا لما يلي:

sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u youruser --hp /home/youruser

بعد ذلك، احفظ قائمة العمليات الحالية، إذ يستعيد PM2 هذه القائمة عند الإقلاع:

pm2 save
شغّل pm2 save مرة أخرى في كل مرة تضيف فيها تطبيقات أو تحذفها، فـ PM2 لا يستعيد إلا ما تم حفظه.

4. كيف أضبط متغيرات البيئة لتطبيق يعمل عبر PM2؟

الخيار الأنظف هو ملف ecosystem الخاص بـ PM2. أنشئ الملف /var/www/app/ecosystem.config.js:

module.exports = {
  apps: [{
    name: 'app',
    script: 'app.js',
    cwd: '/var/www/app',
    exec_mode: 'cluster',
    instances: 2,
    env_production: {
      NODE_ENV: 'production',
      PORT: 3000
    }
  }]
};

استبدل العملية التي شغّلتها سابقًا بعملية تستخدم هذا الملف:

pm2 delete app
pm2 start ecosystem.config.js --env production
pm2 save

بالنسبة للبيانات السرية مثل مفاتيح API، يمكنك بدلًا من ذلك الاحتفاظ بملف .env وتحميله داخل كودك. ثبّت أداة التحميل باستخدام npm install dotenv، ثم أضف require('dotenv').config() في أول سطر من app.js. قيّد صلاحيات الملف باستخدام chmod 600 .env ولا تضفه إلى git أبدًا. بعد تغيير المتغيرات، شغّل pm2 restart app --update-env ليقرأ التطبيق القيم الجديدة.

5. كيف أضبط Nginx كوكيل عكسي (reverse proxy) لتطبيق Node.js؟

ثبّت Nginx واسمح بترافيك الويب عبر الجدار الناري. اسمح بـ OpenSSH أولًا حتى لا تمنع نفسك من الوصول إلى السيرفر:

sudo apt install -y nginx
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable

أنشئ الملف /etc/nginx/sites-available/app. كتلة map وترويستا Upgrade/Connection هي ما يجعل WebSockets تعمل:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        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;
        proxy_read_timeout 300s;
    }
}

فعّل الموقع، وعطّل الموقع الافتراضي، واختبر الإعدادات، ثم أعد تحميل Nginx:

sudo ln -s /etc/nginx/sites-available/app /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx

إذا كنت تستخدم Express، فأضف app.set('trust proxy', 1) ليرى التطبيق عناوين IP الحقيقية للعملاء والبروتوكول الصحيح خلف Nginx.

6. كيف أضيف شهادة SSL مجانية من Let's Encrypt؟

بمجرد أن تشير سجلات DNS إلى السيرفر، شغّل:

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
sudo certbot renew --dry-run

يضيف Certbot إعدادات HTTPS إلى كتلة server الخاصة بك، ويضبط إعادة التوجيه من HTTP إلى HTTPS، ويثبّت مؤقّت systemd يجدّد الشهادة تلقائيًا.

7. كيف أعرض السجلات وأنشر التحديثات دون توقف؟

اعرض السجلات المباشرة، أو آخر 100 سطر، باستخدام:

pm2 logs app
pm2 logs app --lines 100

تكبر ملفات سجلات PM2 بلا حدود ما لم تقم بتدويرها. ثبّت وحدة التدوير مرة واحدة:

pm2 install pm2-logrotate

تبدو عملية النشر المعتادة كما يلي:

cd /var/www/app
git pull
npm ci --omit=dev
pm2 reload ecosystem.config.js --env production

لا يحقق pm2 reload التحديث دون توقف إلا في وضع cluster (exec_mode: 'cluster' مع نسختين أو أكثر)، لأنه يعيد تشغيل العمليات العاملة (workers) واحدة تلو الأخرى. أما في وضع fork، فيتصرف reload مثل pm2 restart ويسبب انقطاعًا قصيرًا.

التحقق من أن كل شيء يعمل

pm2 status
curl -I http://127.0.0.1:3000
curl -I https://example.com
systemctl status pm2-$USER

تأكد من نجاح الأوامر الأربعة جميعها:

  • يعرض pm2 status التطبيق بحالة online.
  • يعيد أمرا curl كلاهما رمز حالة HTTP مثل 200.
  • تظهر وحدة systemd بحالة enabled.

أخيرًا، شغّل sudo reboot. بعد عودة السيرفر للعمل، تأكد من أن pm2 status يعرض التطبيق مجددًا دون أن تشغّله يدويًا.

مشكلات شائعة

Error: listen EADDRINUSE :::3000

هناك عملية أخرى تحجز المنفذ بالفعل، وغالبًا ما تكون نسخة قديمة من التطبيق تم تشغيلها باستخدام node مباشرةً. اعثر عليها باستخدام sudo ss -ltnp | grep :3000، وأوقفها، ثم شغّل pm2 restart app. تحقق أيضًا من pm2 list بحثًا عن إدخالات مكررة، واحذف الزائد منها باستخدام pm2 delete.

التطبيق لا يعود للعمل بعد إعادة تشغيل السيرفر

إما أن الأمر pm2 save لم يُشغَّل قط، أو أن pm2 startup شُغِّل لمستخدم خاطئ، مثل root. شغّل pm2 unstartup systemd، ثم كرّر الخطوة 3 بالمستخدم المالك للتطبيق، واختم بـ pm2 save.

502 Bad Gateway

لا يستطيع Nginx الوصول إلى التطبيق. شغّل sudo tail -n 50 /var/log/nginx/error.log. إذا رأيت connect() failed (111: Connection refused)، فهذا يعني أن التطبيق متعطّل أو يستمع على منفذ مختلف. راجع pm2 logs app وتأكد من أن proxy_pass يطابق قيمة PORT الخاصة بالتطبيق.

فشل WebSockets أو انقطاعها كل 60 ثانية

تحقق من أمرين:

  • يجب أن تكون كتلة map وproxy_http_version 1.1 وترويستا Upgrade/Connection موجودة جميعها. يحتفظ Certbot بها، لكن التعديلات اليدوية قد تحذفها أحيانًا.
  • ارفع قيمة proxy_read_timeout إذا كانت الاتصالات تُغلق بعد فترة من عدم النشاط.

إذا كنت تستخدم Socket.IO مع long-polling في وضع cluster، فإما أن تفعّل الجلسات الثابتة (sticky sessions) أو تضبط instances: 1.

الأسئلة الشائعة

هل يجب أن أشغّل PM2 بحساب root؟

لا. شغّله بمستخدم عادي يملك /var/www/app، حتى لا يتمكن خلل في التطبيق من السيطرة على السيرفر بالكامل. يستمع Nginx على المنفذين 80 و443، لذا لا يحتاج التطبيق أبدًا إلى صلاحيات root.

هل يمكنني استضافة عدة تطبيقات Node.js على سيرفر واحد؟

نعم. خصّص لكل تطبيق منفذًا خاصًا به واسمًا مستقلًا في PM2، وأنشئ كتلة server منفصلة في Nginx لكل نطاق. ثم شغّل pm2 save بعد تشغيل جميع التطبيقات.

ما الفرق بين pm2 restart وpm2 reload؟

يوقف restart جميع العمليات ويعيد تشغيلها دفعة واحدة، مما يسبب انقطاعًا قصيرًا. أما reload فيستبدل العمليات العاملة في وضع cluster واحدة تلو الأخرى، فيظل التطبيق متاحًا أثناء التحديث.

تحتاج إلى مساعدة؟ إذا واجهتك مشكلة، أو احتجت إلى تغيير على سيرفرك لا يمكن إجراؤه إلا من جانبنا، مثل rDNS أو وضع الإنقاذ (rescue mode) أو إعادة تثبيت نظام التشغيل أو ترقية الباقة، فافتح تذكرة دعم عبر https://cloudesta.com/submitticket.php. يمكنك أيضًا تصفح المزيد من الشروحات في مكتبة شروحات Cloudesta.

هل كانت المقالة مفيدة ؟

0 أعضاء وجدوا هذه المقالة مفيدة

Powered by WHMCompleteSolution