В связке технологий (NGINX, NodeJS, PM2) на сайте стала периодически появляться ошибка 502 bad gateway, которую возвращал веб-сервер NGINX, который работал в режиме «пробрасывающего» запросы сервера на NodeJS-приложение. В логах сервера NGINX эта ошибка была оформлена как «connect() failed (111: Connection refused) while connecting to upstream«.
Управление NodeJS-приложениями было доверено PM2. PM2 — это демон-менеджер процессов, который поможет вам управлять вашим приложением и поддерживать его в сети 24/7.
Источник проблем не удалось до конца идентифицировать (хотя и были предположения), но получилось на 100% избавиться от 502 ошибки в процессе общения NGINX с NodeJS.
Решение проблемы
Шаг № 1 — Включение режима «Cluster Mode» в PM2
В процессе чтения документации по менеджеру процессов PM2 стало ясно, что он умеет работать в режиме «Cluster Mode«.
Выдержка из документа:
Кластерный режим («Cluster Mode«) позволяет масштабировать сетевые приложения Node.js (сервер http (s) / tcp / udp) на все доступные ЦП (CPUs) без каких-либо изменений кода. Это значительно увеличивает производительность и надежность ваших приложений в зависимости от количества доступных процессоров. Под капотом здесь используется кластерный модуль Node.js, так что дочерние процессы масштабируемого приложения могут автоматически совместно использовать порты сервера. Дополнительные сведения см. В разделе «Как это работает» в официальной документации Node.js по модулю кластера.
![Cluster Mode in PM2 - NodeJS server Cluster Mode in PM2 - NodeJS server](https://efim360.ru/wp-content/uploads/cluster-mode-in-pm2-nodejs-server-1024x540.png)
О чём речь? Простыми словами.
Изначально работающее приложение было запущено стандартной командой от PM2:
$ pm2 start app.js
В таком запуске приложение работало в режиме «Fork» (вместо «Cluster») и задействовало всего одно ядро процессора. Ниже пример:
![Режим Fork для приложения NodeJS в менеджере процессов PM2 Режим Fork для приложения NodeJS в менеджере процессов PM2](https://efim360.ru/wp-content/uploads/2021-12-19_20-30-47-1024x220.png)
В какой-то момент «запросной» активности этого оказалось недостаточно и в пиковые моменты вываливались 502 ошибки.
Все приложения сначала были остановлены:
$ pm2 stop app_name
После этого все приложения были удалены из процессов:
$ pm2 delete app_name
Затем пустой PM2 был сохранён в качестве «автозагрузки»
$ pm2 startup
…и заморожен
$ pm2 save
После этого выделенный сервер ушёл в выключение (не перезагружен), а потом был запущен (включен).
После этого в PM2 было запущено «проблемное» приложение с опцией «-i» и значением «max«, которое символизировало «максимальное» доступное количество ядер процессора на этом выделенном сервере.
$ pm2 start app.js -i max
Ядра были автоматически посчитаны и в результате приложение запустило несколько версий себя. После этого решения все «проблемные» ошибки 502 были устранены.
Шаг № 2 — Анализ конфигурационного файла виртуального хоста в NGINX
На всякий случай был проанализирован файл конфигурации виртуального хоста, от имени которого работало приложение. В этом файле были найдены проблемные места.
Директива proxy_http_version в контексте location. Её просто не существовало в конфиге нужного виртуального хоста. Это большая проблема, так как отсутствие пропечатанной директивы proxy_http_version означает то, что она работает в режиме по умолчанию в формате 1.0.
Формат 1.0 не поддерживает ПОСТОЯННЫЕ СОЕДИНЕНИЯ и ПРОВЕРКИ ПОДЛИННОСТИ NTLM. Иными словами NGINX каждый раз устанавливает соединение с NodeJS приложением и говорит «Здравствуйте», когда пробрасывает запрос. Это очень трудозатратно и повышает шанс отсутствия ответа от NodeJS-приложения, если оно в какой-то момент времени занято более важными вещами.
Дополнительно прописали директиву:
location / {
...
proxy_http_version 1.1;
...
}
Добавление директивы proxy_http_version открыло возможность использования другой директивы (keepalive) в контексте upstream
Задействует кэш соединений для группы серверов.
Выдержка из документа:
Параметр
соединения
устанавливает максимальное число неактивных постоянных соединений с серверами группы, которые будут сохраняться в кэше каждого рабочего процесса. При превышении этого числа наиболее давно не используемые соединения закрываются.
Следует особо отметить, что директива
keepalive
не ограничивает общее число соединений с серверами группы, которые рабочие процессы nginx могут открыть. Параметрсоединения
следует устанавливать достаточно консервативно, чтобы серверы группы по-прежнему могли обрабатывать новые входящие соединения.
upstream my_nodejs_upstream {
server 127.0.0.1:3001;
keepalive 64;
}
Информационные ссылки
PM2 — Cluster Mode — https://pm2.keymetrics.io/docs/usage/cluster-mode/