Linux: Управление Node.js приложениями через pm2

Node.js, благодаря своей асинхронной событийно-ориентированной архитектуре, стал одним из самых популярных инструментов для создания высоконагруженных веб-приложений, API-сервисов и realtime-систем. Однако у этой медали есть обратная сторона: стандартный Node.js процесс является однопоточным и уязвимым. Любое необработанное исключение — и приложение "падает", переставая отвечать на запросы. Более того, если ваш сервер перезагрузится по любой причине (апдейт ядра, сбой питания, плановая перезагрузка), все запущенные node app.js процессы не восстановятся автоматически.

Здесь на сцену выходит PM2 (Process Manager 2) — промышленный менеджер процессов для Node.js, ставший стандартом де-факто в индустрии. Это не просто "перезапускалка" упавших приложений. PM2 предоставляет полноценную экосистему для управления приложениями в production: встроенный балансировщик нагрузки (cluster mode), систему логирования, мониторинг потребления ресурсов, инструменты для zero-downtime обновлений и механизмы автозапуска при старте системы.

В этой статье мы не просто рассмотрим базовые команды, а проведем полное погружение в PM2. Вы узнаете, как правильно настроить приложение для работы в условиях высокой нагрузки, обеспечить его отказоустойчивость, организовать централизованное логирование и настроить автозапуск на примерах для серверов под управлением Linux (Ubuntu/Debian/CentOS). Материал ориентирован на администраторов хостингов и разработчиков, которые хотят вывести свои приложения на уровень промышленной эксплуатации.

Данная информация предназначена для услуг: VPS хостинг или Облачный хостинг

Установка и первый запуск: Базовая настройка окружения

Прежде чем мы начнем управлять процессами, необходимо установить PM2 глобально в систему. Выполните следующую команду от имени пользователя, который будет запускать приложения (или с sudo для глобальной установки):

npm install pm2 -g

После завершения установки проверьте версию, чтобы убедиться в успешности операции:

pm2 --version

Для работы с PM2 не требуется прав суперпользователя (хотя они нужны для некоторых функций, таких как автозапуск системы), но рекомендуется запускать менеджер процессов под тем же пользователем, что и ваши Node.js приложения, чтобы избежать проблем с правами на файлы и порты.

Альтернативный способ установки (для CI/CD или облачных платформ):
Если у вас нет доступа к глобальной установке (например, на некоторых PaaS-платформах), вы можете добавить PM2 как зависимость проекта и использовать pm2-runtime:

npm install pm2 --save

В package.json пропишите скрипт запуска:

{
  "scripts": {
    "start": "pm2-runtime start ecosystem.config.js --env production"
 }
}

Этот метод также рекомендован для использования в Docker-контейнерах, так как pm2-runtime работает в режиме форграунда (foreground), корректно реагируя на сигналы SIGTERM и SIGINT .

Базовое управление приложениями: CLI на каждый день

После установки давайте рассмотрим основные команды, с которых начинается каждый день администратора.

Запуск приложения

Простейший способ запустить Node.js приложение — передать pm2 start путь к точке входа:

pm2 start app.js

Вы также можете задать имя приложению, чтобы легче было его идентифицировать в списке процессов:

pm2 start app.js --name "my-api"

Просмотр статуса и мониторинг

Чтобы увидеть список всех запущенных процессов, их статус (online/stopped/errored), потребление памяти и CPU, используйте:

pm2 list
# Сокращенный вариант
pm2 ls

Для детального мониторинга в реальном времени с автоматическим обновлением данных существует команда monit:

pm2 monit

Этот интерфейс покажет вам логи конкретного процесса в реальном времени, а также графики загрузки CPU и памяти .

Остановка, перезапуск и удаление

  • Остановка процесса (процесс завершается, но остается в списке PM2):pm2 stop my-api

  • Перезапуск (полная остановка и запуск заново):pm2 restart my-api

  • Удаление (остановка и полное удаление из списка PM2):pm2 delete my-api

