1. 1
  2. 2
  3. 3
  4. 4

# теория · шаг 2 из 4

nginx -t и reload: как не уронить прод

Коротко

Правило одной строкой: nginx -t && systemctl reload nginx. Сначала проверка, применение только если она прошла.

  • reload не перезапускает nginx: мастер поднимает новых воркеров, а старым даёт доработать текущие запросы. Ни одно соединение не рвётся.
  • Сломанный конфиг reload просто не применит - сайт продолжит работать на старом. А вот restart попробует стартовать, упадёт и оставит сайт лежать.
  • reload перечитывает конфиг целиком, вместе с именами бэкендов; чего он не делает - так это не чистит кеш и не сбрасывает состояние лимитов.
  • Полный перезапуск нужен редко: смена user, обновление бинарника.

Если разница reload и restart очевидна - листай до «Теперь сам».

Что будет с тем, кто качает большой файл

Клиент прямо сейчас скачивает архив на два гигабайта, скачано двести мегабайт. В этот момент администратор применяет новый конфиг:

$  команда
nginx -t && systemctl reload nginx

Что произойдёт со скачиванием: оборвётся, начнётся заново или продолжится?

Продолжится, и докачается до конца - причём по СТАРОМУ конфигу. Почему так и чем за это платят, разберём ниже.

На что это похоже

Пересменка диспетчеров на вокзале.

Новая смена приходит и садится за свободные пульты - все новые вызовы идут уже к ним, по новым инструкциям. Старая смена при этом не встаёт и не бросает трубки: каждый доводит свой разговор до конца и только потом уходит домой.

Какое-то время за пультами сидят обе смены сразу, и работают они по разным инструкциям. Это не сбой, это и есть механизм: пассажир, которому уже начали отвечать, дослушает ответ до конца.

restart - это другое. Это «всем встать и выйти, новая смена придёт через минуту».

Механизм: что делает reload по шагам

  1. Мастер читает новый конфиг. Если тот сломан - не происходит ничего: старые воркеры продолжают работать со старым конфигом, сайт жив.
  2. Конфиг в порядке - мастер поднимает новых воркеров с новыми настройками и передаёт им слушающие сокеты.
  3. Старым воркерам мастер говорит: новых соединений не принимать, доработать текущие и завершиться.
мастер новые воркеры старые воркеры принимают новые соединения дорабатывают текущие и завершаются поднять гасить в ps старые видны как «worker process is shutting down»
Две смены воркеров какое-то время работают одновременно и по разным конфигам. Именно поэтому скачивание из начала урока не обрывается.

Правило

Всегда nginx -t && systemctl reload nginx, одной строкой через &&. Проверка стоит секунду и снимает целый класс инцидентов.

Полный перезапуск нужен редко: смена user, обновление самого бинарника, в некоторых сборках - изменение worker_processes. Всё остальное - новые сайты, сертификаты, маршрутизация - применяется через reload.

Разбор: что видно в ps во время reload

Применили конфиг и сразу посмотрели на процессы:

$  команда
ps -o pid,stat,cmd -C nginx
↳  выводps -o pid,stat,cmd -C nginx
  PID STAT CMD
 1234 Ss   nginx: master process /usr/sbin/nginx
 4501 S    nginx: worker process
 4502 S    nginx: worker process
 1236 S    nginx: worker process is shutting down
 1237 S    nginx: worker process is shutting down

Два новых воркера и два старых с пометкой is shutting down. Это норма в первые секунды после reload.

Ненормально, если такие процессы висят часами. Значит на них остались живые соединения, и чаще всего это либо длинные загрузки, либо WebSocket, либо keepalive-соединения, которые клиент не закрывает.

По умолчанию мастер ждёт их бесконечно. Ограничить ожидание можно явно:

/etc/nginx/nginx.confглавный контекст
worker_shutdown_timeout 30s;

Тогда через тридцать секунд старые воркеры закроют что осталось и уйдут. Подробный разбор этого поведения - в разделе про эксплуатацию; здесь важно знать, что бесконечное ожидание - это умолчание, а не поломка.

