PM2 для JavaScript (Node.js): полное руководство с примерами
1. Что это и зачем нужна
PM2 (Process Manager 2) — это мощный менеджер процессов для приложений на Node.js, предназначенный для использования в production-среде. Он позволяет запускать, останавливать, перезагружать и мониторить Node.js-приложения, обеспечивая их высокую доступность и отказоустойчивость. PM2 автоматически перезапускает упавшие процессы, распределяет нагрузку между несколькими экземплярами приложения (кластеризация), предоставляет встроенный балансировщик нагрузки и поддерживает бесшовные (graceful) перезагрузки без простоев.
В production-среде Node.js-приложения часто сталкиваются с неожиданными ошибками, утечками памяти или перегрузками. PM2 решает эти проблемы, выступая в роли «демона-хранителя»: он отслеживает состояние каждого процесса, логирует stdout/stderr, предоставляет метрики производительности (CPU, память) и позволяет управлять приложением через CLI или API. Без PM2 разработчику пришлось бы вручную писать скрипты для перезапуска, мониторинга и масштабирования — PM2 автоматизирует все эти задачи.
Ключевые возможности PM2 включают: кластеризацию (запуск нескольких воркеров на всех ядрах CPU), zero-downtime перезагрузку, управление окружением (development/production), встроенный мониторинг (pm2 monit), поддержку конфигурационных файлов (ecosystem.config.js) и интеграцию с системами инициализации (systemd, upstart) для автозапуска при старте сервера.
2. Установка
PM2 устанавливается глобально через npm (Node Package Manager). Точная команда:
npm install -g pm2
Для установки конкретной версии:
npm install -g pm2@5.3.0
После установки проверьте версию:
pm2 --version
3. Быстрый старт — минимальный рабочий пример
Создайте простое Node.js-приложение app.js:
// app.js
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello from PM2!\n');
});
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Запустите приложение через PM2:
pm2 start app.js --name my-app
Проверьте статус:
pm2 status
Остановите приложение:
pm2 stop my-app
4. Основные методы/функции/классы
4.1. pm2.start() — Запуск приложения
Сигнатура: pm2.start(script, options, callback)
Что делает: Запускает указанный скрипт как процесс PM2 с заданными опциями (имя, количество инстансов, переменные окружения).
Рабочий пример кода:
const pm2 = require('pm2');
pm2.connect((err) => {
if (err) {
console.error(err);
process.exit(2);
}
pm2.start({
script: 'app.js',
name: 'my-app',
instances: 2, // запустить 2 инстанса
exec_mode: 'cluster', // кластерный режим
env: {
NODE_ENV: 'production',
PORT: 3000
}
}, (err, apps) => {
if (err) {
console.error(err);
return pm2.disconnect();
}
console.log('App started:', apps);
pm2.disconnect();
});
});
4.2. pm2.stop() — Остановка процесса
Сигнатура: pm2.stop(process, callback)
Что делает: Останавливает указанный процесс (по имени, ID или всем процессам).
Рабочий пример кода:
const pm2 = require('pm2');
pm2.connect((err) => {
if (err) {
console.error(err);
process.exit(2);
}
// Остановить процесс по имени
pm2.stop('my-app', (err, proc) => {
if (err) {
console.error(err);
return pm2.disconnect();
}
console.log('Process stopped:', proc);
pm2.disconnect();
});
});
4.3. pm2.restart() — Перезапуск процесса
Сигнатура: pm2.restart(process, callback)
Что делает: Перезапускает указанный процесс (или все процессы). При graceful restart приложение сначала получает сигнал SIGINT, ждет завершения текущих соединений, затем перезапускается.
Рабочий пример кода:
const pm2 = require('pm2');
pm2.connect((err) => {
if (err) {
console.error(err);
process.exit(2);
}
pm2.restart('my-app', (err, proc) => {
if (err) {
console.error(err);
return pm2.disconnect();
}
console.log('Process restarted:', proc);
pm2.disconnect();
});
});
4.4. pm2.delete() — Удаление процесса из списка PM2
Сигнатура: pm2.delete(process, callback)
Что делает: Останавливает процесс и удаляет его из списка управляемых процессов PM2.
Рабочий пример кода:
const pm2 = require('pm2');
pm2.connect((err) => {
if (err) {
console.error(err);
process.exit(2);
}
pm2.delete('my-app', (err) => {
if (err) {
console.error(err);
return pm2.disconnect();
}
console.log('Process deleted');
pm2.disconnect();
});
});
4.5. pm2.list() — Получение списка всех процессов
Сигнатура: pm2.list(callback)
Что делает: Возвращает массив объектов, описывающих все процессы, управляемые PM2 (статус, имя, ID, использование CPU/памяти, uptime).
Рабочий пример кода:
const pm2 = require('pm2');
pm2.connect((err) => {
if (err) {
console.error(err);
process.exit(2);
}
pm2.list((err, list) => {
if (err) {
console.error(err);
return pm2.disconnect();
}
console.log('Process list:', JSON.stringify(list, null, 2));
pm2.disconnect();
});
});
4.6. pm2.describe() — Детальная информация о процессе
Сигнатура: pm2.describe(process, callback)
Что делает: Возвращает подробную информацию о конкретном процессе: версия Node.js, аргументы командной строки, переменные окружения, путь к скрипту, количество рестартов и т.д.
Рабочий пример кода:
const pm2 = require('pm2');
pm2.connect((err) => {
if (err) {
console.error(err);
process.exit(2);
}
pm2.describe('my-app', (err, proc) => {
if (err) {
console.error(err);
return pm2.disconnect();
}
console.log('Process details:', JSON.stringify(proc, null, 2));
pm2.disconnect();
});
});
4.7. pm2.reload() — Бесшовная перезагрузка (graceful reload)
Сигнатура: pm2.reload(process, callback)
Что делает: Выполняет zero-downtime перезагрузку для кластеризованных приложений: перезапускает воркеры по одному, чтобы приложение продолжало обрабатывать запросы без простоев.
Рабочий пример кода:
const pm2 = require('pm2');
pm2.connect((err) => {
if (err) {
console.error(err);
process.exit(2);
}
pm2.reload('my-app', (err, proc) => {
if (err) {
console.error(err);
return pm2.disconnect();
}
console.log('Graceful reload completed:', proc);
pm2.disconnect();
});
});
4.8. pm2.flush() — Очистка логов
Сигнатура: pm2.flush(process, callback)
Что делает: Очищает все логи (stdout и stderr) для указанного процесса или всех процессов.
Рабочий пример кода:
const pm2 = require('pm2');
pm2.connect((err) => {
if (err) {
console.error(err);
process.exit(2);
}
pm2.flush('my-app', (err) => {
if (err) {
console.error(err);
return pm2.disconnect();
}
console.log('Logs flushed');
pm2.disconnect();
});
});
4.9. pm2.sendSignalToProcessName() — Отправка сигнала процессу
Сигнатура: pm2.sendSignalToProcessName(signal, process, callback)
Что делает: Отправляет произвольный сигнал (например, SIGUSR2, SIGINT) указанному процессу для кастомной обработки.
Рабочий пример кода:
const pm2 = require('pm2');
pm2.connect((err) => {
if (err) {
console.error(err);
process.exit(2);
}
pm2.sendSignalToProcessName('SIGUSR2', 'my-app', (err) => {
if (err) {
console.error(err);
return pm2.disconnect();
}
console.log('Signal sent');
pm2.disconnect();
});
});
4.10. pm2.launchBus() — Подписка на события PM2
Сигнатура: pm2.launchBus(callback)
Что делает: Создает шину событий (EventEmitter) для подписки на события PM2: запуск, остановка, перезапуск, ошибки процессов и т.д.
Рабочий пример кода:
const pm2 = require('pm2');
pm2.connect((err) => {
if (err) {
console.error(err);
process.exit(2);
}
pm2.launchBus((err, bus) => {
if (err) {
console.error(err);
return pm2.disconnect();
}
// Подписываемся на событие остановки процесса
bus.on('process:event', (event) => {
console.log('Event received:', event);
});
// Подписываемся на логи
bus.on('log:out', (data) => {
console.log('Log output:', data.data);
});
// Подписываемся на ошибки
bus.on('log:err', (data) => {
console.error('Log error:', data.data);
});
});
});
5. Реальный практический пример
Ниже представлен полноценный пример production-ready Express-приложения с использованием PM2 API для управления процессами, graceful shutdown и мониторинга.
// server.js — основное приложение
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
// Graceful shutdown handler
process.on('SIGINT', () => {
console.log('Received SIGINT. Performing graceful shutdown...');
server.close(() => {
console.log('HTTP server closed.');
process.exit(0);
});
});
process.on('SIGTERM', () => {
console.log('Received SIGTERM. Performing graceful shutdown...');
server.close(() => {
console.log('HTTP server closed.');
process.exit(0);
});
});
app.get('/', (req, res) => {
res.json({ message: 'Hello from PM2-managed app!', pid: process.pid });
});
app.get('/health', (req, res) => {
res.json({ status: 'ok', uptime: process.uptime() });
});
const server = app.listen(PORT, () => {
console.log(`Worker ${process.pid} started on port ${PORT}`);
});
// pm2-manager.js — скрипт для программного управления PM2
const pm2 = require('pm2');
const path = require('path');
class PM2Manager {
constructor() {
this.connected = false;
}
connect() {
return new Promise((resolve, reject) => {
pm2.connect((err) => {
if (err) {
reject(err);
return;
}
this.connected = true;
resolve();
});
});
}
disconnect() {
if (this.connected) {
pm2.disconnect();
this.connected = false;
}
}
async startApp(config = {}) {
const defaultConfig = {
script: path.join(__dirname, 'server.js'),
name: 'express-app',
instances: 'max', // использовать все ядра CPU
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
PORT: 3000
},
max_memory_restart: '500M', // перезапуск при превышении 500MB
error_file: './logs/err.log',
out_file: './logs/out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss',
merge_logs: true,
autorestart: true,
watch: false,
max_restarts: 10,
restart_delay: 1000
};
const finalConfig = { ...defaultConfig, ...config };
return new Promise((resolve, reject) => {
pm2.start(finalConfig, (err, apps) => {
if (err) {
reject(err);
return;
}
resolve(apps);
});
});
}
async gracefulReload(appName) {
return new Promise((resolve, reject) => {
pm2.reload(appName, { timeout: 30000 }, (err, proc) => {
if (err) {
reject(err);
return