Просмотр логов

Без логов невозможно представить production-эксплуатацию. PM2 по умолчанию сохраняет stdout и stderr в файлы, расположенные в ~/.pm2/logs/. Чтобы посмотреть логи в реальном времени:

pm2 logs my-api

Очистить все логи (сбросить файлы) можно командой:

pm2 flush

Кластеризация (Cluster Mode): Масштабирование на все ядра CPU

Node.js работает в одном потоке. На сервере с 8 ядрами процессора ваше приложение будет использовать только одно, если вы не примените кластеризацию. PM2 решает эту проблему элегантно, используя встроенный модуль Node.js cluster.

Запуск в кластерном режиме

Чтобы использовать все доступные ядра CPU, выполните:

pm2 start app.js -i max

Параметр -i max указывает PM2 определить количество ядер автоматически и запустить ровно столько воркеров (копий приложения). Вы также можете указать конкретное число, например -i 4, если хотите оставить ресурсы для других задач .

Внутри вашего приложения через переменную окружения NODE_APP_INSTANCE можно узнать ID текущего воркера. Это полезно, например, если нужно запустить CRON-задачу только на одном экземпляре:

if (process.env.NODE_APP_INSTANCE === '0') {
  // Запуск очистки базы данных только на первом воркере
  startCleanupJob();
}

Горизонтальное масштабирование на лету

Если в какой-то момент нагрузки стало недостаточно (например, вы увеличили количество ядер или приложению стало тесно), можно изменить количество процессов без остановки сервиса:

pm2 scale my-api 6

Требования к коду приложения

Кластеризация накладывает важное ограничение: приложение должно быть stateless (не хранить состояние локально). То есть вы не должны хранить сессии пользователей в памяти процесса (в массивах или Map). Если это необходимо, используйте внешние хранилища: Redis, Memcached или базы данных. Иначе запросы одного пользователя, попадая на разные воркеры, будут терять его авторизацию и данные .

Zero-downtime reload: Обновляем код без остановки сервиса

Классический pm2 restart my-api убивает все процессы одновременно, что приводит к кратковременному простою сервера (downtime). Для продакшена это недопустимо. Вместо этого используйте механизм graceful reload.

Как работает graceful reload

Команда pm2 reload запускает процессы обновления по очереди:

  1. Запускается новый воркер с новой версией кода.

  2. PM2 отправляет сигнал SIGINT старому воркеру, давая ему время завершить текущие запросы.

  3. После того как старый воркер завершил работу (или истек таймаут), он удаляется.

  4. Цикл повторяется для следующего воркера.

В любой момент времени хотя бы один воркер продолжает обрабатывать трафик. Перезагрузка инициируется командой:

pm2 reload my-api

Graceful Shutdown: Учим приложение "правильно умирать"

Чтобы reload работал корректно, ваше приложение должно корректно обрабатывать сигнал завершения. Если процесс просто "убивается" сразу, все текущие соединения оборвутся. Node.js позволяет нам перехватить сигнал SIGINT и закрыть соединения плавно.

Пример кода для Express.js приложения:

const express = require('express');
const app = express();

const server = app.listen(3000, () => {
  console.log('App is running');
});

process.on('SIGINT', () => {
  console.log('Graceful shutdown initiated');
    server.close(() => {
    console.log('All connections closed. Exiting.');
    process.exit(0);
  });

  // Принудительный выход, если что-то пошло не так (например, через 10 секунд)
  setTimeout(() => {
    console.error('Force shutdown');
    process.exit(1);
  }, 10000);
});

В конфигурации PM2 для таких приложений стоит установить флаг wait_ready и таймаут:

module.exports = {
  apps: [{
    name: "my-api",
    script: "./app.js",
    wait_ready: true,
    kill_timeout: 15000
  }]
}

