بلاگ/دیپلوی اپلیکیشن Node.js روی سرور اختصاصی و VPS با PM2 و Nginx: Cluster Mode، ری‌استارت خودکار و HTTPS
به‌روز شده

دیپلوی اپلیکیشن Node.js روی سرور اختصاصی و VPS با PM2 و Nginx: Cluster Mode، ری‌استارت خودکار و HTTPS

1405/07/130 بازدید
دیپلوی اپلیکیشن Node.js روی سرور اختصاصی و VPS با PM2 و Nginx: Cluster Mode، ری‌استارت خودکار و HTTPS

دیپلوی اپلیکیشن Node.js روی سرور اختصاصی و VPS با PM2 و Nginx: Cluster Mode، ری‌استارت خودکار و HTTPS

دیپلوی Node.js روی سرور با PM2 و Nginx

اجرای یک اپلیکیشن Node.js با دستور ساده node app.js برای تست محلی کافی است، اما روی یک سرور اختصاصی یا VPS که قرار است واقعاً ترافیک کاربر را جواب بدهد، چند مشکل جدی دارد: با هر خطای بدون مدیریت پراسس کرش می‌کند و بالا نمی‌آید، فقط از یک هسته CPU استفاده می‌کند، و هیچ لایه‌ای برای HTTPS یا مدیریت دامنه ندارد. این مقاله نشان می‌دهد چطور با PM2 مدیریت پراسس و استفاده از همه هسته‌ها را حل کنید و با Nginx به‌عنوان ریورس پروکسی، HTTPS رایگان و یک آدرس تمیز برای اپلیکیشن بسازید.

چرا دیپلوی مستقیم Node.js کافی نیست؟

وقتی یک پراسس Node.js مستقیم روی پورت ۳۰۰۰ یا هر پورت دیگری اجرا می‌شود، چند محدودیت ساختاری دارد:

  • اگر پراسس به هر دلیلی (exception مدیریت‌نشده، out-of-memory) متوقف شود، کسی آن را دوباره اجرا نمی‌کند.
  • با ریبوت سرور، اپلیکیشن بالا نمی‌آید مگر اینکه خودتان یک اسکریپت استارتاپ بنویسید.
  • Node.js تک‌نخی (single-threaded) است؛ یک پراسس فقط یک هسته CPU را درگیر می‌کند، حتی روی سروری با ۸ یا ۱۶ هسته.
  • اتصال مستقیم کلاینت به پورت اپلیکیشن یعنی نه گواهی SSL ساده‌ای دارید، نه امکان سرو کردن چند اپلیکیشن از پشت یک دامنه و پورت ۴۴۳.

PM2 مشکل اول تا سوم را حل می‌کند، Nginx مشکل چهارم را.

آماده‌سازی سرور

قبل از هر چیز روی VPS یا سرور اختصاصی‌تان Node.js و npm را نصب کنید (ترجیحاً از طریق NodeSource یا nvm، نه ریپوی پیش‌فرض توزیع که معمولاً نسخه قدیمی دارد):

curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
node -v

کد اپلیکیشن را روی سرور کلون یا آپلود کنید، وابستگی‌ها را نصب کنید و مطمئن شوید اپلیکیشن با node app.js به‌صورت دستی بالا می‌آید و روی پورت داخلی (مثلاً ۳۰۰۰) پاسخ می‌دهد. این تست دستی قبل از رفتن سراغ PM2 و Nginx، نصف مشکلات بعدی را زودتر نشان می‌دهد. اگر می‌خواهید خود همین مرحلهٔ کلون و آپلود کد را هم خودکار کنید، دیپلوی خودکار پروژه با Git Hook یک روش جایگزین برای همین بخش از کار است.

مدیریت پراسس با PM2

PM2 یک process manager برای Node.js است که پراسس را در پس‌زمینه نگه می‌دارد، لاگ جمع می‌کند، و در صورت کرش دوباره اجرایش می‌کند.

نصب و اجرای اولیه

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

از این لحظه، پراسس اپلیکیشن دیگر به ترمینال باز شما وابسته نیست؛ می‌توانید SSH را ببندید و اپلیکیشن سر جایش می‌ماند.

Cluster Mode: استفاده از همه هسته‌های CPU

تک‌نخی بودن Node.js یعنی یک پراسس معمولی فقط یک هسته را اشغال می‌کند. PM2 با cluster mode چند worker از همان اپلیکیشن بالا می‌آورد و ترافیک را بین آن‌ها تقسیم می‌کند، بدون اینکه کد اپلیکیشن تغییری کند:

pm2 start app.js --name my-app -i max

فلگ -i max به تعداد هسته‌های موجود روی سرور، worker اجرا می‌کند. اگر می‌خواهید یک یا چند هسته را برای سرویس‌های دیگر (دیتابیس، Redis) آزاد نگه دارید، به‌جای max عدد مشخصی بدهید، مثلاً -i 2.

ری‌استارت خودکار و startup روی ریبوت سرور

PM2 به‌طور پیش‌فرض پراسس کرش‌شده را دوباره اجرا می‌کند، اما این رفتار با ریبوت سرور از بین می‌رود مگر اینکه صریحاً فعالش کنید:

