
Привет, Хабр!
Я Анастасия, ведущий SRE-инженер РСХБ.Цифра. В этой статье я расскажу о том, как мы в App.Farm, PaaS-платформе Россельхозбанка, перешли от одной «большой» Kafka до реализации услуги «Kafka as a Service» c индивидуальными кластерами под ключ. Поделюсь первой частью нашей истории (во второй будет про ошибки):
Почему мы отказались от одной «большой» Kafka и как этим минимизировали затраты на сопровождение
Как через декларативный GitOps-подход автоматизировали развертывание кластеров Kafka в Kubernetes
Как настроили пресеты настроек кластеров под запросы пользователей PaaS-платформы
Как упростили авторизацию с помощью middleware, «влезая» в протокол обмена
Мы постарались сделать Kafka удобной платформенной услугой: разработчик описывает потребность, щепотка магии CI/CD, и через несколько минут получается готовый к использованию кластер с топиками, доступами и понятным статусом в UI. Звучит просто, но за этим кроется сложный путь проб и ошибок.
О платформе
Давайте добавим контекста. Что такое App.Farm ?
App.Farm - PaaS-решение для разработки и развёртывания программных продуктов, которое обеспечивает весь интеграционный поток и среду для исполнений информационных систем Россельхозбанка.

Если кратко:
5500 разработчиков используют нашу платформу
Более 1 млн пользователей взаимодействуют с сервисами платформы каждый день
2300+ развернутых бизнес-приложений
Через платформу проходит более 1 ТБ данных и 15 млн сообщений в день
Платформа полностью построена на Open-Source-продуктах (Kubernetes, Istio и другие), принципах и методологиях GitOps и IaC.
Подробнее про платформу можно почитать в статье «Как мы создавали PaaS-платформу App.Farm – цифровое сердце РСХБ».
Где и как наша Kafka живёт сейчас
Кластеры Kafka располагаются внутри платформы App.Farm, а для их развертывания, автоматизации и управления используется Strimzi. Плюсом это решение мы присыпали россыпью платформенных операторов Kubernetes, в том числе и самописных.
Kafka в этой картине – это предоставляемая услуга. На момент написания статьи этой услугой пользуются 20+ бизнес-систем банка. В промышленном окружении работает 24 Kafka-кластера, а суммарно по всем другим окружениям – 120+ кластеров.

Отдельный плюс такого решения: есть возможность одновременно поддерживать версии Kafka от 2.8.0 до 4.0.0.
Вишенка на торте: всё это сопровождает команда всего из 3 человек. Причём эти 3 человека занимаются не только Kafka, но и поддерживают работоспособность платформы и других интеграционных компонентов. При подсчете трудозатрат в месяц на обслуживание кластеров уходят минимальные человеко-ресурсы.
Взгляд со стороны пользователя
Как для сопровождения платформы для нас разработчики бизнесовых микросервисов являются пользователями платформы. Поэтому далее по тексту будет фигурировать термин «пользователь».
С точки зрения пользователя платформы, чтобы получить готовый к работе кластер Kafka с топиками и доступами, нужно выполнить всего несколько шагов:
1. В Gitlab в проекте описания информационной системы декларируется блок с описанием желаемого кластера с топиками:
kafkaClusters: cluster-name: size: medium topics: topic-name: another-topic-name:
В данном примере рассматривается минимальная декларация для создания кластера. Давайте пройдемся чуть подробнее:
kafkaClusters - блок для указания параметров кластера (название, версия и другие параметры)
cluster-name - пример названия кластера
size - блок для указания размера кластера (один из тарифов/пресетов).
Новый кластер «готовится» от 4 минут в зависимости от пресета: количества брокеров, CPU, RAM, дискового пространства и сопутствующих компонентов.
На данный момент реализовано 5 пресетов: minimal, medium, large, custom и disaster recovery.
Пользователь платформы может самостоятельно решить, какой кластер нужно развернуть в зависимости от ожидаемой нагрузки.

Пресеты можно менять только на повышение: minimal → medium и так далее. Это связано с техническим ограничением - под капотом лежат диски с файловой системой xfs, что накладывает архитектурное ограничение. Но если убрать эту переменную, то доработка по смене пресета на убывание имеет место быть.
topics - блок, где перечисляется список топиков
По умолчанию любой топик в точности соответствует аналогичной настройке в официальной документации Apache Kafka, если не указать иначе. Whitelist настроек, которые можно задать с кастомным параметром:
retention.ms segment.bytes cleanup.policy # Возможные значения: delete и compact delete.retention.ms max.compaction.lag.ms max.message.bytes min.cleanable.dirty.ratio min.compaction.lag.ms retention.bytes segment.ms
2. Выполняем коммит, фиксируем изменения:
3. Вы молодец, а платформа дальше всё сделает за вас! 🙌 👑
Пользователю остается подождать, когда кластер развернется и будет готов к работе. Статус кластера и топиков можно отслеживать на портале платформы App.Farm:
При деградации и неполадках статус кластера изменится на «Предупреждение» или «Ошибка» в зависимости от проблемы. Посмотреть подробный статус можно, если навестись на плашку/кнопку статуса.

