# теория · шаг 1 из 4
Что делает reload и чего не делает
Коротко
nginx -s reload - это сигнал HUP: мастер перечитывает конфиг, поднимает
новых воркеров и даёт старым доработать начатые запросы. Битый конфиг просто не
применяется, сайт живёт на старом. Но успешный код возврата не означает, что
конфиг применился: если новый порт занят, reload вернёт ноль, а правда
останется только в error_log. Старый воркер с живым соединением не уйдёт
никогда - для этого есть worker_shutdown_timeout. Логи ротируются лишь в паре
с nginx -s reopen.
Знаешь про HUP и worker_shutdown_timeout - листай до «Что ломается без этого»: там про молчаливый ноль от reload и про ротацию.
Конфиг прошёл nginx -t, ты выполняешь nginx -s reload, команда возвращает
ноль. Значит ли это, что новый конфиг работает?
nginx -t && nginx -s reload; echo "код возврата: $?"
nginx: configuration file /etc/nginx/nginx.conf test is successful
код возврата: 0
А сайт при этом продолжает отвечать по старому конфигу, и единственный след правды - строка в логе:
bind() to 0.0.0.0:9090 failed (98: Address in use)
На что это похоже
Reload - это не «перезапуск», а пересменка. Смена, стоящая за стойкой, дообслуживает своих посетителей и уходит; новая смена принимает всех, кто пришёл после. Дверь при этом не закрывается ни на секунду.
Отсюда два следствия, о которых обычно не думают. Уходящая смена остаётся столько, сколько длится разговор с последним посетителем. И если новая смена не смогла заступить, старая просто продолжает работать - об этом никто не объявит.
Механизм: пересменка воркеров
Правило
По HUP мастер проверяет конфиг, открывает новые слушающие сокеты, запускает
новых воркеров и командует старым завершиться плавно: новых соединений они не
берут, начатые дорабатывают. Битый конфиг не применяется вовсе - сайт остаётся
на прежнем.
nginx -t && nginx -s reload
Две команды через && - привычка, экономящая инциденты. Но помни границу: -t
проверяет синтаксис и только его. Существует ли файл сертификата, свободен ли
порт, пускает ли SELinux в каталог - выясняется уже при применении.
Разбор: старый воркер, который не уходит
Стенд: через nginx идёт соединение к бэкенду, который отвечает по кусочку две
минуты. Делаем reload и смотрим процессы.
nginx: master process /usr/sbin/nginx
nginx: worker process is shutting down
nginx: worker process
Через две секунды после reload старый воркер на месте, запрос жив. Через
семнадцать - всё ещё на месте, запрос жив. Он честно ждёт конца соединения,
и для WebSocket или SSE это «никогда». Десять релоадов за день - десять таких
воркеров в памяти, каждый со своим старым конфигом.
Лечится одной строкой, у которой нет значения по умолчанию:
worker_shutdown_timeout 5s;
С ней тот же замер даёт другое: через две секунды воркер на месте и запрос жив, через семь - воркера нет и запрос оборван. Это и есть цена: директива не ускоряет завершение, а назначает момент, когда долгие соединения будут убиты.
Тонкость, которую видно только на стенде: воркер, который уже уходит, директива не догоняет. Тот, что висел с прошлого релоада, остался висеть и после того, как строку дописали, - настройка достаётся воркеру при старте, а не в момент завершения. То есть первый релоад после правки ещё не помогает.
Разбор: что переживает reload
Проверяем на limit_req с rate=1r/m:
| действие | ответ |
|---|---|
| первый запрос | 200 |
| второй сразу | 503 |
после reload |
503 |
после смены размера зоны с 1m на 2m |
200 |
Счётчики лимитов живут в разделяемой памяти и переживают перезагрузку конфига -
это правильно, иначе релоад стал бы способом обойти лимит. А вот изменение
параметров зоны заставляет создать её заново, и счётчики обнуляются.
Ровно так же ведёт себя кеш: содержимое proxy_cache_path остаётся, но правка
параметров зоны его отвязывает.
Заодно стоит помнить, чего reload не меняет вовсе: пользователя (user),
загруженные модули (load_module) и сам бинарник. Для них нужен рестарт или
приём из следующего урока.
Что ломается без этого
Ротация логов без сигнала. logrotate переименовывает файл, а nginx держит
открытый дескриптор и продолжает писать в него - под новым именем. Замер:
mv access.log access.log.1 ; curl .../after-rename
новый access.log создан: нет
строк с after-rename в переименованном файле: 1
После nginx -s reopen появляется новый файл, и следующая строка идёт уже
туда. Пока сигнала нет, место на диске занимают файлы, которых не видно в ls,
а свежий лог не растёт - и это замечают не сразу, потому что мониторинг обычно
следит за размером каталога, а не за датой последней строки.
Молчаливый ноль от reload. Из крючка в начале урока: команда вернула
успех, конфиг не применился. Такое бывает всегда, когда ошибка обнаруживается
не при разборе конфига, а при его применении: занятый порт, отсутствующий файл
сертификата, нехватка прав. Поэтому после выкладки смотрят не код возврата, а
error_log и поведение сайта.
Текст системной ошибки зависит от библиотеки. На alpine строка выглядит
как (98: Address in use), на debian - (98: Address already in use). Номер
одинаков, слова разные; искать в логах надо по номеру, иначе grep из чужой
статьи не находит ничего.
Зачем это в работе
Выкладка конфига nginx - одна из немногих операций, которую можно делать в рабочее время, и это ценно ровно до тех пор, пока ты знаешь три вещи: успешный код возврата ничего не гарантирует, старые воркеры могут висеть неделями, а логи не ротируются сами.
Практический минимум для боевого сервера: worker_shutdown_timeout в конфиге,
nginx -s reopen в postrotate и привычка после каждой выкладки посмотреть
хвост error_log. Проверить второе стоит один раз:
grep -A3 postrotate /etc/logrotate.d/nginx
Теперь сам
Сайт с WebSocket. Ты выкатываешь конфиги по десять раз в день и однажды
замечаешь, что nginx съел четыре гигабайта памяти, а в ps два десятка
процессов worker process is shutting down. Что произошло и что сделать
сейчас, а что - чтобы не повторилось?
Ответ: каждый релоад оставлял по воркеру, который ждёт конца
WebSocket-соединений, а они не кончаются. Сейчас - дописать
worker_shutdown_timeout (например, 30 секунд) и сделать релоад; помня, что
уже висящие воркеры эта строка не догонит, их придётся убрать рестартом в
спокойное время. Чтобы не повторилось - та же строка в конфиге плюс алерт на
число процессов nginx: их должно быть ровно worker_processes плюс мастер.
Главное
reload = HUP: мастер перечитывает конфиг, поднимает новых воркеров, старым
даёт доработать начатое; битый конфиг не применяется, и сайт живёт на прежнем.
Код возврата reload не доказывает применение - при занятом порте он равен
нулю, а правда только в error_log; nginx -t проверяет синтаксис, но не
файлы, порты и права. Старый воркер с долгим соединением не уходит никогда
(замер: жив и через семнадцать секунд), лечится worker_shutdown_timeout, у
которого нет умолчания и который не догоняет уже уходящих воркеров. Счётчики
лимитов и кеш переживают reload, а смена параметров зоны их обнуляет. Логи
ротируются только вместе с nginx -s reopen.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий