# теория · шаг 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 мастер проверяет конфиг, открывает новые слушающие сокеты, запускает
новых воркеров и командует старым завершиться плавно: новых соединений они не
берут, начатые дорабатывают. Простаивающее keepalive-соединение старый воркер
закрывает сразу (замер на 1.31.8: 0,1 с после HUP) - клиент просто открывает
новое. Битый конфиг не применяется вовсе - сайт остаётся на прежнем.
nginx -t && nginx -s reload
Две команды через && - привычка, экономящая инциденты. Но помни границу: -t
разбирает конфиг и открывает то, что в нём названо. Отсутствующий сертификат,
include или каталог лога он поймает (замер: cannot load certificate). А вот
свободен ли порт и есть ли на диске каталог из root - выясняется уже при
применении и на живом запросе.
Разбор: старый воркер, который не уходит
Стенд: через 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 reload
подхватывает: мастер перечитывает конфиг целиком, и новые воркеры стартуют уже
под новым пользователем. На стенде nginx 1.31.6 после смены user nginx на
user nobody и reload воркер работал от nobody. Поэтому права на файлы,
сокеты и каталоги кеша проверяют ДО перезагрузки, а не после.
Что ломается без этого
Ротация логов без сигнала. 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 секунд) и сделать релоад; помня, что
уже висящие воркеры эта строка не догонит, их придётся убрать рестартом в
спокойное время. Чтобы не повторилось - та же строка в конфиге плюс алерт на
воркеры в состоянии is shutting down, которые живут дольше нескольких минут.
Просто считать процессы нельзя: во время reload поколений воркеров два, а при
proxy_cache рядом живут ещё cache manager и cache loader.
Главное
После reload проверяй error_log и новый ответ сервера. Успешная отправка HUP не означает, что мастер смог открыть новые сокеты и применить конфиг. Следи также за уходящими воркерами: уже начатые долгие соединения могут удерживать старые процессы после нескольких выкладок.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий