← Все статьи

SiteGuard17 мин чтения

Health check сайта: что проверять в /health и как разделить liveness и readiness

Health check сайта — это проверка диагностического адреса, который сообщает о конкретном состоянии приложения: выполняется ли его код, готово ли оно принимать запросы или завершён ли запуск. Полезный /health отвечает на заранее определённый вопрос. Если он всегда возвращает 200 OK, даже когда приложение не может обслужить посетителя, такой адрес создаёт ложное ощущение исправности.

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

Что именно должен доказывать адрес /health

Название маршрута не определяет глубину проверки. Статический файл с текстом OK доказывает, что его смог выдать обслуживающий этот запрос компонент. Если файл возвращает Nginx, он не подтверждает выполнение PHP, работу CMS или доступность базы. Если ответ сформирован обработчиком приложения, доказательство уже другое: запрос дошёл до этого обработчика и тот смог выполнить свой код.

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

В архитектурном шаблоне Health Endpoint Monitoring от Microsoft отдельно рассматриваются простые проверки приложения и более глубокие проверки необходимых зависимостей. Это хорошая отправная точка: состав проверки определяется её назначением, а дополнительные подробности не обязаны попадать в публичный ответ.

Зафиксируйте контракт до реализации:

  • Объект: конкретный процесс, экземпляр приложения или публичный сервис за балансировщиком.
  • Условие успеха: какие возможности должны сохраняться, чтобы ответ считался положительным.
  • Потребитель: внешняя система мониторинга, балансировщик, оркестратор или дежурный инженер.
  • Действие при отказе: уведомить, убрать экземпляр из маршрутизации, дождаться запуска или перезапустить.
  • Границы: время ответа, свежесть данных, доступ к адресу и условия возврата в рабочее состояние.

Один URL подходит нескольким потребителям, если им нужен одинаковый смысл ответа. В остальных случаях проще поддерживать несколько небольших маршрутов.

Liveness, readiness и startup: в чём разница

Эти названия обозначают разные вопросы. Liveness оценивает жизнеспособность самого приложения; readiness — готовность экземпляра принимать обслуживаемую им работу; startup — завершение первоначального запуска. Пути /health/live, /health/ready и /health/startup ниже приведены как удобное соглашение команды, а не обязательный стандарт.

ПроверкаНа какой вопрос отвечаетЧто разумно включитьКак использовать отказ
LivenessСпособен ли процесс выполнять работу или он безнадёжно завис?Дешёвую проверку внутри приложения, отражающую его способность продвигаться в работе.Рассмотреть перезапуск, если именно он устраняет обнаруживаемую неисправность.
ReadinessМожно ли этому экземпляру сейчас направлять новые запросы?Готовность конфигурации, завершение локальной инициализации, отсутствие режима вывода из обслуживания, согласованные критические условия.Временно исключить экземпляр из распределения новых запросов, сохранив процесс.
StartupДошло ли приложение до состояния, после которого применимы обычные проверки?Завершение необходимых этапов запуска, загрузку локальных данных и подготовку обработчиков.Дать запуску ограниченное время; при превышении бюджета разбирать причину.

В Kubernetes после установленного числа последовательных неудач liveness или startup kubelet завершает контейнер, а его повторный запуск подчиняется restartPolicy. Неудача readiness делает Pod неготовым для обычного трафика соответствующих Service; контейнер она не перезапускает. При наличии startup-пробы liveness и readiness не выполняются до её успеха; затем startup больше не проверяется до следующего старта. Эти различия описаны в официальной документации Kubernetes.

На обычном сайте без Kubernetes автоматических действий из названий не возникает. Хостинг не обязан понимать /health/ready: отдельно настройте мониторинг и, если он есть, балансировщик. На одном сервере endpoint может служить источником сигнала: «приложение отвечает, однако каталог не получает данные».

Как выбрать зависимости для проверки готовности

