آموزش سرور

نصب Node.js و اجرای پروژه با PM2؛ اجرای پایدار و همیشگی روی سرور لینوکس

تهیه: کلودیفای (نگارش با کمک هوش مصنوعی) · انتشار: · زمان مطالعه: ۷ دقیقه

root@server: ~$ pm2 start ecosystem.config.js[PM2] App [myapp] launched (2 instances)$ pm2 status| id | name | mode | status | cpu | | 0 | myapp | cluster | online | ... | $ pm2 startup systemdNode.jsPM2آموزش سرورنصب Node.js و اجرای پروژهبا PM2؛ اجرای پایدار وهمیشگی روی سرور لینوکسمجلهٔ کلودیفایclodify.net/blog
پاسخ کوتاه

برای اجرای پایدار پروژهٔ Node.js روی سرور، ابتدا نسخهٔ LTS را از مخزن NodeSource یا با nvm نصب کنید، سپس PM2 را با npm install -g pm2 نصب کنید و برنامه را با pm2 start اجرا کنید. با pm2 startup و pm2 save برنامه پس از ری‌استارت سرور خودکار بالا می‌آید. PM2 برنامهٔ از کار افتاده را دوباره اجرا می‌کند، لاگ‌ها را نگه می‌دارد و با حالت Cluster از همهٔ هسته‌ها استفاده می‌کند. در پایان برنامه را پشت Nginx و HTTPS قرار دهید.

چرا node app.js کافی نیست؟

وقتی پروژهٔ Node.js را روی کامپیوتر خودتان با node app.js اجرا می‌کنید، همه‌چیز خوب است. اما روی سرور، این روش چند مشکل جدی دارد: با بستن ترمینال SSH برنامه متوقف می‌شود، اگر برنامه به‌خاطر یک خطای پیش‌بینی‌نشده از کار بیفتد کسی آن را دوباره اجرا نمی‌کند، پس از ری‌استارت سرور برنامه خودکار بالا نمی‌آید و لاگ‌ها جایی ذخیره نمی‌شوند.

PM2 یک مدیر پردازه (Process Manager) برای Node.js است که همهٔ این مشکلات را حل می‌کند. برنامه را در پس‌زمینه اجرا می‌کند، در صورت توقف دوباره راه‌اندازی می‌کند، لاگ‌ها را ذخیره می‌کند، پس از ری‌استارت سرور برنامه‌ها را برمی‌گرداند و حتی می‌تواند یک برنامه را روی چند هستهٔ پردازنده اجرا کند. جایگزین آن، نوشتن سرویس systemd است که در راهنمای سرویس‌های systemd توضیح داده‌ایم؛ PM2 برای پروژه‌های Node.js امکانات تخصصی بیشتری دارد.

معماری اجرای Node.js در تولید
HTTPS127.0.0.1:3000پخش درخواست‌هاکاربرhttps://app.example.comNginxTLS و پراکسی معکوسPM2مدیریت و پایشنمونه‌های برنامهCluster روی هسته‌هاHTTPS127.0.0.1:3000پخش درخواست‌هاکاربرhttps://app.example.comNginxTLS و پراکسی معکوسPM2مدیریت و پایشنمونه‌های برنامهCluster روی هسته‌ها
شکل ۱ Nginx در لبه و PM2 در پشت، برنامه را امن، پایدار و چندهسته‌ای اجرا می‌کنند.

قدم ۱: نصب Node.js

نسخهٔ Node.js موجود در مخازن پیش‌فرض اوبونتو معمولاً قدیمی است. دو روش رایج برای نصب نسخهٔ LTS (پشتیبانی بلندمدت) وجود دارد.

روش اول: مخزن NodeSource

مناسب سرورهایی که یک نسخهٔ ثابت Node.js برای همهٔ کاربران لازم دارند:

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

روش دوم: nvm

اگر چند پروژه با نسخه‌های مختلف Node.js دارید، nvm اجازه می‌دهد برای هر کاربر چند نسخه نصب و بینشان جابه‌جا شوید:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
source ~/.bashrc
nvm install --lts
nvm use --lts

شمارهٔ نسخهٔ اسکریپت nvm را از صفحهٔ رسمی پروژه در گیت‌هاب بررسی کنید. اگر روی سرور ایران دسترسی به این آدرس‌ها یا رجیستری npm مشکل دارد، تنظیم میرور npm و pip را ببینید.

قدم ۲: آماده کردن پروژه

پروژه را با git یا scp به سرور منتقل کنید، مثلاً در /var/www/myapp. سپس وابستگی‌ها را نصب کنید:

cd /var/www/myapp
npm ci --omit=dev

دستور npm ci دقیقاً نسخه‌های ثبت‌شده در package-lock.json را نصب می‌کند و برای سرور تولید مناسب‌تر از npm install است. اگر پروژه مرحلهٔ ساخت دارد (مثل Next.js یا TypeScript)، npm run build را هم اجرا کنید.

برنامه را با یک کاربر غیر root اجرا کنید. اجرای سرویس‌ها با root یعنی اگر برنامه آسیب‌پذیری داشته باشد، مهاجم دسترسی کامل به سرور می‌گیرد. ساخت کاربر را در مدیریت کاربران و sudo ببینید.

deploy@server: /var/www/myapp
$ pm2 start ecosystem.config.js[PM2] App [myapp] launched (2 instances)$ pm2 status| id | name  | mode    | status | cpu | memory || 0  | myapp | cluster | online | ... | ...    |$ pm2 startup systemd$ pm2 save[PM2] Saving current process list...$ 
شکل ۲ اجرا با فایل ecosystem، بررسی وضعیت و ذخیرهٔ فهرست برای اجرای خودکار پس از ری‌استارت.

قدم ۳: نصب و استفاده از PM2

sudo npm install -g pm2
pm2 start app.js --name myapp
pm2 status
pm2 logs myapp

دستورهای پرکاربرد دیگر:

pm2 restart myapp
pm2 reload myapp     # zero-downtime in cluster mode
pm2 stop myapp
pm2 delete myapp
pm2 monit            # live CPU/RAM per process

فایل پیکربندی ecosystem

به‌جای پارامترهای طولانی در خط فرمان، تنظیمات را در فایل ecosystem.config.js در ریشهٔ پروژه بنویسید:

module.exports = {
  apps: [{
    name: "myapp",
    script: "app.js",
    instances: "max",
    exec_mode: "cluster",
    max_memory_restart: "400M",
    env: { NODE_ENV: "production", PORT: 3000 }
  }]
};

و با pm2 start ecosystem.config.js اجرا کنید. این فایل را در مخزن پروژه نگه دارید تا تنظیمات همیشه همراه کد باشد.

حالت Cluster

Node.js به‌طور پیش‌فرض فقط از یک هستهٔ پردازنده استفاده می‌کند. با instances: "max" و exec_mode: "cluster"، PM2 به تعداد هسته‌ها نمونه از برنامه اجرا می‌کند و درخواست‌ها را بینشان پخش می‌کند. دقت کنید که در این حالت، برنامه نباید داده‌ای را در حافظهٔ خود نگه دارد که بین نمونه‌ها باید مشترک باشد (مثل نشست کاربران)؛ برای این کار از Redis یا دیتابیس استفاده کنید.

ری‌استارت در صورت نشت حافظه

گزینهٔ max_memory_restart برنامه را وقتی مصرف رم از حد تعیین‌شده بیشتر شود، ری‌استارت می‌کند. این یک راه‌حل موقت برای نشت حافظه است تا سرور به کمبود حافظه نخورد؛ علت اصلی نشت را هم پیدا کنید.

قدم ۴: اجرای خودکار پس از ری‌استارت سرور

pm2 startup systemd
# run the sudo command that pm2 prints
pm2 save

دستور اول یک سرویس systemd برای PM2 می‌سازد و دستوری را چاپ می‌کند که باید با sudo اجرا کنید. دستور دوم فهرست برنامه‌های در حال اجرا را ذخیره می‌کند تا پس از ری‌استارت بازگردانده شوند. هر بار برنامه‌ای اضافه یا حذف کردید، pm2 save را دوباره اجرا کنید.

قدم ۵: مدیریت لاگ‌ها

لاگ‌های PM2 در ~/.pm2/logs ذخیره می‌شوند و اگر کنترل نشوند، به‌مرور دیسک را پر می‌کنند. ماژول چرخش لاگ را نصب کنید:

pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 20M
pm2 set pm2-logrotate:retain 7

قدم ۶: Nginx و HTTPS

برنامهٔ Node.js را مستقیم روی پورت 80 یا 443 در معرض اینترنت قرار ندهید. Nginx را جلوی آن بگذارید تا TLS، فشرده‌سازی، فایل‌های ثابت و محدودیت‌ها را مدیریت کند:

