Skip to main content

Що таке PM2 і як управляти процесами Node.js у виробництві?

PM2 - це менеджер процесів для Node.js у виробничому середовищі, який автоматично перезапускає додатки після збоїв, розподіляє навантаження між ядрами CPU через кластеризацію та зберігає логи у файлах.

Теорія

TL;DR

  • PM2 схожий на менеджера ресторану: якщо один офіціант пішов, він одразу замінює його новим, у пік трафіку відкриває більше станцій і записує все в журнал без зупинки кухні.
  • Головна різниця: node server.js вмирає при падінні й використовує одне ядро CPU. PM2 перезапускається автоматично, кластеризує на всі ядра й відстежує метрики.
  • Використовуй PM2 при деплої на VPS або сервер. Для локальної розробки - nodemon. Для serverless (Lambda, Vercel) - платформа сама керує процесами.
  • pm2 reload і pm2 restart - це різні речі. Перша замінює воркери поступово, друга вбиває всіх одразу.

Швидкий приклад

bash
# Без PM2 - одне падіння вбиває все node server.js # Необроблена помилка → процес мертвий → потрібен ручний перезапуск # З PM2 - автоматичне відновлення npm install -g pm2 pm2 start server.js --name api -i max # кластерний режим, всі ядра CPU pm2 list # online | uptime | restarts: 0 # Симулюй падіння: вбий процес вручну # PM2 виявляє вихід, перезапускає за 1 секунду # pm2 list показує: restarts: 1 pm2 stop api pm2 delete api

Одна команда замінює весь скрипт запуску плюс ручний моніторинг.

Ключова різниця

node server.js прив'язує життя додатку до одного OS-процесу. Будь-який необроблений виняток вбиває його назавжди, весь трафік іде через одне ядро CPU, а логи зникають після перезапуску. PM2 огортає процес супервізором: перехоплює сигнал виходу, запускає замінника за мілісекунди і розподіляє трафік між кількома воркерами через вбудований модуль cluster. Додаток стає сервісом, а не скриптом.

Коли використовувати

  • Один сервер, API на Express або Fastify: pm2 start server.js -i max одразу дає кластеризацію.
  • Self-hosted Next.js: pm2 start npm --name "next" -- start з кастомним сервером.
  • NestJS або скомпільований TypeScript: ecosystem-файл, що вказує на dist/server.js.
  • Навантажений додаток за Nginx: PM2 керує процесами, Nginx - маршрутизацією.
  • Локальна розробка: nodemon краще справляється з гарячим перезавантаженням.
  • Serverless (Lambda, Vercel): платформа сама керує процесами, PM2 нічого не додає.

Таблиця порівняння

Можливістьnode app.jsPM2nodemonforever
Перезапуск при падінніНіТакТак (тільки dev)Так
Кластеризація CPUРучний модуль clusterВбудована (-i max)НіНі
Збереження логівstdout, зникають при рестартіФайли в ~/.pm2/logs/КонсольФайли
Перезавантаження без простоївВручнуpm2 reloadНіНі
МоніторингНемаєpm2 monit + хмарна панельНемаєБазовий
Коли використовуватиСкрипти, локальна розробкаПродакшн Node.js сервериГаряче перезавантаженняПростий рестарт (застарілий)

Як PM2 працює всередині

PM2 запускається як Node.js master-процес і форкає дочірні процеси через OS-рівневі fork() виклики, керуючи ними через модуль child_process. Він слідкує за кодами виходу кожного дочірнього процесу і сигналами (SIGINT, необроблені винятки) та запускає перезапуск за мілісекунди, якщо код виходу ненульовий. Кластеризація делегується вбудованому модулю cluster з одним воркером на ядро CPU (через os.cpus().length).

Команда pm2 reload запускає нові воркери, чекає поки кожен з них сигналізує готовність (подія "listening"), потім надсилає SIGTERM старим воркерам і чекає на завершення відкритих з'єднань. Саме ця послідовність забезпечує деплой без простоїв.

Ecosystem-файл

Для серйозного деплою використовуй ecosystem-файл:

js
// ecosystem.config.js module.exports = { apps: [{ name: 'api', script: 'dist/server.js', // скомпільований TypeScript instances: 'max', // по одному екземпляру на кожне ядро exec_mode: 'cluster', // обов'язково - без цього instances ігнорується max_memory_restart: '1G', // перезапустити воркер при перевищенні 1GB RAM max_restarts: 5, // зупинити після 5 падінь за 60 секунд kill_timeout: 5000, // 5 секунд на завершення з'єднань перед SIGKILL env_production: { NODE_ENV: 'production', PORT: 3000 } }] };
bash
pm2 start ecosystem.config.js --env production pm2 reload api # нові воркери стартують, старі завершують запити і виходять pm2 save # зберегти список процесів між перезавантаженнями сервера pm2 startup # згенерувати systemd unit для автозапуску

Типові помилки

Запуск без назви pm2 start app.js без --name створює запис з назвою "app" або "server". Коли в pm2 list з'являється п'ять однаково названих записів, неможливо зупинити чи перезавантажити конкретний. Завжди додавай --name myapp.

Відсутність exec_mode: 'cluster' в ecosystem-файлі Якщо встановити instances: 'max' без exec_mode: 'cluster', PM2 запускає один екземпляр у fork mode. Вся конфігурація з кількома екземплярами мовчки ігнорується. 8-ядерний сервер працює на одному потоці Node.js. Це причина приблизно половини скарг на продуктивність PM2 на Stack Overflow.

