HealthCheck микросервиса + примеры от Олега
Статья создана с целью дополнить описание важного функционала каждого микросервиса в Кубернетесе - HealthCheck.
Текущее описание данного функционала из документации "Стандарт разработки микросервиса для деплоя":
Микросервис должен иметь функцию healthcheck по URL /health (проверка жизнедеятельности сервиса и его связей если такие имеются). Коды ответа: 200, если все ОК; 50x, если микросервис не готов к работе (нет подключения к базе, etc). Оркестратор периодически опрашивает все реплики микросервиса и по кодам ответа ориентируется, жива ли конкретная реплика. Если микросервис не готов к работе и /health возвращает код 50x, причина сбоя обязательно должна быть записана в лог (graylog|stdout). Пример: нет подключения к MongoDB 10.20.30.40:27017 (connection refused).
Зачем читать эту статью?!
У разработчиков могут возникнуть вопросы для чего нужен функционал HealthCheck
или неправильное представление о том, что будет делать Кубернетес при получении ошибки от функции HealthCheck.
Подробнее, о том как все работает
Кубернетес предоставляет функционал проверок (probes) программ в контейнерах (pod).
Для этого существует три типа проверок:
- ExecAction - запуск bash команды внутри контейнера.
Ожидает exit code = 0; - TCPSocketAction - открытием TCP-соединения по указанному порту.
Ожидает открытый порт - HTTPGetAction - HTTP-запросом по указанному адресу и порту.
Ожидает status code в промежутке [200,400).
Проверки можно настроить в деплой файле Кубернетеса.
Существует три типа запуска проверок с разной реакцией на результат проверки:
- liveness - перезагружает микросервис, если сервис завис, например из-за deadlock или сервис упал из-за бага.
- readiness - останавливает трафик на микросервис, если сервис не готов принимать его, например при старте микросервиса,
или отсутствия связи с базой/хранилищем или другим микросервисом без которого данный сервис не может обрабатывать запросы. - startup - приостанавливает запуск liveness и readiness проверок при старте микросервиса, например если сервис медленно стартует.
На данный момент типичный деплой файл для микросервиса настроен так:
- Liveness - использует HTTPGetAction, чтобы слушать порт 8080 и адрес "/".
Обработчик запросов по этому адресу должен отдавать 200 и ничего более. - Readiness - использует HTTPGetAction, чтобы слушать порт 8080 и адрес "/health".
Именно в этом обработчике запросов проверяются критически важные зависимости сервиса, тот самый функционал HealthCheck, который должен отвечать статус кодом 200 если все хорошо и 500 при не способности принимать входящие запросы.
Например, может начать отвечать статусом 200 только после завершения миграции БД, если они запускаются при старте микросервиса. - Startup - не используется.
Типовому микросервису достаточно перестать принимать входящий трафик в случае не возможности обработать запросы. Для этого вам нужно написать HealthCheck для Readiness проверок.
Но возможно, вам нужно перезагружать микросервис каждый раз когда, например, пропадает связь с внешним сервисом. Для этого вам нужно написать HealthCheck для Liveness проверок.
Из описанного выше появляется ремарка к стандарту написания микросервисов, а в частности к описанию функционала HealthCheck.
Предложение о HealthCheck в стандарт микросервисов
- Нужно понимать как работают leaviness и readiness проверки
- Типовой микросервис должен запускать внутри себя http-сервер и обрабатывать два адреса (endpoints):
- по адресу "/" - обработчик должен отвечать статусом 200 на все запросы.
Этот адрес будет назначен для Liveness проверок. - по адресу "/health/" - обработчик должен ответить на вопрос "может ли сервис продолжать принимать входящие запросы?". Этот адрес будет назначен для Readiness проверок.
Для этого нужно запускать проверку на все внешние связи, без которых сервис не сможет обрабатывать входящие запросы. К ним могут относиться: базы данных, разного рода хранилища, другие микросервисы, API и прочее.
Примеры от Олега
Несколько кейсов:
- Кластер куба состоит из нескольких нод. У нас, например, в ДЦ PS 6 нод в двух блейд-серверах, в двух стойках. Может отказать сеть ноды, сеть блейд-сервера, сеть между стойками (такое случалось, случается и будет случаться).
При сетевом сбое какая-то реплика (или несколько) могут оказаться изолированными от БД. В таком случае часть реплик будет отвечать нормально, а часть с ошибками.
Readiness probe в таком случае выпилит из балансировщика больные реплики. - Сервис пишет нетранзакционно в две или более точки. Записал в первую точку, но не записал в вторую (третью, пятую... и т.д.), потому что та была недоступна. Получили неконсистентные данные.
В таком случае лучше не ответить вообще. Нужен Readiness probe с проверкой обоих точек. - Сервис делает тяжелые запросы при хелсчеке. Кейс из личной практики. Сервис использует etcd, при хелсчеке проверял не только доступность etcd, но и делал тестовую запись.
3 ДЦ x несколько реплик х тогда еще dynaproxy, который тоже делал хелсчек запросы - в итоге etcd сложился от нагрузки.
Вывод по кейсу - думать надо, что и как проверять. - Сервис по разным запросам читает/пишет независимо в разные точки.
В таком случае readiness probe нужно заваливать при недоступности всех точек, либо не делать его вообще. При отвале одной из точек можно сохранить часть функционала. Хотя, опять таки, зависит от... смотрите первый кейс
FAQ
Что такое каскадное падение реплик?
Как поступит Кубернетес если все реплики не прошли readiness проверку?
К8 не сможет передать входящий трафик ни одному из реплик и входящий трафик не будет ни кем обработан.
Следует отметить, что решения этой проблемы пока нет.
Не следует ожидать от К8 следующих вещей:
- НЕ запустит новые реплики.
- и даже НЕ перенаправит трафик в другой ЦОД.
Внешние фронтенды, перед кластерами К8, могут перенаправлять запрос в другой ЦОД если весь кластер К8 в этом ЦОДе не доступен.
Однако, когда все реплики не прошли readiness проверку, тогда IngressController отдает http status code - 500. Поэтому внешний фронтенд не будет перенаправлять запрос в другой ЦОД из-за отсутствия идемпотентности POST-запросов.