Список зависимостей начинайте с пользовательских действий. Для сайта услуг критична отправка заявки; для магазина — просмотр товара и оформление заказа; для кабинета — вход и получение данных. У этих функций могут быть разные требования. Доступность почтового сервиса не обязательно нужна для показа каталога, а доступность основного хранилища заказов обычно нужна для сохранения покупки.

Составьте карту: функция, зависимость, обходной вариант, действие при отказе. Ниже — пример решения для условного магазина; адаптируйте его к своей архитектуре.

КомпонентЧто зависит от негоВозможное решение для readinessЧто проверять отдельно
Основная база заказовСоздание и чтение заказаДля экземпляра сервиса заказов отсутствие обязательного доступа означает неготовность; для витрины с самостоятельным источником данных правило может быть другим.Возможность выполнить нужную операцию и ошибки реальных запросов.
Кеш каталогаСкорость выдачи товаровСохранить готовность, если доступен проверенный резервный путь и его мощности достаточно.Рост задержек и нагрузку на резервный источник.
Почтовый провайдерПисьмо после заказаНе выключать витрину, если заказ надёжно сохранён, а уведомление поставлено в очередь.Задержку доставки и накопление недоставленных уведомлений.
Сервис рекомендацийДополнительный блок товаровОбычно оставить основную функцию доступной с упрощённым представлением.Фактическую потерю блока и длительность деградации.

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

Что доказывает проверка базы данных

Короткий запрос вроде SELECT 1 может подтвердить выполнение простейшей команды через конкретное подключение. Он не подтверждает наличие нужных таблиц, права на запись, применённые миграции, успешную оплату или корректность бизнес-данных. Формулируйте результат соответственно: «контрольный запрос выполнен», а не «база полностью исправна».

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

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

Общая зависимость и частичная деградация: как не ухудшить аварию

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

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

  • Полезная работа невозможна. Отказ готовности может быть честным результатом. Предусмотрите предсказуемую страницу временной недоступности на подходящем уровне и самостоятельный сигнал об общей зависимости.
  • Сохранилась часть функций. Поддерживайте допустимый режим: например, просмотр заранее подготовленной витрины без возможности отправить заказ. Не обещайте пользователю операцию, результат которой нельзя сохранить.
  • Функции имеют разные зависимости. Если архитектура позволяет, разделите маршрутизацию и признаки готовности по сервисам. Один общий флаг для витрины, личного кабинета и оплаты часто слишком груб.

Состояние «degraded» само по себе не определяет HTTP-код или поведение балансировщика. В стандартном сопоставлении ASP.NET Core Healthy и Degraded возвращают 200, а Unhealthy — 503. Это настройка конкретного средства. Если деградация сохраняет трафик, о потерянной функции нужно сообщать по другому согласованному признаку.

Изучите и поведение инфраструктуры при отказе всех целей. Например, Application Load Balancer в AWS при группе, состоящей только из нездоровых зарегистрированных целей, может направлять запросы на них по принципу fail-open. Нельзя переносить это поведение на другой балансировщик или предполагать, что любая неуспешная readiness полностью остановит обращения. Проверяйте свой конечный маршрут трафика.

HTTP-код и тело ответа: простой контракт без двусмысленности

Для небольшого endpoint удобно выбрать два исхода: 200 при выполнении условий проверки и 503, если они не выполнены. Это проектное соглашение с потребителем. Разветвлённый набор состояний не поможет, если мониторинг различает только успех и отказ.

Минимальный ответ готового экземпляра может выглядеть так. Показаны значимые заголовки и тело; транспортные заголовки добавляет HTTP-сервер.

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: no-store

{"status":"ready"}

Если обязательное условие нарушено, тело остаётся небольшим, а код меняется вместе с результатом:

HTTP/1.1 503 Service Unavailable
Content-Type: application/json; charset=utf-8
Cache-Control: no-store

{"status":"not_ready"}

Не возвращайте 200 с текстом «database unavailable» в расчёте на чтение JSON. HTTP-проба Kubernetes считает успешными коды от 200 до 399 и не принимает решение по вашему полю status. Для внешнего мониторинга и балансировщика отдельно проверьте критерии успеха, анализ содержимого и обработку перенаправлений.