Флаг wait_ready заставит PM2 ожидать сигнал process.send('ready') от приложения, прежде чем считать его запущенным и направлять на него трафик .

Ecosystem.config.js: Декларативное управление конфигурацией

Писать длинные команды с кучей параметров неудобно и чревато ошибками. Профессиональный подход — использовать файл декларации ecosystem.config.js. Сгенерировать шаблон можно командой:

pm2 init

Этот файл на JavaScript (или JSON) позволяет описать все приложения, переменные окружения и настройки развертывания в одном месте.

Пример продвинутой конфигурации

module.exports = {
  apps: [{
    // Базовое имя приложения
    name: "nestjs-app",

    // Точка входа (обычно собранный бандл)
    script: "./dist/main.js",

    // Режим работы: 'cluster' или 'fork'
    exec_mode: "cluster",

    // Количество инстансов: 'max' или число
    instances: "max",

    // Переменные окружения для разработки
    env: {
      NODE_ENV: "development",
      PORT: 3000
    },

    // Переменные окружения для продакшена (переопределяют env)
    env_production: {
      NODE_ENV: "production",
      PORT: 8080
    },

    // Автоматический перезапуск при падении
    autorestart: true,

    // Автоматический перезапуск при превышении памяти (2GB)
    max_memory_restart: "2G",

    // Задержка между рестартами (защита от бесконечных циклов перезапуска)
    restart_delay: 4000,

    // Куда писать стандартный вывод (stdout)
    out_file: "./logs/app-out.log",

    // Куда писать ошибки (stderr)
    error_file: "./logs/app-err.log",

    // Формат времени в логах
    log_date_format: "YYYY-MM-DD HH:mm:ss Z",

    // Объединять ли логи разных воркеров в один файл (удобно для cluster mode)
    merge_logs: true,

    // Следить за изменениями в коде (только для разработки, не для прода!)
    watch: false,

    // Игнорировать определенные папки при слежении
    ignore_watch: ["node_modules", "logs"],

    // Время ожидания graceful shutdown (ms)
    kill_timeout: 5000,

    // Минимальное время работы процесса до того, как он будет считаться стабильным
    min_uptime: "1m"
  }]
};

Запуск всех приложений из конфига:

pm2 start ecosystem.config.js --env production

Флаг --env production заставит PM2 использовать секцию env_production .

Автозапуск при загрузке системы (Startup Hook)

Самая частая проблема на серверах: вы настроили и запустили приложения через PM2, все работает. Но после перезагрузки сервера (reboot) сайт ложится, и нужно вручную заходить и писать pm2 resurrect. Чтобы этого избежать, существует механизм Startup Hook.

Как это работает

PM2 генерирует скрипт инициализации, совместимый с вашей системой инициализации (systemd, upstart, launchd и т.д.), и регистрирует его в системе.

  1. Сначала сохраните текущий список процессов:

    pm2 save

    Эта команда создает дамп (dump) всех запущенных приложений в файл ~/.pm2/dump.pm2.

  2. Сгенерируйте и установите startup-скрипт:

    pm2 startup

    PM2 автоматически определит вашу ОС (например, systemd для Ubuntu 16+/CentOS 7+) и выдаст команду, которую нужно скопировать и вставить от имени суперпользователя. Выглядит это примерно так:

    # Пример вывода команды:
    sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u your_user --hp /home/your_user

  3. Проверка:
    После настройки, если вы перезагрузите сервер, PM2 автоматически запустится как демон и восстановит все сохраненные приложения.

Отключение автозапуска

Если вам больше не нужно, чтобы PM2 стартовал при загрузке:

pm2 unstartup

Особенности при использовании NVM (Node Version Manager)

Если вы управляете версиями Node.js через NVM, пути к исполняемым файлам меняются при обновлении Node.js. После каждого обновления версии вам нужно заново выполнить pm2 save и пересоздать startup hook (сначала pm2 unstartup, затем снова pm2 startup) .