Весь процесс от лица пользователя по итогу выглядит так:
1. Есть некий пользователь - бизнес-система, у которой есть потребность на использование Kafka.
2. Этот запрос направляется в нашу платформу декларативно через Gitlab.
3. Платформа подготавливает по запросу атомарный кластер под систему, топики, доступы и предоставляет это всё пользователю.

Пользователю платформы не нужно писать сопровождению: «создайте, пожалуйста, топик», «выдайте доступ», «переименуйте топики», «а можно ещё retention поменять вон там». Вместо этого пользователь самостоятельно описывает желаемое состояние в репозитории. Именно здесь и экономятся человеко-ресурсы. Сопровождение платформы будет подключаться в случае наличия проблем или при запросе на консультацию. Всё остальное время специалисты могут решать более важные и срочные задачи, не погрязая в рутине.
С чего всё начиналось
Изначально Kafka в нашей Paas-платформе не существовала от слова совсем. Но когда у бизнеса появилась потребность взаимодействия через Kafka на платформе, мы стали дружно думать, а как это все реализовать.
Так как речь идёт о банке, любой брокер для интеграции автоматически предполагал собой подход единой шины. Поэтому первое решение представляло собой такую картину:
Единый кластер, к которому должны были подключаться микросервисы всех бизнес-систем и подразделений банка.
Подключение микросервисов проходило через адаптеры, через которые происходит прозрачное подключение к брокеру + реализовывается контроль и мониторинга трафика.
Изоляция и разграничение доступов было на уровне топиков (multi-tenant by topic).

Всё было хорошо и просто до того момента, пока не начался этап тестирования и эксплуатации. Решение успели протестировать всего пара-тройка бизнес-систем, но по отзывам и показателям решение не стало успешным. Поэтому выпуск функционала первой версии в промышленное окружение так и не состоялось.
Что сломалось в идее единой шины
Всё есть опыт, независимо от того, удачный он или нет. Взяв паузу, мы стали анализировать следующее:
Какие проблемы есть?
Как мы их можем исправить ?
Как мы можем уже разработанный функционал и код переиспользовать ?
Что мы поняли:
1. При резком росте интеграционного обмена одной системы аффектились бы другие бизнес-системы в части ресурсов
Допустим, в одном кластере живут несколько бизнес-систем. У одной резко вырос интеграционный обмен. У второй тоже. Остальные системы в этот момент ничего не меняли, но внезапно начинают чувствовать просадку по ресурсам, ведь живут они в одном кластере.
2. Тенант на топике = плохая изоляция
Изоляция на уровне топиков здесь не спасает. Топик не изолирует CPU, память, диск. Формально системы разные, но фактически они конкурируют за один общий ресурсный пул.
3. Нет гарантии, что вычислительных ресурсов хватит всем, нет четкого менеджмента
Просадка ресурсного менеджмента. Одна система принесла условные 10 ГБ, другая – 30 ГБ. Но если всё складывается в общий пул, сложно гарантировать, что первая не начнёт потреблять 35 ГБ, а вторая при этом останется прибедненной. И не забываем, что должники есть везде: что будет, если система начнет пользоваться кластером, а ресурсы так и не принесет?
4. Нет возможности проведения нагрузочного тестирования + сложности прогнозирования нагрузки
Если несколько бизнес-систем живут в одном кластере, трудно корректно протестировать одну из них под нагрузкой. Тест одной команды влияет на соседей, а соседи по коммуналке влияют на результаты теста.
5. Обновления
Представим, что нужно обновить версию Kafka на единой шине. Не забываем, что недоступность должна быть минимальной. Этот вопрос можно решить реализацией backup кластера, на который можно всех переключить, пока обновляется основной кластер. Но:
Во-первых, обновление Kafka превращается в большой координационный проект. Нужно предупредить команды, проверить совместимость клиентов, заложить окно работ, подготовиться к инцидентам и надеяться, что в ближайшее время удастся хоть немного поспать после ночных работ.
Во-вторых, это затратно, если нужно рядом под боком иметь backup кластер, полностью идентичный основному или хотя бы на 60-70%.
В-третьих, для банка проведение таких работ с вероятностью простоя интеграционной шины – не просто техническая неприятность, а реальный бизнес-риск.
6. Мы поздно услышали пользователей и пошли не по тому сценарию

Мы недостаточно активно собирали пожелания и обратную связь от пользователей. На старте, когда только первая бизнес-система начала использовать Kafka, нам казалось, что единая шина закрывает все потребности. Когда бизнес-систем, использующих кластер, стало больше, чудесным образом выяснилось, что реальная потребность была совершенно другой: полная изоляция, управляемость, свои ресурсы, поддержка разных версий и более гибкая модель ответственности.
Так мы пришли к архитектуре 2.0.
Вторая версия: кластер под систему
Во второй версии мы отказались от идеи одной Kafka для всех и перешли к модели Kafka as a Service: создание Kafka-кластеров по запросу.
Теперь кластер выделяется под бизнес-систему. Причём не обязательно один. Если системе нужно несколько Kafka-кластеров, платформа должна уметь их предоставить.