По той же причине health endpoint лучше выдавать итоговый ответ напрямую, без перехода на форму входа, главную страницу или другой домен. Редирект может оказаться успешным с точки зрения инструмента, хотя диагностический обработчик вообще не был выполнен. Не используйте HTML-страницу ошибки с кодом 200 как ответ такого маршрута.

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

Таймауты и стоимость: проверка не должна усиливать нагрузку

У health check должен быть собственный бюджет времени. Если система ждёт ответ две секунды, а обработчик способен десять секунд ожидать базу, посетитель проверки уже получил таймаут, но работа на сервере может продолжаться. При регулярных запросах такие незавершённые операции накапливаются. Устанавливайте ограничения и у внешнего потребителя, и внутри проверяемого приложения.

Разделите три величины: предел отдельной операции, общий срок завершения health endpoint и время ожидания его клиента. В учебной схеме операция к зависимости может иметь предел 300 мс, endpoint — 700 мс, а внутренний потребитель — 1 с. Это пример соотношения бюджетов, не готовые значения для любого сайта. Для публичного контроля дополнительно учитывайте сеть, TLS и обычные задержки вашего сервиса.

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

Базовую частоту можно оценить так:

Запросов health в секунду ≈
число экземпляров × число независимо опрашивающих потребителей
÷ интервал проверки в секундах

Например, 20 экземпляров, два потребителя на каждый и интервал 10 секунд дают примерно четыре health-запроса в секунду. Если каждый выполняет по три обращения к зависимостям, это около 12 дополнительных операций в секунду до учёта повторов. Расчёт условный: внешний монитор публичного балансируемого адреса обычно не опрашивает каждую реплику напрямую, поэтому его нагрузку следует считать отдельно.

Уберите из частых проверок полные выборки таблиц, массовое чтение файлов и цепочки взаимных запросов «сервис A проверяет B, B проверяет A». Не добавляйте много внутренних повторов: они удлиняют ответ и усиливают нагрузку именно тогда, когда зависимость уже испытывает проблемы. Повторную оценку обычно удобнее поручить следующему запланированному опросу и его порогам.

Как исключить ложный успех из-за кеша

Health endpoint должен отражать достаточно свежее состояние. Иначе CDN или кеш страниц способен продолжить возвращать прежний положительный ответ после остановки приложения. Для диагностического ответа обычно подходит Cache-Control: no-store: директива запрещает сохранять его в частных и общих HTTP-кешах. no-cache имеет другой смысл — допускает хранение с проверкой перед повторным использованием. Различие описано в справочнике Cache-Control.

Заголовок — часть решения. Исключите маршрут из кеширования CDN, прокси, CMS и плагина страниц, если такие слои используются. Проверьте, не подменяет ли прокси ошибку приложения собственной успешной страницей. После изменения правила учитывайте ранее сохранённые ответы: убедитесь, что они больше не обслуживают диагностический URL.

Самая понятная проверка — контролируемый отрицательный сценарий в тестовом окружении. Сначала получите положительный ответ через тот же маршрут, которым пользуется мониторинг. Затем сделайте обязательное условие невыполненным и убедитесь, что результат изменился за допустимое время. Просмотр заголовков при исправном приложении такого доказательства не даёт.

Отдельный случай — намеренное хранение результата внутри приложения. Иногда зависимости проверяет фоновый механизм, а endpoint быстро отдаёт последнее наблюдение. Такая схема допустима при явном сроке свежести: измеряйте возраст результата, определите реакцию на остановку фоновой проверки и не выдавайте бесконечно старое «готов» за актуальное состояние. После запуска состояние должно оставаться неизвестным до первого достоверного наблюдения, если именно оно требуется контрактом.

