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

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

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

Коротко

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Правило

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

Полный перезапуск нужен редко: по сути, только при обновлении самого бинарника. Всё остальное - новые сайты, сертификаты, маршрутизация, число воркеров и даже user - применяется через 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-соединения сюда не относятся: их старый воркер закрывает сам, замер на 1.31.8 - через 0,1 секунды после reload.

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

/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. Новые воркеры примут новые запросы, старые завершат начатые в пределах заданного worker_shutdown_timeout. После команды проверь error_log и ответ сайта: отправка сигнала мастеру ещё не доказывает, что он смог применить изменения.

Комментарии

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

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

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