Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що таке PM2 і як управляти процесами Node.js у виробництві?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**PM2** - це менеджер процесів для Node.js у виробничому середовищі, який автоматично перезапускає додатки після збоїв, використовує всі ядра CPU через кластерний режим і зберігає логи у файлах. ```bash pm2 start server.js --name api -i max # кластерний режим, всі ядра CPU pm2 reload api # заміна воркерів по одному без простоїв ``` **Головне:** у продакшні використовуй `pm2 reload`, а не `pm2 restart` - restart вбиває всіх воркерів одразу й обриває активні з'єднання.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**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.js` | PM2 | nodemon | forever | |-----------|--------------|-----|---------|--------| | Перезапуск при падінні | Ні | Так | Так (тільки 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 спайків від нескінченних перезапусків о третій ночі. Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.