Продвинутое логирование и ротация

Со временем логи могут занять десятки гигабайт, если их не ротировать (не разбивать на части и не архивировать).

Настройка расположения логов

В файле ecosystem.config.js вы можете задать кастомные пути:

module.exports = {
  apps: [{
    name: "app",
    script: "app.js",
    out_file: "/var/log/myapp/out.log",
    error_file: "/var/log/myapp/error.log",
    log_file: "/var/log/myapp/combined.log", // Объединенный лог
    merge_logs: true
  }]
}

Ротация логов (Logrotate)

PM2 не имеет встроенной ротации, но для этого существует официальный модуль pm2-logrotate. Установите его:

pm2 install pm2-logrotate

После установки модуль автоматически начнет ротировать логи. Настроить параметры (максимальный размер файла, количество копий, интервал) можно через pm2 set:

pm2 set pm2-logrotate:max_size 10M # Ротировать при достижении 10MB
pm2 set pm2-logrotate:retain 7 # Хранить 7 файлов бэкапов
pm2 set pm2-logrotate:compress true # Сжимать в gzip

Конфигурация хранится в ~/.pm2/module_conf.json .


Мониторинг и метрики в реальном времени

PM2 предоставляет встроенные инструменты мониторинга, но если вы хотите собирать метрики (например, количество запросов в секунду, время ответа БД) и отправлять их во внешнюю систему (или видеть в веб-интерфейсе Keymetrics), используйте модуль pmx внутри вашего кода.

Установка PMX

npm install pmx --save

Пример добавления кастомных метрик

const pmx = require('pmx');

// Инициализация с опциями (например, включение мониторинга сети)
pmx.init({
  network: true, // Мониторинг сетевых портов
  ports: true // Отображение используемых портов
});

const probe = pmx.probe();

// 1. Простая метрика (значение обновляется каждую секунду)
const userCount = probe.metric({
  name: 'Online Users',
  value: () => Object.keys(users).length
});

// 2. Счетчик (инкремент/декремент)
const reqCounter = probe.counter({
  name: 'Requests processed'
});

app.use((req, res, next) => {
  reqCounter.inc();
next();
});

// 3. Измеритель (событий в секунду)
const meter = probe.meter({
  name: 'Requests/sec',
  samples: 1 // Обновление раз в секунду
});

app.use((req, res, next) => {
  meter.mark();
  next();
});

Эти метрики будут видны в команде pm2 monit, а также в платном сервисе PM2 Plus (бывший Keymetrics) .

Триггеры и алерты

PMX позволяет создавать триггеры, удаленно вызываемые через PM2 Plus:

pmx.action('db:clean', (reply) => {
  cleanDatabase();
  reply({ success: true });
});

Затем из веб-интерфейса вы сможете нажать кнопку "db:clean", и функция выполнится на сервере.

Особые случаи: Socket.IO и WebSockets

При использовании кластерного режима (-i max) с Socket.IO или любыми другими WebSocket-библиотеками возникает проблема: соединения через WebSocket требуют "липкости" (sticky sessions), т.к. они не могут свободно переключаться между процессами. Обычный PM2 не поддерживает sticky-сессии на уровне балансировщика.

Решение 1: Отказ от HTTP Long-Polling

На клиенте принудительно используйте только WebSocket транспорт:

const socket = io({
  transports: ["websocket"]
});

Решение 2: Использование форка PM2 для Socket.IO

Существует специальный форк @socket.io/pm2, который модифицирует балансировщик PM2 для поддержки sticky-сессий.

Установка:

npm remove -g pm2
npm install -g @socket.io/pm2

Конфигурация ecosystem.config.js:

module.exports = {
  apps: [{
    script: "worker.js",
    instances: "max",
    exec_mode: "cluster"
  }]
};

И в коде используйте официальные адаптеры @socket.io/cluster-adapter и @socket.io/sticky .

