Health-checks и отказоустойчивость апстрима

Сервер в апстриме упал — а пользователи не должны этого заметить. Nginx умеет выводить мёртвые бэкенды из ротации автоматически.
«Хорошая балансировка — это не только раздать нагрузку, но и вовремя перестать отправлять её туда, где никто не отвечает.»

Бэкенды падают: деплой, перегрузка, сбой. Если Nginx продолжит слать туда запросы, пользователи получат ошибки. Поэтому в апстриме есть механизмы определения «здоровья» серверов и автоматического исключения мёртвых.

Пассивные проверки: max_fails и fail_timeout

upstream backend {
    server 10.0.0.1 max_fails=3 fail_timeout=30s;
    server 10.0.0.2 max_fails=3 fail_timeout=30s;
}

Это пассивные проверки: Nginx судит о здоровье по реальным запросам. Если в течение fail_timeout (30s) случилось max_fails (3) неудач, сервер помечается недоступным на fail_timeout и не получает запросов. По истечении срока Nginx снова осторожно попробует его.

   сервер B: ошибка, ошибка, ошибка  (3 за 30с)
            |
            v
   помечен DOWN на 30с -> трафик идёт только на A
            |
            v
   через 30с -> пробный запрос; ОК -> вернулся в ротацию

Резервные серверы и переключение

upstream backend {
    server 10.0.0.1;
    server 10.0.0.2;
    server 10.0.0.9 backup;     # включается, только если основные легли
}
location / {
    proxy_pass http://backend;
    proxy_next_upstream error timeout http_502 http_503 http_504;
    proxy_next_upstream_tries 2;
}

backup-сервер молчит, пока живы основные, и подхватывает нагрузку при их отказе. proxy_next_upstream говорит, при каких ошибках Nginx должен повторить запрос на другом сервере апстрима — это даёт прозрачное восстановление: клиент даже не узнает, что первый бэкенд не ответил.

Как работает под капотом

Пассивные проверки бесплатны: Nginx уже отправляет запросы, ему лишь нужно считать неудачи. Минус — он узнаёт о падении только когда чей-то запрос уже провалился (этот один запрос «принимает удар», но proxy_next_upstream повторит его на здоровом сервере). Активные health-checks (Nginx сам периодически дёргает /health до запросов пользователей) есть в коммерческом Nginx Plus и в Open Source реализуются внешними средствами. Важно: повторять (proxy_next_upstream) безопасно только идемпотентные операции — иначе можно дважды списать платёж.

Частые ошибки

  • Полагаться на default max_fails=1. Один случайный таймаут выкинет живой сервер из ротации; для шумной сети подними порог.
  • proxy_next_upstream на неидемпотентных POST. Повтор платежа = двойное списание. Ограничь повторы безопасными методами.
  • Думать, что Open Source Nginx делает активные проверки сам. Из коробки — только пассивные.

Best practices

  • Задавай max_fails и fail_timeout явно под свою сеть, не полагайся на дефолты.
  • Держи backup-сервер для аварийного режима.
  • proxy_next_upstream — только для безопасных к повтору ситуаций; для платежей будь осторожен.
  • Заведи на бэкенде лёгкий эндпоинт /health для внешнего мониторинга.

Пассивные и активные проверки на практике

Разница между пассивными и активными проверками здоровья определяет, как быстро пользователи почувствуют падение бэкенда. При пассивной проверке (то, что умеет open-source Nginx) сервер узнаёт о проблеме только когда реальный запрос пользователя провалится: этот невезучий запрос «принимает удар на себя», а Nginx после нескольких таких неудач выводит бэкенд из ротации. Спасает то, что proxy_next_upstream тут же повторяет пострадавший запрос на здоровом сервере, так что пользователь обычно ничего не замечает. Но окно между падением и срабатыванием max_fails существует.

Активные проверки решают это иначе: Nginx сам, в фоне, периодически дёргает специальный эндпоинт /health на каждом бэкенде — ещё до того, как туда пойдёт пользовательский трафик. Упавший сервер исключается превентивно. В чистом open-source Nginx этой функции нет (она в коммерческом Nginx Plus), но на практике её роль выполняют внешние инструменты: оркестраторы вроде Kubernetes делают liveness/readiness-пробы и сами убирают нездоровые поды из сервиса, а системы вроде Consul обновляют список апстримов. Поэтому хорошая привычка — всегда заводить на бэкенде лёгкий эндпоинт /health, который быстро отвечает «жив» (а в продвинутом варианте ещё и проверяет доступность базы). Этот маленький маршрут становится точкой опоры для всей системы отказоустойчивости, кто бы ни выполнял проверки — сам Nginx, оркестратор или внешний мониторинг.

Итоги

Nginx выводит упавшие бэкенды из ротации пассивно через max_fails/fail_timeout, держит backup-серверы на случай аварии и прозрачно повторяет запросы через proxy_next_upstream (осторожно с неидемпотентными). Активные health-checks — за Nginx Plus или внешними инструментами. На этом балансировка завершена — переходим к финальному разделу: HTTPS, безопасность и оптимизация прода.

Проверьте себя
1. Что делают параметры max_fails и fail_timeout?
AОграничивают размер файла
BПосле max_fails неудач за fail_timeout сервер считается недоступным на fail_timeout и не получает запросов
CЗадают таймаут TLS
DВключают сжатие
2. Почему proxy_next_upstream опасен для неидемпотентных POST-запросов?
AОн замедляет ответ
BПовтор запроса на другом сервере может выполнить операцию дважды — например, списать платёж два раза
CОн отключает HTTPS
DОн ломает кеш