Пробуем приложения Apache Kafka Streams в Kubernetes: probe-механизм и проблемы stateful

Автор Категория ,
Пробуем приложения Apache Kafka Streams в Kubernetes: probe-механизм и проблемы stateful

Вчера мы говорили про сложности развертывания множества stateful-приложений Apache Kafka Streams в кластере Kubernetes и роль контроллера StatefulSet, который поддерживает состояние реплицированных задач за пределами жизненного цикла отдельных подов. В продолжение этой темы, сегодня рассмотрим механизм проб, которые позволяют определить состояние распределенного приложения, развернутого на платформе контейнерной виртуализации. В качестве примера для проверок живучести, готовности и запуска будем ориентироваться на stateful-приложения Kafka Streams.

Зачем нужен probe-механизм в Kubernetes: проблемы микросервисной архитектуры и зависания распределенных приложений

При всех достоинствах микросервисной архитектуры, связанных, в основном с ускорением разработки за счет разделения продукта на отдельные модули (микросервисы) в лучших традициях Agile, ей свойственны некоторые специфические проблемы. В частности, автоматическое обнаружение неисправных приложений, перенаправление запросов в другие доступные модули распределенных систем и восстановление поврежденных компонентов. В Kubernetes, начиная с версии 1.16, эта проблема решается с помощью механизма проб или проверок работоспособности, которые позволяют определить состояние микросервиса. По умолчанию Kubernetes наблюдает за жизненным циклом модуля (пода, Pod) и начинает направлять в него трафик, когда контейнеры переходят из состояния «ожидания» (Pending) в «успешно» (Succeeded). Kubernetes-компонент Kubelet, который отвечает за статус выполнения подов на узле кластера, отслеживает сбои приложения и перезапускает его для восстановления. Однако, эта базовая конфигурация может стать причиной ошибки по следующему сценарию [1]:

  • Kubernetes считает под работоспособным и готовым к запросам, как только запускаются все контейнеры, поэтому приложение может начать получать трафик еще до того, как будет фактически готово. Это случается, когда приложению нужно инициализировать какое-либо состояние, установить соединение с СУБД или загрузить данные перед обработкой бизнес-логики.
  • Этот промежуток времени между фактической готовностью приложения и моментом, когда Kubernetes считает его готовым, становится проблемой при масштабировании развертывания: неготовые приложения получают трафик и отправляют обратно ошибку «500 Internal Server Error». Это означает внутренние проблемы с сервером, сбой работы или нарушение конфигурации системы.

Для диагностики подобных случаев с зависанием распределенных приложений в Kubernetes используются пробы (probe), чтобы определить, когда контейнер готов принять трафик и в какой момент он должен быть перезапущен. Как это работает, мы рассмотрим далее на примере stateful-приложений Kafka Streams.

Пробы для приложений Apache Kafka Streams

Напомним, при использовании интерактивных запросов (Interactive queries) в Kafka Streams, которые позволяют использовать состояние приложения извне, локальное хранилище состояний не доступно, если экземпляр приложения повторно балансирует, восстанавливает или перетасовывает свое локальное состояние из топика Kafka с логами изменений. В этом случае пригодятся так называемые пробы Kubernetes для настройки для настройки распределенных приложений потоковой обработки данных и корректной работы с ними. Различают 3 вида проб Kubernetes [2]:

  • живучести (liveness) для проверки, когда перезапустить контейнер. Например, liveness-проба должна поймать блокировку, когда приложение «зависло», т.е. запущено, но не может выполнить никаких действий. В случае Kafka Streams даже когда приложение и все его экземпляры успешно запущены и работают, может произойти перебалансировка при изменении их количества: присоединение новых экземпляров или завершение текущих. Проба позволяет проверить, находится ли экземпляр в состоянии готовности с точки зрения интерактивных запросов. Для этого Kubernetes поддерживает конечные точки HTTP, сокет TCP и выполнение произвольных команд в качестве проверки. Для stateful-приложения Kafka Streams целесообразно проверять его работоспособность с помощью информацию о статусе хранилища состояний через конечную точку REST GET-запросом [3].
  • готовности (readiness), чтобы узнать, готов ли контейнер принимать траффик. Pod считается готовым, если все его контейнеры готовы. Например, так можно проконтролировать, какие поды будут использованы в качестве backend’а для сервиса. Если под не готов, он будет исключен из балансировщиков нагрузки сервиса. В Kafka Streams это гарантирует, что экземпляры приложения будут зарегистрированы в Kubernetes Service только после выполнения определенных пользователем критериев. Если экземпляр недоступен из-за перебалансировки, его конечные точки REST не будут доступны клиентам для интерактивных запросов. На этом этапе только частичное состояние приложения может быть запрошено через экземпляры, которые проходят проверку готовности. После запуска контейнера Kubernetes будет ждать в течение времени, указанного в параметре initialDelaySeconds, прежде чем запускать пробу готовности, и повторять его через через periodSeconds, пока он не будет успешным или не будет достигнут таймаут (timeoutSeconds) [3].
  • запуска (startup), чтобы понять, когда приложение в контейнере было запущено. Если проба запуска настроена, она блокирует liveness- и readiness-проверки. Это пригодится для проверки работоспособности медленно стартующих контейнеров или приложений с непредсказуемыми процессами инициализации.