Разбор: чего reload не делает

Из механизма легко унести «reload перечитывает всё». Он перечитывает конфиг - и только его. Три вещи, которые остаются как были.

DNS перерезолвится, но только в этот момент. Имена бэкендов разрешаются при загрузке конфига - и на старте, и на каждом reload. Проверено на стенде: контейнер переехал на новый адрес, без перезагрузки nginx отдавал 502, после reload - 200 с нового адреса. А вот САМ по себе, между перезагрузками, nginx имя не спрашивает никогда: переехал контейнер - и до ближайшего reload трафик идёт в никуда. Лечится это resolve у сервера группы, разбор в восьмом разделе.

Кеш не чистится. Ответы, сложенные в proxy_cacheРазбирается в разделе 11, глава «Кеширование ответов бэкенда»: Как устроен кеш nginx (кеш ответов бэкенда), переживают reload целиком.

Состояние лимитов не сбрасывается. Счётчики limit_reqРазбирается в разделе 12, глава «limit_req и limit_conn»: Как nginx считает частоту и limit_conn (ограничители частоты и числа соединений) живут в разделяемой памяти и продолжают считать.

И отдельно про рапорт об успехе: systemctl reload nginx в некоторых системах завершается успешно просто потому, что успешно послал сигнал. Что мастер решил дальше, systemd уже не знает. Поэтому проверка идёт до, а не после - и поэтому после применения стоит посмотреть в error_log.

Что ломается без этого

«Сделал restart, и сайт лёг на пять минут». Конфиг был сломан. reload отказался бы применять его молча и безопасно, restart попробовал стартовать и не смог.

«Старые воркеры висят вторые сутки». На них остались долгие соединения, а worker_shutdown_timeout не задан. Память при этом занята двумя поколениями воркеров сразу.

«Перевыпустили сертификат, reload сделали, браузер показывает старый». Скорее всего, reload применился не туда: сертификат прописан в другом server-блоке или файл перезаписан после команды. Проверяется nginx -T | grep ssl_certificate и датой файла.

Зачем это в работе

Первое - выкладка на живом сервере. Пять команд, тридцать секунд, и правка уезжает без единого потерянного запроса:

$  команда
nginx -t                    # синтаксис в порядке?
nginx -T | grep server_name # то ли я вообще правлю?
systemctl reload nginx      # применить
curl -sI https://shop.local # проверить, что жив
tail -f /var/log/nginx/error.log   # и посмотреть, не ругается ли

Второе - автоматика. В любом сценарии выкладки строка обязана быть именно nginx -t && systemctl reload nginx: без && скрипт бодро применит сломанный конфиг там, где человек бы остановился.

Теперь сам

В docker-compose сертификат обновляется по расписанию, после чего запускается docker exec nginx nginx -s reload. Однажды утром сайт отдаёт ошибку сертификата, хотя новый файл на месте и reload в логах прошёл. Что стоит проверить первым и почему reload мог не помочь?

Ответ. Первым - какой файл nginx считает своим: nginx -T | grep ssl_certificate и дату этого файла. Типичных причин две. Либо сертификат обновился по другому пути (симлинк live/ перевесили, а в конфиге прописан конкретный файл из archive/), и тогда reload честно перечитал старый. Либо reload выполнился РАНЬШЕ, чем скрипт дописал новый файл, - и тогда достаточно повторить его руками. Оба случая разводятся одной проверкой: путь из собранного конфига плюс ls -l по этому пути.

Главное

reload поднимает новых воркеров и даёт старым доработать - поэтому ни одно соединение не рвётся, а сломанный конфиг просто не применяется. restart такой страховки не даёт. Перечитывается только конфиг: кеш и счётчики лимитов остаются прежними, а имена бэкендов как раз спрашиваются заново. Пишем всегда одной строкой через &&.

Комментарии

Пока нет комментариев. Будь первым!

Оставить комментарий

Комментарий появится после проверки. Email не публикуется. Войти, чтобы не вводить имя каждый раз.