pm2 save
pm2 startup

دستور pm2 startup یک دستور دیگر (مخصوص توزیع و init system سرور شما) چاپ می‌کند که باید جداگانه با sudo اجرا شود؛ این دستور یک systemd service می‌سازد که روی بوت شدن سرور، PM2 و همه پراسس‌های ذخیره‌شده با pm2 save را دوباره بالا می‌آورد. بدون این دو دستور، یک ریبوت ساده سرور (برای آپدیت کرنل یا نگهداری) اپلیکیشن را عملاً آفلاین می‌کند.

Nginx به‌عنوان Reverse Proxy

اپلیکیشن Node.js روی پورت داخلی (مثلاً ۳۰۰۰) اجرا می‌شود، اما کاربر نهایی باید روی پورت ۸۰/۴۴۳ و با دامنه شما به آن وصل شود. Nginx این نقش را به‌عنوان reverse proxy بازی می‌کند: درخواست HTTP/HTTPS را از کاربر می‌گیرد و به پورت داخلی اپلیکیشن پاس می‌دهد.

نمودار معماری: Nginx با HTTPS در جلوی چند worker اپلیکیشن Node.js در cluster mode PM2 قرار می‌گیرد و ترافیک را بین آن‌ها توزیع می‌کند، با قابلیت ری‌استارت خودکار

برای سخت‌تر کردن همین پیکربندی در برابر حملات رایج، ارتقای امنیت اپلیکیشن Node با پروکسی بازگشتی Nginx نکات تکمیلی دارد.

sudo apt-get install -y nginx

یک فایل پیکربندی برای دامنه بسازید (مثلاً /etc/nginx/sites-available/my-app):

server {
    listen 80;
    server_name 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 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_cache_bypass $http_upgrade;
    }
}

و فعال‌سازی:

sudo ln -s /etc/nginx/sites-available/my-app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

هدرهای X-Real-IP و Upgrade/Connection مخصوصاً وقتی اپلیکیشن از WebSocket استفاده می‌کند (مثلاً Socket.IO) لازم‌اند؛ بدون آن‌ها اتصال WebSocket از پشت Nginx برقرار نمی‌شود.

HTTPS رایگان با Let's Encrypt

ساده‌ترین راه گرفتن گواهی SSL رایگان، Certbot است که پیکربندی Nginx را هم خودش اصلاح می‌کند:

sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com

Certbot بلاک server را برای گوش دادن روی ۴۴۳ و ریدایرکت خودکار از HTTP به HTTPS به‌روزرسانی می‌کند. گواهی‌ها ۹۰ روز اعتبار دارند و Certbot معمولاً یک cron job یا systemd timer برای تمدید خودکار نصب می‌کند؛ کافی است هر چند وقت یک‌بار با sudo certbot renew --dry-run مطمئن شوید تمدید خودکار واقعاً کار می‌کند. جزئیات بیشتر نصب و تمدید گواهی در نصب گواهی SSL رایگان با Let's Encrypt و Certbot روی سرور مجازی و اختصاصی لینوکس آمده است.

PM2 fork mode در برابر cluster mode

ویژگیFork Mode (پیش‌فرض)Cluster Mode (-i max)
تعداد هسته CPU استفاده‌شدهیک هستههمه هسته‌های تعیین‌شده
مناسب برایاسکریپت‌های پس‌زمینه، worker تکی، دیباگAPI و وب‌سرورهایی با ترافیک واقعی
اشتراک پورت بین پراسس‌هاندارددارد (PM2 خودش load balance می‌کند)
نیاز به تغییر کد اپلیکیشن-معمولاً خیر، مگر state درون‌حافظه‌ای مشترک بین ریکوئست‌ها داشته باشید

اگر اپلیکیشن state (مثل session در حافظه) را بین ریکوئست‌ها نگه می‌دارد، قبل از رفتن به cluster mode باید آن state را به Redis یا دیتابیس منتقل کنید؛ وگرنه هر worker نسخه جدای خودش از آن state را خواهد داشت و رفتار اپلیکیشن ناپایدار می‌شود.

جمع‌بندی

برای یک دیپلوی قابل‌اعتماد Node.js روی سرور، سه لایه لازم است: PM2 برای اینکه پراسس همیشه بالا باشد و از همه هسته‌ها استفاده کند، Nginx برای مسیر تمیز بین دامنه و پورت داخلی اپلیکیشن، و Let's Encrypt برای HTTPS رایگان و تمدیدشونده. هیچ‌کدام از این سه به تغییر زیادی در کد اپلیکیشن نیاز ندارند؛ بیشترِ کار، پیکربندی سرور است. همین الگو برای پشته‌های دیگر هم کاربرد دارد؛ برای PHP می‌توانید دیپلوی پروژه Laravel روی سرور اختصاصی و VPS را ببینید.

اگر هنوز زیرساخت را انتخاب نکرده‌اید، یک سرور مجازی (VPS) با منابع قابل‌تنظیم و شبکه داخل ایران، نقطه شروع مناسبی برای همین روال دیپلوی است؛ برای بارهای سنگین‌تر و نیاز به منابع اختصاصی کامل، سرور اختصاصی هم گزینه بعدی است.