Readiness- и liveness-пробы могут использоваться одновременно на одном контейнере, чтобы обеспечить отсутствие траффика в контейнер, пока он не готов для этого. В случае сбоя контейнер будет перезапущен [2]. Все 3 вида проб имеют следующие параметры [1]:

  • initialDelaySeconds – число секунд ожидания перед запуском проверки работоспособности или готовности;
  • periodSeconds – частота проверки;
  • timeoutSeconds – число секунд до отметки тайм-аута пробы, когда проверка работоспособности дала сбой;
  • successThreshold – минимальное количество последовательных успешных проверок, чтобы проба была помечена как пройденная;
  • failureThreshold – количество повторных попыток до того, как проба будет помечена как неуспешная. Для liveness-проб это приведет к перезапуску пода, а для проб готовности – к отметке контейнера как неготового.

Проверить состояние приложения через пробу Kubernetes можно не только с помощью HTTP-запроса GET, как было отмечено ранее. Если нужно просто проверить, доступна ли установка TCP-соединения, подойдет TCP-пробу. Под помечается как работоспособный, если может установить TCP-соединение. Это полезно для gRPC или FTP-сервера, где HTTP-вызовы не подходят. Также можно настроить пробу для запуска shell-команды. Проверка проходит, если команда возвращается с кодом выхода 0, иначе под помечается как неисправный. Это пригодится, когда нежелательно открывать HTTP-сервер/порт или нужна простая проверка шагов инициализации с помощью команд. Например, проверить, факт создания файла конфигурации или запустить CLI-команду [1].

 

Когда пробы не помогут: особенности stateful-приложений Kafka Streams

Итак, probe-механизм в Kubernetes не просто позволяет проверить работоспособность контейнерного приложения, но и устранить проблемы при их наличии, перезапустив под. Однако, это не всегда работает в случае stateful-приложений Kafka Streams, т.к. состояние потоков может быть настолько велико, что его перебалансировка превышает ограничения, указанные пробами готовности и живучести [3]:

  • если не прошла проверка готовности, то probe примет во внимание значение параметра failureThreshold, прежде чем Kubernetes перестанет его выполнять, и пометит под как Unready, что сделает его недоступным;
  • если не прошла проверка живучести, Kubernetes перезапустит Pod после повторной попытки, число которых указано в параметре failureThreshold. Таким образом, приложение может застрять в цикле, где оно пытается восстановить состояние, но прерывается, поскольку Kubernetes перезапускает Pod заново из-за пробы.

Избежать этих проблем можно следующим образом [3]:

  • хранить состояние приложения Kafka Streams во внешнем долговременном хранилище;
  • протестировать приложение, помня о его состоянии и времени, которое может потребоваться для восстановления, перебалансировки или перемешивания. Затем соответствующим образом настроить параметры конфигурации Kubernetes-проб (failureThreshold, initialDelaySeconds и пр.), чтобы у приложения было достаточно времени для фактического выполнения процесса восстановления.
  • настроить приложение Kafka Streams на резервные реплики локальных состояний через конфигурацию standby.replicas, которые являются полностью реплицированными копиями состояния. Когда запустится перебалансировка, Kafka Streams попытается назначить задачу экземпляру, где такая резервная реплика уже существует, чтобы минимизировать стоимость повторной инициализации задачи.

Больше практических деталей по администрированию кластеров и разработке Kafka-приложений для аналитики больших данных, вы узнаете на специализированных курсах в нашем лицензированном учебном центре обучения и повышения квалификации для разработчиков, менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data в Москве:

 

Источники

  1. https://habr.com/ru/company/mailru/blog/530752/
  2. https://kubernetes.io/ru/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
  3. https://itnext.io/health-checks-for-kafka-streams-application-on-kubernetes-e9c5e8c21b0d