pm2 restart замість pm2 reload в продакшні pm2 restart вбиває всіх воркерів одразу. Активні з'єднання обриваються і повертають 5xx помилки. pm2 reload замінює воркерів по одному, чекаючи на завершення кожного. Для CI/CD пайплайнів завжди використовуй pm2 reload.

Запуск PM2 від root Дочірні процеси успадковують права root. Якщо додаток виконує shell-команди, це реальна дірка в безпеці. Використовуй непривілейованого системного користувача, а pm2 startup згенерує systemd-конфігурацію для коректного автозапуску.

Ігнорування ротації логів Я бачив, як через це падає продакшн сервер о третій ночі - логи займають 100GB і диск переповнюється. Встановлюй pm2-logrotate в перший же день: pm2 install pm2-logrotate. За замовчуванням ротація при 10MB.

Де використовується в реальних проєктах

  • Ghost blog, Strapi CMS: pm2 start ecosystem.config.js для кластеризованих API маршрутів.
  • Self-hosted Next.js: pm2 start npm --name "next" -- start.
  • NestJS бекенди: ecosystem-файл з max_memory_restart: '1G' і скомпільованим dist.
  • Feathers.js real-time додатки: -i max для масштабування Socket.io воркерів.
  • PM2 у Docker: використовуй pm2-runtime як entrypoint для коректної обробки PID 1 і уникнення zombie-процесів.

Питання на співбесіді

Q: Як PM2 реалізує деплой без простоїв?
A: Запускає нові cluster-воркери, чекає поки кожен з них видасть подію "listening" (HTTP сервер готовий приймати з'єднання), потім надсилає SIGTERM старим воркерам і чекає на закриття відкритих з'єднань.

Q: Яка різниця між pm2 start -i max і ручним написанням cluster module?
A: PM2 додає автоматичний перезапуск окремих воркерів, збереження логів і моніторинг поверх Node's cluster. Якщо один воркер падає, PM2 перезапускає саме його, не зачіпаючи інших.

Q: Що відбувається коли воркер перевищує ліміт пам'яті?
A: PM2 опитує розмір V8 heap і порівнює з max_memory_restart. При перевищенні ліміту перезапускає конкретний воркер, інші продовжують обробляти запити.

Q: Best practice для PM2 у Docker?
A: Використовуй pm2-runtime замість звичайного pm2 start. Він коректно обробляє сигнали PID 1 і запобігає накопиченню zombie-процесів, які plain PM2 пропускає у Docker-контексті.

Q: Рівень senior: як PM2 розрізняє падіння і штатну зупинку?
A: Слухає child.on('exit') і перевіряє код виходу разом з тим, чи PM2 сам надсилав SIGTERM (від pm2 stop). Ненульовий код виходу без попереднього SIGTERM від PM2 означає падіння і тригерить перезапуск. Після max_restarts спроб додаток переходить у стан "errored" і PM2 припиняє спроби.

Приклади

Базовий: Express API з автоперезапуском

javascript
// server.js const express = require('express'); const app = express(); app.get('/', (req, res) => res.send('Hello from PM2')); app.listen(3000, () => console.log('Сервер запущено на порту 3000'));
bash
pm2 start server.js --name basic-api -i 2 pm2 list # basic-api | cluster | 2 instances | online | restarts: 0

Вбий один з воркерів вручну. PM2 виявляє вихід і запускає замінника. Другий воркер продовжує обробляти запити поки перший відновлюється.

Середній рівень: Ecosystem-файл для продакшну (NestJS / TypeScript)

js
// ecosystem.config.js - використовується в деплоях NestJS і Strapi module.exports = { apps: [{ name: 'api', script: 'dist/main.js', instances: 'max', exec_mode: 'cluster', max_memory_restart: '1G', max_restarts: 5, kill_timeout: 5000, env_production: { NODE_ENV: 'production', PORT: 3000 } }] };
bash
pm2 start ecosystem.config.js --env production pm2 reload api # Під час перезавантаження: жодних 5xx помилок - нові воркери приймають до завершення старих pm2 logs api pm2 save && pm2 startup

Комбінація pm2 save і pm2 startup зберігає список процесів після перезавантаження сервера, нічого не потрібно запускати вручну.

Просунутий рівень: Захист від нескінченних перезапусків

Без обмежень баг, який кладе додаток одразу після старту, змушує PM2 перезапускати його в нескінченному циклі, навантажуючи CPU і переповнюючи логи.

js
// Додай до apps[] в ecosystem.config.js { max_restarts: 5, // зупинитись після 5 падінь min_uptime: '10s', // додаток має протримати 10с щоб вважатись запущеним kill_timeout: 5000 // 5 секунд перед примусовим завершенням SIGKILL }
bash
pm2 start ecosystem.config.js # Додаток падає 5 разів поспіль за менш ніж 10 секунд кожен pm2 list # Статус: errored - PM2 припинив спроби pm2 logs api --lines 50 # подивись причину падіння

Після 5 перезапусків PM2 позначає додаток як "errored" і зупиняється. Фіксуєш баг, запускаєш pm2 restart api, лічильник скидається. Ніяких CPU спайків від нескінченних перезапусків о третій ночі.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?