NGINX NodeJS PM2 — 502 bad gateway — ошибка connect() failed (111: Connection refused) while connecting to upstream

NGINX NodeJS PM2 — 502 bad gateway — ошибка connect() failed (111: Connection refused) while connecting to upstream

В связке технологий (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

О чём речь? Простыми словами.

Изначально работающее приложение было запущено стандартной командой от PM2:

$ pm2 start app.js

 

В таком запуске приложение работало в режиме «Fork» (вместо «Cluster») и задействовало всего одно ядро процессора. Ниже пример:

Режим Fork для приложения NodeJS в менеджере процессов PM2
Режим Fork для приложения NodeJS в менеджере процессов PM2

В какой-то момент «запросной» активности этого оказалось недостаточно и в пиковые моменты вываливались 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/