В этой статье рассмотрим настройку инфраструктуры Kubernetes для потоковой платформы комплексных мобильных приложений на основе Apache Kafka. Что поможет добиться оптимальной масштабируемости приложений-потребителей и высокой доступности всей Big Data системы.
Проблемы масштабирования платформы Grab из приложений-потребителей Apache Kafka
Grab считается ведущей платформой суперприложений в 8 странах Юго-Восточной Азии, которая предоставляет повседневные услуги для потребителей, от перевозки пассажиров до доставки еды и посылок в 428 городах. Платформа получает терабайты данных каждый час со множества устройств, безотказно обслуживая около 400 миллиардов событий в неделю. Технически платформа представлена облегченными конвейерами обработки данных на основе архитектуры плагинов Golang, развернутых в Kubernetes. По сути, это поды приложений-потребителей данных из Apache Kafka, которые потребляют данные, обрабатывают их, а затем материализуют результаты в различные приемники (реляционные базы данных, другие топики Kafka и пр.).
Каждый под потоковой обработки, т.е. наименьшая единица развертывания конвейера, имеет три компонента верхнего уровня:
- триггер – интерфейс, который напрямую подключается к источнику данных и преобразует его в канал событий.
- runtime – точка входа приложения и оркестратор пода, который управляет пулами рабочих процессов, триггерами, каналами событий и событиями жизненного цикла;
- плагин конвейера, который предоставляется пользователем и соответствует контракту, публикуемому командой платформы. Он содержит доменную логику для конвейера и его оркестровку, определенную пользователем на основе платформы потоковой обработки.
Изначально команда платформы Grab настраивала Kubernetes на основе горизонтального автоматического масштабирования подов (HPA, Horizontal Pod Autoscaler), который автоматически развертывает больше подов при увеличении рабочей нагрузки, т.е. росте потребления ресурсов ЦП и памяти. Это отличается от вертикального масштабирования, что для Kubernetes означает выделение большего количества ресурсов уже запущенным и работающим подам. HPA поддерживает ЦП и память для каждого пода, указанные в манифесте развертывания, и масштабирует горизонтально по мере изменения нагрузки. Об этом мы писали здесь.
Однако, при использовании HPA администраторы платформы столкнулись со следующими проблемами:
- из-за неравномерности спроса на услуги Grab требовалось заранее предусмотреть пиковый трафик, что приводило к нерациональному потреблению ресурсов;
- неравномерное распределение трафика на поды не компенсировалось выравниванием нагрузки на разделы Kafka. Поскольку сам платформа потоковой обработки реализована множеством приложений-потребителей Kafka, рост количества масштабируемых подов приводит к неравной нагрузке на каждый раздел.
Поэтому DevOps-инженеры Grab задумались о необходимости вертикального масштабирования с фиксированным количеством подов для конвейера обработки данных, сохранив его равным количеству разделов топика Kafka, откуда конвейер потребляет сообщения. Это обеспечило равномерное распределение разделов между каждым подом, обеспечивая сбалансированное потребление. Чтобы абстрагироваться от конечного пользователя, процесс развертывания приложения был автоматизирован через непосредственные вызовы API Kafka для получения количества разделов во время выполнения.
С фиксированным количеством подов Kubernetes нет необходимости в HPA. Чтобы обеспечить вертикальное автомасштабирование подов по мере увеличения или уменьшения нагрузки без ручного вмешательства, нужен Vertical Pod Autoscaler (VPA), который освобождает пользователей от необходимости устанавливать актуальные лимиты ресурсов и запрашивать контейнеры в своих подах. При настройке он автоматически устанавливает запросы в зависимости от использования, разрешая правильное планирование для узлов, чтобы для каждого пода был доступен соответствующий объем ресурсов. VPA также поддерживает соотношения между лимитами и запросами, указанными в начальной конфигурации контейнеров.
Вертикальное автомасштабирование в Kubernetes настраивается с помощью CRD-объекта (Custom Resource Definition) с именем VerticalPodAutoscaler. Это позволяет указать, какие поды должны масштабироваться по вертикали, а также как применяются рекомендации по ресурсам. DevOps-инженер просто разворачивает приложение, позволяя VPA управлять ресурсами, необходимыми для его работы. Хотя VPA не очень чувствителен к быстрым изменениям нагрузки, поскольку обучает свою модель отслеживать тенденцию нагрузки развертывания в течение определенного периода времени, он обеспечивает оптимальное распределение ресурсов для конвейеров с учетом исторических тенденций пропускной способности. В Grab применение VPA позволило сократить общее потребление ресурсов примерно на 45% по сравнению с использованием HPA.
Ограничения вертикального масштабирования в Kubernetes
Говоря о преимуществах вертикального автомасштабирования в Kubernetes, будет справедливо сказать и о недостатках этой возможности. Прежде всего, не рекомендуется применять вертикальное автомасштабирование подов с рабочими нагрузками на основе JVM, поскольку JVM обеспечивает ограниченную видимость фактического использования памяти. Также не следует применять VPA вместе с HPA на основе тех же показателей, т.е. использования ЦП или памяти. Однако, можно использовать VPA с HPA на основе настраиваемых метрик.
Рекомендации VPA могут превышать доступные ресурсы, например емкость кластера или квоту команды. Нехватка доступных ресурсов может привести к тому, что поды будут «заморожены» (pending). Поэтому лучше установить объекты LimitRange для ограничения запросов ресурсов для каждого пространства имен и задать максимально допустимые рекомендации по ресурсам для каждого пода в объекте VPA.
VPA в режиме Auto или Recreate не будет вытеснять поды с одной репликой, поскольку это может привести к нарушению работы. Однако, если нужны автоматические рекомендации для приложений с одной репликой, можно изменить это поведение, задав флаг —min-replicas. При использовании режима RequestsAndLimits в VPA рекомендуется установить начальные лимиты ЦП на большое количество запросов из-за проблемы с ядром Kubernetes/Linux, которая приводит к чрезмерному регулированию. Наконец, стоит помнить, что не все рекомендации VPA выполняются на практике. Например, при запуске системы с высокой доступностью и двумя репликами, один из контейнеров решает быстро увеличить объем памяти, но это может привести к уничтожению контейнера из-за ее нехватки. Поскольку нехватка памяти для уничтоженных подов не перепланируется, VPA не будет применять новые рекомендации по ресурсам. А вытеснение пода не произойдет, т.к. один под всегда не готов или зацикливается. Единственный способ решить такую проблему — уничтожить под и реализовать новые рекомендации по ресурсам.
Чувствительность приложений-потребителей к задержке
Мониторинг производительности приложений-потребителей Kafka жизненно важен для правильной работы систем, которым требуются потоки данных с малой задержкой. Проще всего измерить отставание потребителя как разницу в идентификаторе смещения сообщения между последним созданным сообщением для раздела топика в Kafka и последним полученным сообщением для этого раздела топика, чтобы обнаружить и исправить потребителей, которые слишком опаздывают. Найти отставание довольно просто: можно сперва опросить топик __consumer_offsets, который хранит запись о последней позиции смещения, зафиксированной всеми группами потребителей в кластере, и найти позицию последнего использованного сообщения. Далее можно найти позицию последнего созданного сообщения, отправив запрос в Kafka Consumer Offsets API. Имея позицию последнего созданного сообщения и последнего использованного сообщения, можно найти задержку, вычитая одно из другого. Вычисленное таким образом запаздывание не обязательно является точным, но разница во времени между запросами достаточно мала, чтобы ею можно было пренебречь.
Использование абсолютной задержки сообщений помогает идентифицировать отстающие группы потребителей, однако каждая из них может потреблять сообщения по-разному. Например, группа потребителей A может быстро обрабатывать сообщения, а топик, на который она подписана, создает много сообщений, тогда как другой группе потребителей B нужно больше времени для обработки одного сообщения, а топик, на который она подписана, содержит гораздо их меньшее количество.
Если группа потребителей А обрабатывает сообщения быстро, отставание в 100 000 сообщений может означать, что группа потребителей А отстает всего на несколько секунд. А отставание в 10 сообщений для группы В может означать отставание на час. Возможность отслеживать отставание по фактическому времени, а не по количеству сообщений, дает дата-инженерам гораздо больше информации о том, как работают приложения потребители Kafka и на какие из них нужно обратить внимание.
Реализовать это можно, создав отдельный поток для заполнения таблицы мониторинга. Ежеминутно отправляя запрос в Kafka Consumer Offsets API, чтобы получить последнее созданное смещение для всех активных разделов топика, в эту таблицу будет добавлено последнее созданное смещение. А соответствующее ему значение метки времени показывает, когда запрос был отправлен в Kafka. Такая поминутная детализация позволяет оценить значение временной задержки и выявить отстающих потребителей.
Возвращаясь к случаю Grab, Отметим, что в этой платформе рабочие нагрузки делятся на чувствительные к задержкам (критические) и устойчивые к задержкам (некритические). Такое деление помогло инженерам оптимизировать планирование и экономическую эффективность с помощью классов приоритета и избыточного выделения ресурсов для разнородных типов узлов в AWS. Основные затраты на запуск EKS в AWS связаны с машинами EC2, которые образуют рабочие узлы для кластера Kubernetes. Запуск по требованию дает все гарантии доступности инстансов, но это стоит очень дорого. Спотовые инстантсы позволяют снизить стоимость облачной инфраструктуры, но имеют меньшую надежность. Чтобы избежать потери спотового экземпляра, DevOps-инженеры Grab начали назначать приоритеты своим приложениям-потребителям Kafka, позволяя пользователю выбирать приоритеты их конвейеров в зависимости от сценария использования. Разные приоритеты приведут к разной привязке узлов к разным типам групп экземпляров: спотовый или по требованию. Например, критические конвейеры, чувствительные к задержкам, выполняются в группах рабочих узлов по требованию, а некритические конвейеры, устойчивые к задержкам, соответственно в группах рабочих узлов спотовых экземпляров. В Grab класс приоритета используется в качестве метода вытеснения, а также сходства узлов, которое выбирает конвейер с определенным приоритетом для группы узлов для развертывания.
Из-за недостаточно высокой надежности спотовых индексов необходимо быстро реагировать на сбои. Чтобы добиться быстрого перераспределения удаленных подов, DevOps-инженеры Grab добавили избыточное выделение ресурсов в кластер, оставив «запасные» поды в группах рабочих узлов для быстрой переброски на них вытесненных единиц развертывания контейнеров. Поды с избыточным выделением ресурсов имеют самый низкий приоритет, поэтому они могут быть вытеснены любым подом, ожидающим в очереди на планирование. Чтобы определить правильное количество этих избыточно выделенных подов, которые масштабируются вверх и вниз пропорционально размеру кластера, т.е. количеству узлов и ЦП в группе рабочих узлов, DevOps-инженеры Grab использовали автомасштабирование. Это избавляет от необходимости настраивать количество таких «запасных» подов по мере масштабирования кластера, сохраняя свободное пространство пропорциональным его текущей емкости.
Наконец, избыточное выделение ресурсов на «запасные» поды помогло сократить время развертывания, устраняя потребность во времени, необходимого группам автоматического масштабирования для добавления нового узла в кластер каждый раз при развертывании нового приложения.
Освойте администрирование и эксплуатацию Apache Kafka для потоковой аналитики больших данных на специализированных курсах в нашем лицензированном учебном центре обучения и повышения квалификации для разработчиков, менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data в Москве:
- Apache Kafka для инженеров данных
- Администрирование кластера Kafka
- Администрирование Arenadata Streaming Kafka
Источники