Healthcheck: как сделать страницу состояния своего сервиса
Healthcheck — специальный адрес, по которому сервис отвечает, здоров ли он. Сделанный правильно, он падает раньше, чем это заметят пользователи. Сделанный неправильно — либо врёт, либо сам становится причиной аварии.
Зачем нужен отдельный адрес
Проверять главную страницу для этой цели неудобно: она тяжёлая, кэшируется и может открываться даже тогда, когда база данных недоступна. Healthcheck решает обратную задачу — он лёгкий, не кэшируется и честно ломается, когда сломано что-то важное внутри.
Его читают сразу несколько потребителей: балансировщик решает, слать ли на этот экземпляр трафик; оркестратор — перезапускать ли контейнер; внешний мониторинг — будить ли дежурного. Отсюда и требования: быстрый ответ, однозначный код, никакой лишней информации наружу.
Три уровня проверки
Смешивать их в одном адресе — самая частая ошибка. Задачи у них разные.
| Уровень | Отвечает на вопрос | Кто читает |
|---|---|---|
| Liveness | Процесс жив и не завис? | Оркестратор — решает, перезапускать ли |
| Readiness | Готов ли принимать трафик прямо сейчас? | Балансировщик — решает, слать ли запросы |
| Deep / dependencies | Работают ли база, кэш, очередь, внешние API? | Мониторинг и дежурный |
Практическое следствие: если положить проверку базы данных в liveness, оркестратор начнёт перезапускать здоровые экземпляры при недоступной базе — перезапуск ничего не починит, а сервис будет метаться. Проверки зависимостей место в отдельном, более редком запросе.
Что отдавать в ответе
Главное — код ответа: 200 при здоровом сервисе и 503 при неготовности. Именно на код смотрят автоматика и внешний мониторинг, тело ответа для них вторично.
Тело полезно человеку, который открыл адрес во время разбора аварии. Достаточно короткого JSON со статусом и списком зависимостей:
{
"status": "ok",
"version": "1.4.2",
"checks": {
"db": "ok",
"cache": "ok",
"queue": "degraded"
}
}
Поле с версией экономит много времени: по нему сразу видно, доехал ли релиз до этого экземпляра. А состояние вроде «degraded» позволяет отличить «всё сломано» от «работает, но без второстепенной функции» — и не будить человека ночью из-за второго.
Четыре типичные ошибки
Слишком тяжёлый healthcheck. Если на каждый запрос выполняется несколько обращений к базе, а балансировщик опрашивает адрес раз в секунду с десятка узлов, проверка сама создаёт заметную нагрузку. Результаты проверки зависимостей стоит кэшировать на несколько секунд.
Ответ кэшируется. Прокси или CDN запоминает ответ «всё хорошо» и продолжает отдавать его, когда сервис уже лёг. Healthcheck обязан отдаваться с запретом кэширования — иначе он бесполезен.
Наружу уходит лишнее. Адреса внутренних серверов, версии библиотек, тексты ошибок с деталями подключения — подарок тому, кто изучает ваш сервис. Публичный healthcheck должен быть скупым, подробный — закрытым или доступным только из внутренней сети.
Проверка чужой доступности внутри своей. Если ваш healthcheck отдаёт 503 из-за недоступного внешнего API, балансировщик выведет из работы ваш совершенно здоровый сервис. Внешние зависимости отражайте статусом в теле ответа, а не кодом.
Healthcheck отвечает на вопрос «здоров ли сервис изнутри». Он ничего не скажет, если сломался DNS, истёк сертификат, лёг канал у провайдера или сайт закрыт файрволом от половины страны: изнутри всё выглядит прекрасно. Поэтому его опрашивают снаружи, из другой сети.
Пруфен опрашивает ваш healthcheck снаружи: проверка доступности с интервалом от 2 минут из 13 точек следит за кодом ответа и временем, а проверка контента — за строкой в теле ответа, так что можно отличить status ok от degraded. Уведомления приходят на Email, в Telegram, в Макс или звонком, а когда сервис поднимется — второе.
Частые вопросы
Закрывать ли healthcheck авторизацией? Публичный лёгкий адрес обычно оставляют открытым — иначе его не сможет опросить внешний мониторинг. Подробную версию с составом зависимостей закрывают: либо отдельным адресом во внутренней сети, либо простым токеном в параметре.
Какой адрес выбрать? Подойдёт любой предсказуемый: часто используют /health или /healthz. Важнее не имя, а то, чтобы он не попадал в кэш, не индексировался поисковиками и был описан в документации команды.
Нужен ли healthcheck небольшому сайту на CMS? Отдельный адрес обычно избыточен: хватает проверки ключевых страниц и содержимого на них. Healthcheck оправдан там, где есть балансировка, несколько экземпляров или зависимости, которые ломаются независимо от сайта.
Как быть с медленными зависимостями? Ставьте им короткий таймаут и кэшируйте результат. Проверка, которая сама ждёт ответа пять секунд, превращает healthcheck в источник ложных срабатываний по таймауту у всех, кто его опрашивает.