Как внедрить health endpoint на обычном сайте

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

  1. Определите проверяемую функцию. Например, экземпляр способен отдавать динамический каталог с обязательными данными.
  2. Выберите минимальные условия. Конфигурация загружена, обязательная инициализация завершена, нужная операция к источнику данных укладывается в ограничение.
  3. Разделите последствия. Проверка для рестарта не наследует автоматически все проверки готовности.
  4. Настройте маршрут. Зафиксируйте метод, путь, код, заголовки, доступ и отсутствие редиректов.
  5. Подключите потребителя. Убедитесь, что он проверяет именно согласованный адрес и правильно трактует отказ.
  6. Проверьте отказ и восстановление. Оба перехода должны быть наблюдаемыми и приводить к ожидаемому действию.

Ниже — псевдокод логики readiness, а не готовая программа. Названия функций условные; реализация ограничений времени, отмены операций и доступа зависит от вашего стека.

GET /health/ready:
    установить Cache-Control: no-store
    установить Content-Type: application/json; charset=utf-8

    если инициализация не завершена:
        вернуть 503, {"status":"not_ready"}

    если экземпляр выведен из приёма новых запросов:
        вернуть 503, {"status":"not_ready"}

    выполнить согласованные обязательные проверки
        с отдельными ограничениями и общим сроком завершения

    если есть ошибка, таймаут или недопустимо старый результат:
        записать безопасную причину во внутреннюю диагностику
        вернуть 503, {"status":"not_ready"}

    вернуть 200, {"status":"ready"}

Метод GET по своей семантике предназначен для получения данных; это отражено в документации HTTP GET. Не размещайте в таком обработчике создание заказа, списание платежа, отправку письма или очистку данных. Логи и измерение длительности допустимы как сопровождение запроса, но бизнес-операции требуют отдельного, осознанно спроектированного теста.

Пример настройки probes в Kubernetes

Этот фрагмент предназначен для добавления внутрь описания одного контейнера, где приложение уже слушает порт 8080 и реализует перечисленные маршруты. Это пример для обсуждения с разработчиком: он не создаёт endpoint и не является полным манифестом развёртывания. Числа нужно подобрать по измерениям запуска и работы вашего приложения.

startupProbe:
  httpGet:
    path: /health/startup
    port: 8080
  periodSeconds: 5
  timeoutSeconds: 1
  failureThreshold: 24

livenessProbe:
  httpGet:
    path: /health/live
    port: 8080
  periodSeconds: 10
  timeoutSeconds: 1
  failureThreshold: 3

readinessProbe:
  httpGet:
    path: /health/ready
    port: 8080
  periodSeconds: 5
  timeoutSeconds: 1
  failureThreshold: 2
  successThreshold: 2

Для запуска здесь выделен ориентировочный бюджет порядка 24 × 5 = 120 секунд до исчерпания серии неудач. Это не точный секундомер: учитывайте момент первого опроса, длительность запросов и работу платформы. Readiness требует двух успешных наблюдений для восстановления, чтобы не возвращать экземпляр в обслуживание после единственного случайного успеха. Значения и ограничения полей приведены в справочнике параметров probes.

Не рассчитывайте время обнаружения только как «интервал умножить на порог». Отказ может начаться между опросами; после решения ещё требуется распространение состояния до потребителя маршрутизации. Измерьте переход целиком в своём окружении. Аналогично проверяйте восстановление: процесс уже может отвечать, но ещё не иметь прогретых данных или достаточного числа соединений.

Не привязывайте пробу Pod к публичному балансируемому имени: ответ может прийти от другого экземпляра. Обычно kubelet обращается к IP Pod; при HTTPS он пропускает проверку сертификата, поэтому не подтверждает исправность публичного TLS. Для виртуального хоста при необходимости задайте ожидаемый заголовок Host.

Внешняя и внутренняя проверка дополняют друг друга

Внутренний endpoint отвечает на вопрос о компоненте в его рабочем окружении. Внешний запрос к публичному имени проходит дополнительный путь: разрешение домена, HTTPS, CDN или защитный шлюз, балансировку и маршрутизацию. Если внутренняя readiness успешна, а публичный URL не открывается, причина может находиться на этом внешнем пути.

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

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

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