server {
    listen 80;
    server_name app.example.com;
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

دو هدر آخر برای پشتیبانی از WebSocket است. سپس با Certbot گواهی SSL بگیرید؛ آموزش در Nginx و Let's Encrypt. برنامه را طوری تنظیم کنید که فقط روی 127.0.0.1 گوش دهد تا از بیرون مستقیم در دسترس نباشد.

اجرای ساده یا PM2
node app.js
  • با بستن SSH متوقف می‌شود
  • پس از خطا دوباره اجرا نمی‌شود
  • پس از ری‌استارت بالا نمی‌آید
  • فقط یک هسته
در برابر
PM2
  • اجرای پس‌زمینه و همیشگی
  • ری‌استارت خودکار پس از خطا
  • اجرای خودکار با startup و save
  • حالت Cluster و reload بدون قطعی
شکل ۳ برای سرور تولید، همیشه از یک مدیر پردازه مثل PM2 یا systemd استفاده کنید.

به‌روزرسانی پروژه

یک روال ساده برای انتشار نسخهٔ جدید:

cd /var/www/myapp
git pull
npm ci --omit=dev
npm run build --if-present
pm2 reload myapp

در حالت Cluster، reload نمونه‌ها را یکی‌یکی ری‌استارت می‌کند تا سایت بدون قطعی به‌روز شود.

رفع خطاهای رایج

  • برنامه مدام ری‌استارت می‌شود (errored): با pm2 logs myapp --lines 100 خطا را ببینید؛ معمولاً متغیر محیطی گم شده یا پورت اشغال است.
  • EADDRINUSE: پورت در حال استفاده است؛ با ss -tlnp | grep 3000 پروسه را پیدا کنید.
  • پس از ری‌استارت سرور برنامه بالا نمی‌آید: pm2 startup و pm2 save را دوباره اجرا کنید.
  • 502 از Nginx: برنامه اجرا نمی‌شود یا روی پورت دیگری است؛ رفع خطای 502 را ببینید.

برای پروژه‌های سبک Node.js، یک سرور مجازی کوچک لینوکسی کافی است و با رشد بار، با حالت Cluster از هسته‌های بیشتر استفاده می‌کنید.

متغیرهای محیطی و رازها

بیشتر برنامه‌های Node.js به اطلاعاتی مثل رمز دیتابیس، کلید API یا توکن ربات نیاز دارند. این اطلاعات را هرگز داخل کد یا مخزن git قرار ندهید. دو روش رایج وجود دارد: تعریف آن‌ها در بخش env فایل ecosystem روی خود سرور (و نگه‌داشتن این فایل خارج از مخزن)، یا استفاده از فایل .env که با کتابخانه‌ای مثل dotenv خوانده می‌شود. در نسخه‌های جدید Node.js، گزینهٔ --env-file هم بدون کتابخانهٔ اضافه فایل را می‌خواند.

دسترسی فایل حاوی رازها را محدود کنید تا فقط کاربر اجراکنندهٔ برنامه بتواند آن را بخواند؛ مثلاً با chmod 600 .env. اگر سرور توسط چند نفر مدیریت می‌شود، این کار جلوی دسترسی ناخواسته را می‌گیرد.

پایش سلامت برنامه

PM2 می‌گوید برنامه اجرا می‌شود، اما نمی‌گوید درست پاسخ می‌دهد. برنامه‌ای که گیر کرده یا به دیتابیس دسترسی ندارد، ممکن است در PM2 وضعیت online داشته باشد. یک مسیر سلامت ساده مثل /health در برنامه بسازید که وضعیت اتصال به دیتابیس و سرویس‌های وابسته را بررسی کند و با ابزاری مثل Uptime Kuma آن را از بیرون پایش کنید. این‌طوری اگر برنامه از دید کاربر خراب شد، حتی با وجود اجرای پردازه، باخبر می‌شوید.

همچنین pm2 monit و pm2 status را گاهی بررسی کنید. شمارندهٔ ری‌استارت (ستون restarts یا ↺) که مدام بالا می‌رود، نشانهٔ خطایی است که برنامه را بارها از کار می‌اندازد و باید در لاگ‌ها دنبالش بگردید.

سؤالات متداول

PM2 بهتر است یا systemd؟

هر دو برنامه را پایدار اجرا می‌کنند. PM2 امکانات ویژهٔ Node.js مثل حالت Cluster، reload بدون قطعی و مانیتور داخلی دارد؛ systemd ابزار استاندارد سیستم است و برای هر نوع برنامه‌ای کار می‌کند.

آیا PM2 را باید با root اجرا کنم؟

خیر. PM2 و برنامه را با یک کاربر عادی اجرا کنید؛ فقط دستوری که pm2 startup چاپ می‌کند به sudo نیاز دارد.

چطور متغیرهای محیطی مثل کلید API را به برنامه بدهم؟

در بخش env فایل ecosystem.config.js یا یک فایل .env که برنامه آن را می‌خواند. فایل‌های حاوی کلید را در مخزن git قرار ندهید.

حالت Cluster برای ربات تلگرام مناسب است؟

اگر ربات با روش Polling کار می‌کند، چند نمونهٔ هم‌زمان پیام‌ها را تکراری دریافت می‌کنند؛ برای این ربات‌ها instances را روی ۱ بگذارید. با Webhook و طراحی بدون حالت، Cluster قابل استفاده است.

آماده‌اید؟ سرورتان را بسازید.

از ۱٬۲۰۰ تومان در ساعت · پرداخت ریالی

سرورتان را همین حالا بسازید.

ساعتی بپردازید. هر وقت خواستید حذف کنید.