В веб-сервисах со стандартным REST HTTP трафиком команда nginx -s reload выполняется незаметно: мастер-процесс запускает новые воркеры с обновлённым конфигом, а старые воркеры закрываются сразу после завершения текущих коротких HTTP-запросов (обычно за 50–100 миллисекунд).
В чём проблема с постоянными WebSocket туннелями?
WebSocket сессии (включая стриминговые шлюзы, телеком-прокси и чат-протоколы) удерживают постоянное TCP соединение часами или сутками. Если не настроить таймауты завершения воркеров, при каждом обновлении сертификатов Let's Encrypt или изменении конфигурации старые воркер-процессы Nginx накапливаются в памяти десятками, либо внезапно обрывают тысячи активных пользователей.
ps aux | grep nginx начинает показывать множество процессов в статусе nginx: worker process is shutting down, каждый из которых продолжает потреблять память и дескрипторы сокетов.
Конфигурация плавного завершения (Graceful Shutdown)
Для управления временем жизни старых воркеров в глобальную секцию nginx.conf добавляется директива worker_shutdown_timeout:
# /etc/nginx/nginx.conf
worker_processes auto;
worker_rlimit_nofile 131072;
# Таймаут на закрытие старых воркеров (например, 240 секунд)
worker_shutdown_timeout 240s;
events {
worker_connections 65535;
multi_accept on;
use epoll;
}
http {
# Корректная обработка заголовков апгрейда сокетов
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
}
Как это работает на практике
Когда мастер-процесс получает сигнал SIGHUP (через systemctl reload nginx):
- Мастер мгновенно порождает новые воркеры, которые подхватывают новые подключения.
- Старые воркеры перестают слушать порт 443, но продолжают обслуживать существующие WebSocket-сессии.
- По истечении
worker_shutdown_timeoutворкер корректно отправляет клиентам фрейм закрытия (Close Frame 1001 Going Away) и завершает работу.
Клиентские приложения (Incy, Happ, браузерные клиенты) получают стандартный статус перезапуска и автоматически переподключаются к новым воркерам за считанные миллисекунды.