АрхитектураОблако и инфраструктураИнженер по эксплуатации облачной инфраструктуры

Внешний балансировщик продолжает отправлять трафик на доступный по сети, но неработающий экземпляр. Как hea...

Внешний балансировщик продолжает отправлять трафик на доступный по сети, но неработающий экземпляр. Как health check исключает его из распределения?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Health check регулярно проверяет экземпляр по заданному протоколу и признаку успешности: например, устанавливает TCP-соединение или отправляет HTTP-запрос. После нескольких последовательных неудач балансировщик помечает экземпляр как нездоровый и исключает его из нового распределения трафика; после серии успешных проверок возвращает его в пул.

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

Исторический контекст

Ранние схемы распределения нагрузки часто ориентировались на наличие узла в сети или на успешное TCP-соединение. Такой подход не обнаруживал зависшее приложение, исчерпанные рабочие потоки, неработающую зависимость или ситуацию, когда процесс принимает соединение, но не способен обработать запрос.

Health check появился как механизм отделения физической и сетевой доступности от готовности обслуживать трафик. Это позволило балансировщику автоматически выводить неисправные экземпляры из пула без изменения маршрутов вручную.

Постановка проблемы

Экземпляр может иметь рабочий IP-адрес и принимать TCP-соединения, но возвращать ошибки, зависать или отвечать слишком медленно. Если балансировщик продолжит направлять на него запросы, пользователи получат ошибки, увеличатся задержки, а повторные попытки могут перегрузить ещё работающие экземпляры.

Слишком поверхностная проверка создаёт ложное ощущение исправности, а слишком строгая или неправильно настроенная проверка может исключить здоровые экземпляры. Дополнительный риск возникает при массовом отказе: одновременное исключение многих экземпляров оставляет слишком маленький пул для обслуживания нагрузки.

Подробное решение

Балансировщик выполняет проверки с заданным интервалом. Проверка может быть сетевой, например TCP, либо прикладной, например HTTP-запрос к специальной точке, которая возвращает успешный результат только при выполнении необходимых условий.

Обычно учитываются следующие параметры:

  • тайм-аут — сколько ждать ответ одной проверки;
  • интервал — как часто выполнять проверки;
  • порог нездорового состояния — сколько последовательных неудач нужно для исключения;
  • порог восстановления — сколько успешных проверок нужно для возврата в пул;
  • тип проверки — TCP, HTTP, HTTPS или другой поддерживаемый протокол;
  • критерий успеха — например, допустимый диапазон кодов ответа и успешное завершение соединения.

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

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

Важно различать health check балансировщика и readiness check оркестратора. Первый управляет включением экземпляра в конкретный пул балансировки, второй сообщает оркестратору, можно ли направлять трафик к рабочему экземпляру на уровне платформы. Их согласуют по смыслу, иначе один слой может считать экземпляр готовым, а другой — нет.

При обнаружении отказа новые запросы перестают направляться на экземпляр, но уже установленные соединения не обязательно немедленно прерываются. Для вывода из эксплуатации нужен отдельный механизм draining или graceful removal, который позволяет завершить принятые запросы. Health check также не гарантирует корректность бизнес-ответа: приложение может возвращать формально успешный статус с неверными данными.

Ситуация из практики

У сервиса было шесть экземпляров за балансировщиком. После зависания пула соединений каждый экземпляр продолжал принимать TCP-соединения, поэтому TCP-проверка считала все экземпляры здоровыми, хотя HTTP-запросы завершались тайм-аутами.

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

Выбрали отдельную лёгкую HTTP-проверку, которая подтверждала работоспособность HTTP-сервера и критического пула соединений, но не выполняла запись и не обращалась к необязательным зависимостям. После двух неудач экземпляр исключался из новых запросов, после трёх успешных проверок возвращался в пул, а перед удалением включался короткий период draining.

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

Что кандидаты часто упускают

  1. **Достаточно ли проверять только TCP-порт?

Нет. TCP-проверка подтверждает, что на адресе принимаются соединения, но не показывает, может ли приложение обработать запрос. Процесс может зависнуть после установления соединения, исчерпать пул потоков или возвращать ошибки на уровне HTTP.

TCP-проверка уместна, когда балансировщику нужно проверить только транспортную доступность или когда протокол выше TCP не поддерживается. Для веб-сервиса обычно нужна прикладная проверка с аккуратно выбранными критериями успеха.

  1. **Почему нельзя исключать экземпляр после одной неудачной проверки?

Одиночная неудача может быть вызвана потерей пакета, кратким сетевым всплеском или временной перегрузкой проверяемого процесса. Немедленное исключение повышает вероятность ложного срабатывания и может уменьшить пул сразу при кратковременной проблеме.

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

  1. **Почему после успешного health check запрос всё ещё может завершиться ошибкой?

Проверка отражает состояние в конкретный момент и обычно охватывает ограниченный путь выполнения. Сразу после успешной проверки зависимость может отказать, экземпляр может быть перегружен, а реальный запрос может использовать функциональность, которую проверка не затрагивает.

Health check не заменяет мониторинг, тайм-ауты, ограничение повторных попыток и контроль ошибок в самом приложении. Он лишь помогает балансировщику принимать решение о направлении новых запросов; корректность каждой операции должны обеспечивать остальные уровни системы.