Кластер под систему решает все проблемы и риски, которые у нас стояли:
1. Изоляция: каждый кластер работает независимо от других, что минимизирует риски, упрощает управление, тестирование и биллинг.
2. Обновления: обновление кластера одной системы не влияет на кластера других систем, что также дает возможность поддержки разных версий Kafka в зависимости от потребности самой системы.
3. Гибкость: пользователи платформы могут получить кластер под свои задачи, а это повышает эффективность использования ресурсов и параллельно решает проблему с биллингом
4. Безопасность: каждый кластер имеет свои настройки доступа, что повышает уровень защиты
Важно заметить, что первая архитектура общей шины НЕ была «плохой». Она просто не подходила для наших реальных потребностей и не решала поставленные задачи.
Архитектурные тезисы
Так как ядро платформы - это Kubernetes, то и Kafka должна разворачиваться в Kubernetes. Мы активно используем операторы в платформе (как кубовые, так и самописные), поэтому и управлением, и автоматизацией тоже должны были заниматься операторы. Дополнительно было решено оставить концепцию с адаптерами.
Тезисы архитектурного решения получились следующими:
Kafka закрыта SASL Bearer аутентификацией. Мы хотели, чтобы использовался наш Keycloak и типичный стек аутентификации.
Доступ к Kafka-брокерам осуществляется через адаптер.
Адаптер аутентифицируется от имени бизнес-приложения.
Бизнес-приложение работает с адаптером без лишнего кода для аутентификации.
Доступ к кластеру через kafka-адаптер остался почти неизменным:
Kafka-адаптер в этой архитектуре играет важную роль:
Авторизация и аутентификация на кластере
Контроль доступа к ресурсам за счет network policies
Журналирование межсистемного взаимодействия
Платформа централизованно управляет доступами
Все связи декларативны
Не забываем про правило хорошего тона: запрещено всё, что явно не разрешено.

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

Развёртывание и управление
Для развертывания и управления мы выбрали Kafka Strimzi. Но Strimzi был выбран не просто так, а на основе критериев:
Open-source
Зрелый Kubernetes-native проект
Активная поддержка комьюнити
Автоматизация почти во всём
Поддержка TLS, SSL, SASL, OAUTHBEARER
Декларативное управление, что идеально для GitOps
Поддержка KRaft (взгляд на будущее)
При сравнении с другими решениями Strimzi одержал полную победу:

Важный плюс Strimzi – релизные окна версий. Один оператор поддерживает ограниченный набор версий Kafka. А чтобы поддерживать Kafka на разных версиях, нужно явно понимать, какая версия оператора с какой Kafka совместима. Для этого мы ввели дополнительный объект binding. Он фиксирует совместимую пару Kafka и Strimzi, а также нужный образ.
Например:
bindings: - kafka: "2.8.0" strimzi: "0.25.0" image: "strimzi-kafka:0.25.0-kafka-2.8.0"
Смысл простой: платформа не должна гадать, каким оператором обслуживать конкретный кластер.

Создание кластера от лица платформы
1. В Gitlab в проекте с описанием информационной бизнес-системы декларируется блок с описанием желаемого кластера. Сначала декларация попадает в GitLab CI/CD. После прохождения pipeline deploy toolchain преобразует её в платформенный Kubernetes-ресурс, условный PlatformKafkaCluster.
2. Дальше срабатывает служебный оператор платформы. Его контроллер подписан на PlatformKafkaCluster и начинает создавать всё, что нужно:
Kafka-адаптер
Keycloak-клиент (пометка: здесь на помощь приходит ещё один самописный оператор)
Необходимые network policies
Ресурсы для дальнейшего создания Kafka-кластера
3. После этого в работу вступает Strimzi. Он создаёт реальный Kafka-кластер уже в инфраструктурном Kubernetes-кластере. Про сам Strimzi можно почитать в официальной документации:

Послесловие
Оглядываясь назад, мы понимаем, что где-то можно было сменить курс раньше, где-то сделать проще и быстрее. Но однозначно можно сказать, что:
Kafka как услуга возможна и нужна.
Архитектура меняется под эксплуатацию.
Если нет идеального решения под вас, сделайте его сами.
Не стройте лишнего, стройте под себя.
Делайте удобно и для пользователей, и для сопровождения: собирайте обратную связь, прислушивайтесь к проблемам и экономьте время и нервы.
На этом первая часть подходит к концу. Во второй части обсудим топ технических ошибок, с которыми мы столкнулись в процессе эксплуатации: симптомы, исследования и решения. Будет не только про красивую архитектуру, но и про неприятные вещи: один адаптер на сотни микросервисов, случайные отвалы авторизации, деградация после обновления плагина и не только.