Как защитить диагностику и сохранить её доступность

Общий статус не требует списка серверов, версий библиотек и результатов SQL. Подробности оставляйте в защищённой системе наблюдения для ответственных сотрудников. Секретный на вид путь сам по себе не ограничивает доступ.

Для закрытого endpoint проверьте поддержку авторизации потребителем и порядок обновления учётных данных. Интерактивный вход не подходит для обычной машинной пробы. Не помещайте секреты в адрес: URL может сохраняться в журналах и отчётах.

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

Как принять работу: проверить не только зелёный статус

Договоритесь о проверке отказов до подключения автоматических действий. Сценарии ниже выполняются в тестовой среде или в согласованном окне с понятным планом возврата. Цель — доказать смысл сигнала и реакцию потребителя, а не просто открыть URL один раз.

СценарийОжидаемое наблюдениеЧто он помогает обнаружить
Остановлен обработчик приложения, прокси работаетПроверка приложения перестаёт быть успешной.Статический ответ прокси вместо проверки нужного слоя.
Недоступна обязательная зависимостьПоявляется согласованный отказ готовности или сигнал деградации; исправные процессы не уходят в цикл рестартов.Смешение liveness с проверкой внешнего сервиса.
Не отвечает необязательный компонентОсновная функция продолжает работать, потерянная возможность видна отдельно.Чрезмерно строгую готовность и скрытую деградацию.
Запуск дольше обычногоПриложение получает предусмотренный бюджет; новые запросы не поступают раньше готовности.Слишком ранние рестарты и преждевременное включение в балансировку.
Зависимость отвечает медленноПроверка завершается в пределах бюджета и освобождает ресурсы.Неограниченное ожидание и накопление фоновых операций.
Отказ после успешного ответа через CDNСтарый успех не маскирует новое состояние.Кеширование, подмену ответа и чрезмерно старый внутренний результат.
Возврат к нормальной работеСостояние и маршрутизация восстанавливаются по согласованным правилам.Залипший отрицательный флаг, слишком быстрые переключения и неполную инициализацию.

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

Уведомление должно подсказывать первое действие. Сообщение «readiness не проходит: обязательный источник данных» полезнее общего «сайт упал», когда публичная витрина ещё отвечает. В инструкции ответственному укажите, где посмотреть подробности, как отличить локальный отказ от общей зависимости и при каком условии допустим ручной перезапуск.

Вопросы о health check сайта

Обязательно ли использовать путь /health?

Нет. Выберите стабильное имя и настройте его у потребителей. Важнее смысл результата и правильная маршрутизация без перенаправлений. При изменении адреса или условий успеха обновляйте проверки вместе с приложением.

Нужны ли небольшому сайту три отдельных адреса?

Не обязательно. Для внешнего мониторинга без перезапусков и балансировки можно начать с одного маршрута. Разделяйте адреса при появлении разных решений: «уведомить», «не давать новый трафик» и «перезапустить».

Нужно ли проверять все внешние сервисы?

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

Можно ли заменить GET на HEAD для экономии?

Только если приложение и потребитель одинаково трактуют оба метода и вы проверили это поведение. HEAD может обрабатываться другим маршрутом или не запускать ожидаемую диагностику. Для короткого ответа экономия тела невелика; важнее удостовериться, что проверка исполняет нужный код.

Почему health зелёный, а посетители видят ошибки?

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

Как часто выполнять проверку?

Исходите из допустимого времени обнаружения, стоимости запроса и числа потребителей. Измерьте длительность и нагрузку, затем подберите интервалы и пороги. Частый тяжёлый запрос способен усилить нагрузку, а редкий опрос старого результата — поздно показать аварию.

Как превратить диагностический URL в полезный контроль

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

Подключите SiteGuard для внешнего HTTP-контроля согласованного публичного URL. Смысл health-проверки и реакцию на её результат задаёт ваша команда.