Решение 3: Внешний балансировщик (Nginx)

Проксируйте трафик через Nginx, который будет направлять запросы от одного пользователя на один и тот же порт (воркер). В конфиге Nginx используется директива ip_hash:

upstream backend {
    ip_hash;
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
}

Развертывание (Deployment) через PM2

PM2 имеет встроенный инструмент развертывания, основанный на Git. Это удобно, когда у вас есть сервер и вы хотите обновлять код одной командой без настройки сложных CI/CD пайплайнов.

Настройка в ecosystem.config.js

Добавьте секцию deploy:

javascript
module.exports = {
  apps: [{
    name: "app",
    script: "app.js"
  }],
  deploy: {
    production: {
      user: "deployer",
      host: "192.168.1.100",
      repo: "git@github.com:user/repo.git",
      ref: "origin/master",
      path: "/var/www/myapp",
      "post-deploy": "npm install && pm2 reload ecosystem.config.js --env production"
    }
  }
};

Команды деплоя

  1. Первоначальная настройка сервера:pm2 deploy production setup

    Эта команда создаст необходимые папки на сервере (source, current, shared) и клонирует репозиторий.

  2. Деплой новой версии:pm2 deploy production

  3. Откат к предыдущей версии:pm2 deploy production revert 1

Важно: Убедитесь, что ваш SSH-ключ добавлен на сервер (ssh-copy-id), и что на сервере глобально установлен PM2 .

Заключение 

PM2 — это мощный инструмент, который превращает запуск Node.js приложений из "простого node app.js" в управляемый, отказоустойчивый и масштабируемый процесс. Он обязателен к использованию в любой production-среде.

Краткий контрольный список для настройки production-сервера:

  1. Установка: npm install pm2 -g.

  2. Инициализация: Создайте ecosystem.config.js через pm2 init.

  3. Конфигурация: Настройте instances: "max", exec_mode: "cluster", max_memory_restart, пути к логам.

  4. Код: Реализуйте graceful shutdown (обработка SIGINT), убедитесь в stateless приложения.

  5. Запуск: pm2 start ecosystem.config.js --env production.

  6. Сохранение: pm2 save.

  7. Автозагрузка: pm2 startup (и выполните предложенную команду sudo).

  8. Логи: Установите pm2-logrotate для контроля дискового пространства.

  9. Проверка: Перезагрузите сервер (sudo reboot) и убедитесь, что приложение ожило автоматически (pm2 list).

Используя эти практики, вы обеспечите своим приложениям uptime на уровне 99.99% и сможете спокойно спать, зная, что даже сбой не положит ваш бизнес.

Часто задаваемые вопросы (FAQ)

Вопрос: В чем разница между pm2 restart и pm2 reload?
Ответ: restart убивает и запускает все процессы одновременно, вызывая простой. reload перезапускает процессы по очереди, обеспечивая zero-downtime обновление.

Вопрос: Как посмотреть, сколько памяти потребляет мое приложение?
Ответ: Используйте команду pm2 list (колонка memory) или pm2 monit для детального графика.

Вопрос: Почему после перезагрузки сервера мое приложение не запустилось?
Ответ: Скорее всего, вы забыли выполнить pm2 save для сохранения дампа процессов и pm2 startup для генерации автозагрузочного скрипта. Повторите шаги из Главы 6.

Вопрос: Можно ли запускать не Node.js приложения (Python, PHP, Bash) через PM2?
Ответ: Да. Используйте параметр -x или --interpreter:

pm2 start script.py --interpreter python3

Вопрос: Как обновить сам PM2 до последней версии?
Ответ: Выполните последовательно:npm install pm2 -g
pm2 update

Команда pm2 update синхронизирует работающий демон с новой версией бинарных файлов

  • 0 Пользователи нашли это полезным

Помог ли вам данный ответ?

Ищете что-то другое?

xvps.ru