Мониторинг балансировщиков нагрузки. Backend health, connection saturation и ошибки 5xx
Балансировщик нагрузки — один из ключевых компонентов современной инфраструктуры. Именно через него проходит весь пользовательский трафик, а значит, любые проблемы на этом уровне почти сразу отражаются на доступности сервисов.
При этом многие ограничиваются проверкой доступности самого балансировщика, хотя гораздо важнее понимать, как он взаимодействует с backend-серверами, справляется ли с текущей нагрузкой и не становится ли сам узким местом.
Разберём метрики, которые действительно помогают обнаруживать проблемы на ранней стадии, и то, откуда их брать в HAProxy, Nginx и облачных балансировщиках.
Почему проверки доступности недостаточно
Если балансировщик отвечает на запросы, это ещё не значит, что сервис работает корректно. Одновременно может происходить следующее:
- часть backend-серверов недоступна, но остаётся в пуле;
- очередь ожидания растёт, а время ответа увеличивается;
- соединения упираются в лимит, и новые подключения отклоняются;
- пользователи получают 502 или 504.
Формально балансировщик жив. Фактически сервис уже деградировал.
Здоровье пула backend-серверов
Это метрика номер один. Важно не только текущее количество живых серверов, но и то, как часто их состав меняется.
Отслеживать стоит:
- количество серверов в состоянии UP;
- частоту переходов UP → DOWN → UP (flapping);
- количество неудачных health check;
- время, которое сервер провёл вне пула.
В HAProxy состояние пула доступно через Runtime API. Первая строка вывода — CSV-заголовок с именами полей:
echo "show stat" | socat stdio /var/run/haproxy/admin.sock | cut -d, -f1,2,18
# web-backend,app-01,UP
# web-backend,app-02,DOWNВ Prometheus для тех же данных используется haproxy_server_up, в облачных балансировщиках — HealthyHostCount и UnHealthyHostCount по каждой target group.
С Nginx есть нюанс: в открытой версии состояние upstream-серверов наружу не отдаётся, stub_status показывает только соединения. Косвенно выпадение backend видно по логам, если в них включены $upstream_addr и $upstream_status — у неудачных попыток там будет несколько адресов через запятую.
Даже один сервер, который периодически выпадает и возвращается, — уже повод для разбора: обычно это первый признак проблем с приложением, а не со здоровьем самого узла.
Health check, который вводит в заблуждение
Проверка работоспособности сама по себе бывает источником инцидентов. Три типичных случая:
- Слишком поверхностная проверка. Эндпоинт отвечает 200, пока жив веб-сервер, — приложение при этом может не подключаться к базе. Балансировщик продолжает слать трафик на нерабочий узел.
- Слишком глубокая проверка. Если
/healthходит в базу, то при недоступности базы health check не проходит сразу на всех узлах и пул опустеет целиком. Отказ одной зависимости превращается в полный отказ сервиса. - Слишком медленная реакция. При интервале 10 секунд и трёх неудачных попытках подряд узел покидает пул только через полминуты, и всё это время часть запросов уходит в никуда.
Разумный компромисс — лёгкая проверка для балансировщика (процесс жив, порт слушается, приложение инициализировано) и отдельный, более подробный эндпоинт для мониторинга, который не влияет на маршрутизацию трафика.
Активные соединения и очередь на приём
Количество соединений показывает текущую нагрузку, но полезнее смотреть на него в разрезе «клиентские против backend» и в динамике.
ss -s # сводка по всем сокетам, включая TIME-WAIT
ss -ltn # очередь на приём у слушающих сокетов
# State Recv-Q Send-Q Local Address:Port
# LISTEN 0 511 0.0.0.0:443Для слушающего сокета Recv-Q — это текущее число установленных соединений, ожидающих accept(), а Send-Q — максимум очереди. Если Recv-Q регулярно приближается к Send-Q, балансировщик не успевает разбирать входящие соединения, и часть клиентов получит таймаут вместо ответа. Подробнее эта механика разобрана в статье про SYN flood и тюнинг backlog.
Резкий рост числа соединений обычно означает одно из четырёх: всплеск трафика, атаку, замедление backend (соединения живут дольше) или сломанный keep-alive.
Connection saturation: где именно кончается ресурс
Балансировщик может упереться в лимит при почти простаивающем процессоре. Проверять стоит все лимиты сразу, потому что заканчиваются они по очереди:
| Что заканчивается | Где смотреть | Порог для тревоги |
|---|---|---|
| Лимит соединений | maxconn в HAProxy, worker_connections × worker_processes в Nginx | 70% от лимита |
| Файловые дескрипторы | LimitNOFILE в unit-файле, ls /proc/<pid>/fd | wc -l | 70% от лимита |
| Локальные порты | net.ipv4.ip_local_port_range, доля TIME-WAIT в ss -s | 70% диапазона |
| Таблица conntrack | nf_conntrack_count против nf_conntrack_max | 80% |
Последний пункт особенно коварен: при переполнении таблицы отслеживания соединений пакеты начинают молча отбрасываться, а в dmesg появляется строка nf_conntrack: table full, dropping packet.
cat /proc/sys/net/netfilter/nf_conntrack_count /proc/sys/net/netfilter/nf_conntrack_maxОшибки 502, 503 и 504 означают разное
Все три относятся к 5xx, но указывают на разные места в цепочке — и это главный инструмент быстрой локализации.
- 502 Bad Gateway — backend принял соединение, но ответил некорректно или закрыл его раньше времени. Чаще всего это упавший или перезапускающийся процесс приложения.
- 503 Service Unavailable — балансировщику некуда отправить запрос: в пуле не осталось серверов в состоянии UP, либо исчерпан
maxconnи очередь ожидания. - 504 Gateway Timeout — соединение установлено, но ответ не пришёл за отведённое время (
timeout serverв HAProxy,proxy_read_timeoutв Nginx). Backend жив, но слишком медленный.
Полезно также отделять ошибки, которые сгенерировал сам балансировщик, от тех, что пришли от приложения. В Nginx признак — пустой $upstream_status при 5xx в $status; в HAProxy эту роль играет поле termination state в логе.
Считать имеет смысл долю, а не абсолютное количество:
sum(rate(haproxy_server_http_responses_total{code="5xx"}[5m]))
/ sum(rate(haproxy_server_http_responses_total[5m]))Порог в 1% на пятиминутном окне — разумная отправная точка для алерта, дальше его подгоняют под реальный профиль трафика, чтобы не спровоцировать усталость от алертов.
Ошибки 4xx тоже стоит наблюдать, но они обычно говорят о клиентах, а не об инфраструктуре. Исключение — всплеск 429, который означает, что сработал rate limiting.
Время ответа backend отдельно от общего
Балансировщик — единственная точка, где видно, из чего складывается время ответа. HAProxy разбивает его на отдельные таймеры (Tq, Tw, Tc, Tr, Tt), и самый показательный из них — Tw, время ожидания в очереди. Если растёт именно он, backend'ов просто не хватает.
В Nginx роль тех же таймеров играют переменные $upstream_connect_time, $upstream_header_time и $upstream_response_time:
log_format upstream '$status $request_time uct=$upstream_connect_time '
'uht=$upstream_header_time urt=$upstream_response_time '
'addr=$upstream_addr ustatus=$upstream_status';Рост upstream_connect_time — это сеть или переполненная очередь на приём у backend. Рост upstream_response_time при нормальном connect — замедление самого приложения. Смотреть, как обычно, лучше на высокие перцентили: p99 растёт заметно раньше среднего.
Неудачные подключения, ретраи и таймауты
Балансировщик замечает нестабильность backend раньше всех остальных. В статистике HAProxy за это отвечают счётчики econ (ошибки подключения), eresp (ошибки ответа), wretr (повторные попытки) и wredis (перенаправления на другой сервер).
Единичные всплески ретраев — нормальный фон. Устойчивый рост означает, что часть узлов уже нездорова, хотя формально health check ещё проходят. Это тот самый ранний сигнал, ради которого мониторинг балансировщика и настраивают.
Равномерность распределения нагрузки
Если один backend получает заметно больше запросов, чем остальные, причиной могут быть:
- неудачный алгоритм балансировки для данного профиля трафика;
- sticky sessions, привязавшие крупных клиентов к одному узлу;
- разная производительность серверов в пуле;
- долгоживущие keep-alive соединения — после добавления нового узла трафик на него не перетекает, пока старые соединения не закроются.
В облачных балансировщиках к этому добавляется распределение по зонам доступности: при неравном количестве узлов в зонах часть трафика может уходить на меньшую группу и перегружать её.
TLS-слой тоже нужно мониторить
Терминация TLS обычно происходит именно на балансировщике, поэтому здесь же появляются и связанные с ней проблемы:
- срок действия сертификата;
- ошибки handshake после обновления конфигурации;
- рост времени handshake при всплеске новых соединений;
- отсутствие нужного сертификата для конкретного SNI.
Про то, почему срок сертификата стоит контролировать отдельной проверкой, мы писали подробнее — просроченный сертификат остаётся одной из самых частых причин полной недоступности сервиса.
Минимальный набор алертов
Если начинать с нуля, этих правил достаточно для покрытия большинства реальных сбоев:
| Условие | Почему важно |
|---|---|
| Живых backend меньше половины пула | Оставшиеся узлы примут удвоенный трафик и уйдут следом |
| Доля 5xx больше 1% за 5 минут | Пользователи уже видят ошибки |
| Занято больше 70% лимита соединений | Запас до отказа в подключении — минуты |
| p99 времени ответа backend выше нормы в 2 раза | Ранний признак деградации до появления ошибок |
| Больше 3 переходов UP/DOWN за 10 минут | Flapping: узел нестабилен, но формально жив |
| До окончания TLS-сертификата меньше 14 дней | Отказ будет полным и внезапным |
Частые вопросы
Достаточно ли метрик самого балансировщика?
Нет. Они собираются с него же и пропадают вместе с ним — при отказе узла или сети до него в графиках будет не всплеск ошибок, а пустота. Поэтому внутренние метрики дополняют внешними проверками из независимых точек.
Что мониторить, если балансировщик управляемый и метрик почти нет?
У облачных балансировщиков минимально нужны четыре метрики: количество здоровых узлов, доля 5xx, время ответа target group и счётчик отклонённых подключений. Всё остальное собирается уже на самих backend-серверах.
Как отличить проблему балансировщика от проблемы приложения?
По коду ответа и таймерам. Пустой upstream-статус, рост времени в очереди и отклонённые подключения указывают на балансировщик; заполненный upstream-статус с 500 и растущее время ответа backend — на приложение.
Заключение
Мониторинг балансировщика не должен ограничиваться проверкой того, что он отвечает. Приоритетный набор выглядит так:
- состояние пула backend и частота его изменений;
- корректность самих health check;
- активные соединения и очередь на приём;
- насыщение лимитов: соединения, дескрипторы, порты, conntrack;
- доля 5xx с разделением на 502, 503 и 504;
- время ответа backend по перцентилям и время ожидания в очереди;
- ошибки подключения и ретраи;
- равномерность распределения и срок TLS-сертификата.
Вместе они позволяют увидеть деградацию за десятки минут до того, как пользователи начнут массово получать ошибки. А чтобы отличить проблему инфраструктуры от проблемы приложения, всегда полезно иметь вторую точку зрения — расследование по метрикам как раз про это.
Настроить мониторинг за 30 секунд
Надежные оповещения о даунтаймах. Без ложных срабатываний