Readiness probe что это
☸️ Первое знакомство с Kubernetes: публикация приложения в интернет
Андрей Трошин
Health check probe
Проверки работоспособности дают кластеру k8s понять, когда с нашим приложением что-то не так и нам нужно зафиксировать это в логах или перезапустить под. Есть два типа проверок: Liveness и Readiness.
Liveness
В этом случае мы определяем работоспособность контейнера. Узнать его состояние можно несколькими способами – это httpGet (запрос HTTP к приложению), tcpSocket (k8s попробует создать соединение на указанный порт вашего приложения) и gRPC (grpc-health-probe). Рассмотрим первые два варианта.
Для приложения с HTTP-сервером определение работоспособности через httpGet выглядит примерно так:
Делается HTTP-запрос к указанным URI и порту (в нашем случае это / на порте 8080). В случае успешного ответа вида 2xx, k8s считает приложение работоспособным. Другой ответ или отсутствие такового говорит о том, что контейнер отключен и его нужно перезапустить.
Если приложение не умеет обрабатывать HTTP-запросы, можно использовать проверку через tcpSoket:
Если TCP-соединение будет успешным, k8s считает приложение работоспособным.
Readiness
С технической точки зрения эта проверка похожа на предыдущую. Разница в том, что если мы не проходим Liveness, то под с нашим приложением уходит в перезагрузку, а в случае с провалом Readiness он блокируется на Service. На приложение в этом случае не будет направляться трафик.
Описать проверку Readiness можно так:
Внимательный читатель увидит, что разница только в одной строчке.
Ingress
От проверок переходим к объекту Ingress, который организует сетевое взаимодействие с подами (как Service), но прокидывает трафик на доменное имя и позволяет добраться до приложения из Internet. Еще одна его функция – балансировка трафика, при этом в настройках можно указать правила маршрутизации на определенные Service.
Ingress сверху мапится с доменным именем, а снизу – с Service, который обслуживает трафик для подов нашего приложения.
Описать YAML можно так:
В статьях цикла мы сделали следующее:
После публикации приложение в Internet с помощью Ingress архитектурная схема нашего кластера выглядит так:
Пошаговое руководство по написанию сервиса для Kubernetes
От автора. Уже пятый декабрь подряд в блоге GopherAcademy самые разные представители Go-сообщества делятся своим опытом в рамках специальной предрождественской серии постов. В этом году я тоже решила предложить свою статью, написанную по мотивам первой части нашего с Игорем Должиковым мастер-класса по микросервисам. На Хабре небольшую часть этого руководства мы уже рассматривали ранее.
Если вы когда-либо пробовали Go, вы знаете, что писать сервисы на Go очень просто. Нам нужно буквально несколько строк кода для того, чтобы можно было запустить http-сервис. Но что нужно добавить, если мы хотим приготовить такое приложение в продакшн? Давайте рассмотрим это на примере сервиса, который готов к запуску в Kubernetes.
Все шаги из этой статьи можно найти в одном теге, или вы можете следить за примерами статьи коммит за коммитом.
Шаг 1. Простейший сервис
Итак, у нас есть очень простое приложение:
Шаг 2. Добавляем логирование
Прежде всего, давайте добавим логирование для того, чтобы понимать, что происходит с сервисом, и для того, чтобы можно было журналировать ошибки или другие важные ситуации. В этом примере мы будем использовать простейший логер из стандартной библиотеки Go, но для настоящего сервиса, запущенного в продакшн, могут быть интересные более сложные решения, такие как glog или logrus.
Нам могут быть интересны 3 ситуации: когда сервис запускается, когда сервис готов обрабатывать запросы, и когда http.ListenAndServe возвращает ошибку. В результате получится что-то такое:
Шаг 3. Добавляем роутер
Если ваш сервис требует наличия некоторого заметрого количества правил роутинга, есть смысл вынести все, связанное с роутингом, в отдельный пакет. Давайте вынесем инициализацию и задание правил роутинга, а также функции-обработчики в пакет handlers (полные изменения можно посмотреть здесь).
Кроме того, нам нужны небольшие изменения в файле main.go :
Шаг 4. Тесты
Для функции home имеет смысл проверить уже не только код, но и тело ответа:
Запускаем go test и проверяем, что тесты работают:
Шаг 5. Конфигурирование
В этом примере, если порт не задан, приложение сразу завершится с ошибкой. Нет смысла пытаться продолжать работу, если конфигурация задана некорректно.
Шаг 6. Makefile
Шаг 7. Версионирование
Следующая практика, которую мы добавим в сервис — версионирование. Иногда полезно знать, какой конкретно билд и даже коммит мы используем в продакшн, и когда конкретно бинарник был собран.
Для того, чтобы хранить эту информацию, добавим новый пакет — version :
Мы можем логировать эти переменные, когда приложение запускается:
И также мы можем добавить их в home (не забудьте поправить тесты!):
Добавим новые переменные в Makefile :
Здесь COMMIT и BUILD_TIME определены через заданные команды, а для RELEASE мы можем использовать, например семантическое версионирование или просто инкрементные версии сборок.
Теперь перепишем цель build для того, чтобы можно было использовать значения этих переменных:
Все изменения, сделанные на этом шаге, можно найти здесь. Попробуйте make run для того, чтобы проверить, как это работает.
Шаг 8. Меньше зависимостей!
И, опять же, не забудьте поправить тесты и внести все необходимые изменения.
Шаг 9. Хелсчеки
В случае запуска сервиса в Kubernetes, обычно требуется добавить два хелсчека: liveness- и readiness-пробы. Цель liveness-пробы — дать понимание того, что сервис запустился. Если liveness-проба провалена, сервис будет перезапущен. Цель readiness-пробы — дать понимание того, что приложение готово к получению траффика. Если readiness-проба провалена, контейнер будет удален из балансировщиков нагрузки сервиса.
Для того, чтобы определить liveness-пробу, можно написать простой хендлер, который всегда возвращает код 200 :
Для readiness-пробы часто достаточно аналогичного решения, но иногда требуется дождаться некоторого события (например, готовности базы данных) для того, чтобы начать обрабаывать трафик:
Посмотрим, как это можно использовать:
Здесь мы говорим, что приложение готово обрабатывать трафик через 10 секунд после запуска. Конечно, в реальной жизни нет никакого смысла ждать 10 секунд, но, может быть, вы захотите добавить сюда прогрев кеша или что-то еще в этом роде.
Как всегда, полные изменения можно найти на GitHub’е.
Примечание. Если приложению придет слишком много трафика, оно начнет отвечать нестабильно. Например, liveness-проба может быть провалена из-за таймаутов, и контейнер будет перезагружен. По этой причине некоторые инженеры предпочитают не использовать liveness-пробы совсем. Лично я считаю, что лучше масштабировать ресурсы, если вы замечаете, что в сервис приходит все больше и больше запросов. Например, можно попробовать автоматическое масштабирование подов через HPA.
Шаг 10. Graceful shutdown
В этом примере мы перехватываем системные сигналы SIGINT и SIGTERM и, если один из них пойман, останавливаем сервис правильно.
Примечание. Когда я писала этот код, я также пробовала перехватывать SIGKILL здесь. Я видела такой подход несколько раз в разных библиотеках и была уверена, что это работает. Но, как заметил Sandor Szücs, перехват SIGKILL невозможен. В случае SIGKILL приложение будет остановлено немедленно.
Шаг 11. Dockerfile
Наше приложение почти готово к запуску в Kubernetes, самое время контейнеризировать его.
Мы создаем минимально возможный контейнер, копируем туда бинарник и запускаем его (кроме того, мы не забыли пробросить переменную PORT ).
Итак, мы добавиил цель container для сборки образа и поправили цель run так, чтобы вместо запуска бинарника, теперь запускался контейнер. Все изменения доступны здесь.
Теперь можно попробовать запустить make run для проверки всего процесса.
Шаг 12. Управления зависимостями
Шаг 13. Kubernetes
И, наконец, финальный шаг: запускаем приложение в Kubernetes. Самый простой способ попробовать Kubernetes — установить и настроить на своем локальном окружении minikube.
Kubernetes скачивает образы из реестра (Docker registry). В нашем случае достаточно публичного реестра — Docker Hub. Нам понадобится еще одна переменная и еще одна команда в Makefile :
Давайте попробуем make push :
Работает! Теперь созданный образ можно найти в реестре.
Примечание. Проект helm решает задачу управления релизами конфигураций в Kubernetes в целом и рассматривает вопросы создания гибких конфигураций в частности. Так что, если простого sed недостаточно, есть смысл познакомиться с Helm.
Рассмотрим конфигурацию для deployment:
Вопросы конфигурирования Kubernetes лучше рассмотреть в рамках отдельной статьи, но, как можно заметить, кроме всего прочего здесь определяются реестр и образ контейнера, а также правила для liveness- и readiness-проб.
Типичная конфигурация для service выглядит проще:
И, наконец, ingress. Здесь мы определяем конфигурацию ingress-контроллера, который поможет, например, получить доступ к сервису извне Kubernetes. Предположим, что мы хотим направлять запросы в сервис при обращению к домену advent.test (который в реальности, конечно, не существует):
Теперь добавим в Makefile отдельную цель для установки сервиса в minikube :
Проверим, как применились конфигурации:
И теперь можно проверять работу сервиса:
Все шаги руководства можно найти здесь, доступны два варианта: коммит-за-коммитом и все шаги в одной директории. Если у вас есть вопросы, можно создать issue, постучаться ко мне в твиттер: @webdeva, или просто оставить комментарий здесь.
Если вам интересно, как может выглядеть настоящий и более гибкий сервис, готовый к продакшн, посмотрите проект takama/k8sapp — шаблон Go-приложения, удовлетворяющий требованиям Kubernetes.
Визуальное руководство по диагностике неисправностей в Kubernetes
Прим. перев.: Эта статья входит в состав опубликованных в свободном доступе материалов проекта learnk8s, обучающего работе с Kubernetes компании и индивидуальных администраторов. В ней Daniele Polencic, руководитель проекта, делится наглядной инструкцией о том, какие шаги стоит предпринимать в случае возникновения проблем общего характера у приложений, запущенных в кластере K8s.
TL;DR: вот схема, которая поможет вам отладить deployment в Kubernetes:
Блок-схема для поиска и исправления ошибок в кластере. В оригинале (на английском) она доступна в PDF и как изображение.
При развертывании приложения в Kubernetes обычно необходимо определить три компонента:
1) В Kubernetes приложения получают трафик из внешнего мира через два слоя балансировщиков нагрузки: внутренний и внешний.
2) Внутренний балансировщик называется Service, внешний – Ingress.
3) Deployment создает pod’ы и следит за ними (они не создаются вручную).
Предположим, вы хотите развернуть простенькое приложение а-ля Hello World. YAML-конфигурация для него будет выглядеть следующим образом:
Определение довольно длинное, и легко запутаться в том, как компоненты связаны друг с другом.
Связь Deployment’а и Service’а
Вы удивитесь, но Deployment’ы и Service’ы никак не связаны. Вместо этого Service напрямую указывает на Pod’ы в обход Deployment’а.
Таким образом, нас интересует, как связаны друг с другом Pod’ы и Service’ы. Следует помнить три вещи:
1) Представим, что сервис направляет трафик в некий pod:
2) При создании pod’а необходимо задать containerPort для каждого контейнера в pod’ах:
5) Допустим, в контейнере открыт порт 3000. Тогда значение targetPort должно быть таким же.
В YAML-файле лейблы и ports / targetPort должны совпадать:
А как насчет лейбла track: canary в верхней части раздела Deployment? Должен ли он совпадать?
Этот лейбл относится к развертыванию, и не используется сервисом для маршрутизации трафика. Другими словами, его можно удалить или присвоить другое значение.
Он всегда должен совпадать с лейблами Pod’а, поскольку используется Deployment’ом для отслеживания pod’ов.
Предположим, что вы внесли верные правки. Как их проверить?
Проверить лейбл pod’ов можно следующей командой:
Или, если pod’ы принадлежат нескольким приложениям:
Можно подключиться к pod’у! Для этого надо использовать команду port-forward в kubectl. Она позволяет подключиться к сервису и проверить соединение.
Если соединение установить не удалось, значит проблема с лейблами или порты не совпадают.
Связь Service’а и Ingress’а
Следующий шаг в обеспечении доступа к приложению связан с настройкой Ingress’а. Ingress должен знать, как отыскать сервис, затем найти pod’ы и направить к ним трафик. Ingress находит нужный сервис по имени и открытому порту.
В описании Ingress и Service должны совпадать два параметра:
1) Как вы уже знаете, Service слушает некий port :
2) У Ingress’а есть параметр, называемый servicePort :
3) Этот параметр ( servicePort ) всегда должен совпадать с port в определении Service:
4) Если в Service задан порт 80, то необходимо, чтобы servicePort также был равен 80:
На практике необходимо обращать внимание на следующие строки:
Как проверить, работает ли Ingress?
Сначала нужно узнать имя pod’а с контроллером Ingress:
Наконец, подключитесь к pod’у:
Теперь каждый раз, когда вы будете посылать запрос на порт 3000 на компьютере, он будет перенаправляться на порт 80 pod’а с контроллером Ingress. Перейдя на http://localhost:3000, вы должны будете увидеть страницу, созданную приложением.
Резюме по портам
Давайте еще раз вспомним о том, какие порты и лейблы должны совпадать:
Что случается, когда что-то идет не так?
Возможно, pod не запускается или он падает.
3 шага для диагностики неисправностей в приложениях в Kubernetes
Прежде чем приступать к отладке deployment’а, необходимо иметь хорошее представление о том, как работает Kubernetes.
Поскольку в каждом выкаченном в K8s приложении имеются три компонента, проводить их отладку следует в определенном порядке, начиная с самого низа.
1) Начинать поиск проблем следует с самого низа. Сперва проверьте, что pod’ы имеют статусы Ready и Running :
2) Если pod’ы готовы ( Ready ), следует выяснить, распределяет ли сервис трафик между pod’ами:
3) Наконец, нужно проанализировать связь сервиса и Ingress’а:
1. Диагностика pod’ов
Как понять, что пошло не так?
Есть четыре полезные команды для диагностики pod’ов:
Дело в том, что нет универсальной команды. Следует использовать их комбинацию.
Типичные проблемы pod’ов
Существует два основных типа ошибок pod’ов: ошибки во время запуска (startup) и ошибки во время работы (runtime).
ImagePullBackOff
Эта ошибка появляется, когда Kubernetes не может получить образ для одного из контейнеров pod’а. Вот три самых распространенных причины этого:
CrashLoopBackOff
Она выводит сообщения об ошибках из предыдущей реинкарнации контейнера.
RunContainerError
Эта ошибка возникает, когда контейнер не в состоянии запуститься. Она соответствует моменту до запуска приложения. Обычно ее причиной является неправильная настройка, например:
Pod’ы в состоянии Pending
Почему такое происходит?
Вот возможные причины (я исхожу из предположения, что планировщик работает нормально):
Pod’ы не в состоянии Ready
2. Диагностика сервисов
Сервисы занимаются маршрутизацией трафика к pod’ам в зависимости от их лейблов. Поэтому первое, что нужно сделать — проверить, сколько pod’ов работают с сервисом. Для этого можно проверить endpoint’ы в сервисе:
Если раздел Endpoins пуст, возможны два варианта:
Как проверить работоспособность сервиса?
Независимо от типа сервиса, можно использовать команду kubectl port-forward для подключения к нему:
3. Диагностика Ingress
Если вы дочитали до этого места, то:
Это означает, что, скорее всего, неправильно настроен контроллер Ingress. Поскольку контроллер Ingress является сторонним компонентом в кластере, существуют различные методы отладки в зависимости от его типа.
Но прежде чем прибегнуть к помощи специальных инструментов для настройки Ingress’а, можно сделать нечто совсем простое. Ingress использует serviceName и servicePort для подключения к сервису. Необходимо проверить, правильно ли они настроены. Сделать это можно с помощью команды:
Если столбец Backend пуст, высока вероятность ошибки в конфигурации. Если бэкэнды на месте, но доступа к приложению по-прежнему нет, то проблема может быть связана с:
Наконец, подключитесь к pod’у:
Теперь все запросы на порт 3000 на компьютере будут перенаправляться на порт 80 pod’а.
Работает ли он теперь?
Существует много разновидностей контроллеров Ingress. Самыми популярными являются Nginx, HAProxy, Traefik и др. (подробнее о существующих решениях см. в нашем обзоре — прим. перев.) Следует воспользоваться руководством по устранению неполадок в документации соответствующего контроллера. Поскольку Ingress Nginx является самым популярным контроллером Ingress, мы включили в статью несколько советов по решению связанных с ним проблем.
Отладка контроллера Ingress Nginx
У проекта Ingress-nginx имеется официальный плагин для kubectl. Команду kubectl ingress-nginx можно использовать для:
Резюме
Диагностика в Kubernetes может оказаться непростой задачей, если не знать, с чего начать. К проблеме всегда следует подходить по принципу «снизу-вверх»: начинайте с pod’ов, а затем переходите к сервису и Ingress’у. Методы отладки, описанные в статье, могут применяться и к другим объектам, таким как:
Liveness probes в Kubernetes могут быть опасны
Прим. перев.: Ведущий инженер из компании Zalando — Henning Jacobs — не раз замечал у пользователей Kubernetes проблемы в понимании предназначения liveness (и readiness) probes и их корректного применения. Посему он собрал свои мысли в эту ёмкую заметку, которая со временем станет частью документации K8s.
Проверки состояния, известные в Kubernetes как liveness probes (т.е., дословно, «тесты на жизнеспособность» — прим. перев.), могут быть весьма опасными. Рекомендую по возможности избегать их: исключениями являются только случаи, когда они действительно необходимы и вы полностью осознаете специфику и последствия их использования. В этой публикации речь пойдет о liveness- и readiness-проверках, а также будет рассказано, в каких случаях стоит и не стоит их применять.
Мой коллега Sandor недавно поделился в Twitter’е самыми частыми ошибками, которые ему встречаются, в том числе связанными с использованием readiness/liveness probes:
Неправильно настроенная livenessProbe может усугубить ситуации с высокой нагрузкой (лавинообразное отключение + потенциально долгий запуск контейнера/приложения) и привести к другим негативным последствиям вроде падения зависимостей (см. также мою недавнюю статью об ограничении числа запросов в связке K3s+ACME). Еще хуже, когда liveness probe сочетается с проверкой здоровья зависимости (health check’ом), в роли которой выступает внешняя база данных: единственный сбой БД перезапустит все ваши контейнеры!
Общий посыл «Не используйте liveness probes» в данном случае помогает мало, поэтому рассмотрим, для чего предназначены readiness- и liveness-проверки.
Примечание: бόльшая часть приведенного ниже теста изначально была включена во внутреннюю документацию для разработчиков Zalando.
Проверки Readiness и Liveness
Kubernetes предоставляет два важных механизма, называемых liveness probes и readiness probes. Они периодически выполняют некоторое действие — например, посылают HTTP-запрос, открывают TCP-соединение или выполняют команду в контейнере, — чтобы подтвердить, что приложение работает должным образом.
Kubernetes использует readiness probes, чтобы понять, когда контейнер готов принимать трафик. Pod считается готовым к работе, если все его контейнеры готовы. Одно из применений этого механизма состоит в том, чтобы контролировать, какие pod’ы используются в качестве бэкендов для сервисов Kubernetes (и особенно Ingress’а).
Liveness probes помогают Kubernetes понять, когда пришло время перезапустить контейнер. Например, подобная проверка позволяет перехватить deadlock, когда приложение «застревает» на одном месте. Перезапуск контейнера в таком состоянии помогает сдвинуть приложение с мертвой точки, несмотря на ошибки, при этом он же может привести к каскадным сбоям (см. ниже).
Если вы попытаетесь развернуть обновление приложения, которое проваливает проверки liveness/readiness, его выкатывание застопорится, поскольку Kubernetes будет ждать статуса Ready от всех pod’ов.
Пример
Вот пример readiness probe, проверяющей путь /health через HTTP с настройками по умолчанию (interval: 10 секунд, timeout: 1 секунда, success threshold: 1, failure threshold: 3):
Рекомендации
Предостережения
Резюме
Дополнительные материалы по теме
Обновление №1 от 2019-09-29
EJ напомнил мне о PDB: одна из бед liveness-проверок — отсутствие координации между pod’ами. В Kubernetes есть Pod Disruption Budgets (PDB) для ограничения числа параллельных сбоев, которое может испытывать приложение, однако проверки не учитывают PDB. В идеале мы можем приказать K8s: «Перезапусти один pod, если его проверка окажется неудачной, но не перезапускай их все, чтобы не сделать еще хуже».
Bryan отлично сформулировал: «Используйте liveness-зондирование, когда точно знаете, что лучшее, что можно сделать, — это «убить» приложение» (опять же, увлекаться не стоит).
Обновление №2 от 2019-09-29
Касаемо чтения документации перед использованием: я создал соответствующий запрос (feature request) на дополнение документации о liveness probes.
Liveness probes в Kubernetes могут быть опасны
Прим. перев.: Ведущий инженер из компании Zalando — Henning Jacobs — не раз замечал у пользователей Kubernetes проблемы в понимании предназначения liveness (и readiness) probes и их корректного применения. Посему он собрал свои мысли в эту ёмкую заметку, которая со временем станет частью документации K8s.
Проверки состояния, известные в Kubernetes как liveness probes (т.е., дословно, «тесты на жизнеспособность» — прим. перев.), могут быть весьма опасными. Рекомендую по возможности избегать их: исключениями являются только случаи, когда они действительно необходимы и вы полностью осознаете специфику и последствия их использования. В этой публикации речь пойдет о liveness- и readiness-проверках, а также будет рассказано, в каких случаях стоит и не стоит их применять.
Мой коллега Sandor недавно поделился в Twitter’е самыми частыми ошибками, которые ему встречаются, в том числе связанными с использованием readiness/liveness probes:
Неправильно настроенная livenessProbe может усугубить ситуации с высокой нагрузкой (лавинообразное отключение + потенциально долгий запуск контейнера/приложения) и привести к другим негативным последствиям вроде падения зависимостей (см. также мою недавнюю статью об ограничении числа запросов в связке K3s+ACME). Еще хуже, когда liveness probe сочетается с проверкой здоровья зависимости (health check’ом), в роли которой выступает внешняя база данных: единственный сбой БД перезапустит все ваши контейнеры!
Общий посыл «Не используйте liveness probes» в данном случае помогает мало, поэтому рассмотрим, для чего предназначены readiness- и liveness-проверки.
Примечание: бόльшая часть приведенного ниже теста изначально была включена во внутреннюю документацию для разработчиков Zalando.
Проверки Readiness и Liveness
Kubernetes предоставляет два важных механизма, называемых liveness probes и readiness probes. Они периодически выполняют некоторое действие — например, посылают HTTP-запрос, открывают TCP-соединение или выполняют команду в контейнере, — чтобы подтвердить, что приложение работает должным образом.
Kubernetes использует readiness probes, чтобы понять, когда контейнер готов принимать трафик. Pod считается готовым к работе, если все его контейнеры готовы. Одно из применений этого механизма состоит в том, чтобы контролировать, какие pod’ы используются в качестве бэкендов для сервисов Kubernetes (и особенно Ingress’а).
Liveness probes помогают Kubernetes понять, когда пришло время перезапустить контейнер. Например, подобная проверка позволяет перехватить deadlock, когда приложение «застревает» на одном месте. Перезапуск контейнера в таком состоянии помогает сдвинуть приложение с мертвой точки, несмотря на ошибки, при этом он же может привести к каскадным сбоям (см. ниже).
Если вы попытаетесь развернуть обновление приложения, которое проваливает проверки liveness/readiness, его выкатывание застопорится, поскольку Kubernetes будет ждать статуса Ready от всех pod’ов.
Пример
Вот пример readiness probe, проверяющей путь /health через HTTP с настройками по умолчанию (interval: 10 секунд, timeout: 1 секунда, success threshold: 1, failure threshold: 3):
Рекомендации
Предостережения
Резюме
Дополнительные материалы по теме
Обновление №1 от 2019-09-29
EJ напомнил мне о PDB: одна из бед liveness-проверок — отсутствие координации между pod’ами. В Kubernetes есть Pod Disruption Budgets (PDB) для ограничения числа параллельных сбоев, которое может испытывать приложение, однако проверки не учитывают PDB. В идеале мы можем приказать K8s: «Перезапусти один pod, если его проверка окажется неудачной, но не перезапускай их все, чтобы не сделать еще хуже».
Bryan отлично сформулировал: «Используйте liveness-зондирование, когда точно знаете, что лучшее, что можно сделать, — это «убить» приложение» (опять же, увлекаться не стоит).
Обновление №2 от 2019-09-29
Касаемо чтения документации перед использованием: я создал соответствующий запрос (feature request) на дополнение документации о liveness probes.