Делаем свой docker-образ Apache Kafka
В 2024 году мы не дождались выхода в свет Apache Kafka 4.0, в которой окончательно исчезнет поддержка ZooKeeper, оставив нам для создания кластеров только KRaft. Кто-то давно уже перешёл на эту прекрасную технологию, другие же размышляют, как им жить дальше — оставаться на линейке 3.х или в омут с головой.
Новогодние каникулы самое подходящее время для того, что пощупать новую версию. Хотя код невозбранно доступен в Github проекта Apache Kafka, docker-образов текущих сборок 4.0 мне найти не удалось. Также поддержку 4.0 ещё не добавили в довольно популярный, и с недавних пор мною любимый, k8s-оператор strimzi. Ну что же, придётся устроить себе праздник самому!
Чего изволим?
Итак, моя цель сделать docker-образ Apache Kafka на текущем коммите ветки trunk (aka nigthly build). На момент написания статьи по данным Docker Hub самыми популярными сборками были:
Проект | Версия | База | Kafka | Java |
bitnami/kafka | 3.9.0-debian-12-r4 | docker.io/bitnami/minideb: bookworm | 3.9.0 | 17.0.13 |
confluentinc/cp-kafka | 7.8.0 | |||
wurstmeister/kafka | 2.13–2.8.1 | openjdk:11-jre-slim | 2.8.1 | 11 |
quay.io/strimzi/kafka | 0.45.0-kafka-3.9.0 | ubi9/ubi-minimal: latest | 3.9.0 | 17.0.13 |
apache/kafka | 3.9.0 | eclipse-temurin:21-jre-alpine | 3.9.0 | 21.0.5 |
В то же время, хочется посмотреть на кластер в kubernetes/OpenShift, а мучиться с его настройкой наоборот, не хочется. При таких вводных нужен такой образ, который можно было бы подать на вход оператору strimzi. Тех, кто не знает, что это такое, отправляю на страницу проекта в Github. В двух словах, он обеспечивает развёртывание, контроль работоспособности и обновление кластера, а также имеет настроенную интеграцию с prometheus и grafana.
Но strimzi не работает с любым kafka-образом. Более того, перечень версий kafka, с которыми работает оператор, фиксирован и задан в настройках оператора в соотнесении с образами:
spec:
template:
spec:
containers:
env:
- name: STRIMZI_KAFKA_IMAGES
value: |
3.8.0=quay.io/strimzi/kafka:0.45.0-kafka-3.8.0
3.8.1=quay.io/strimzi/kafka:0.45.0-kafka-3.8.1
3.9.0=quay.io/strimzi/kafka:0.45.0-kafka-3.9.0
О том, как устанавливать и настраивать strimzi здесь рассказывать не буду. Тема вполне раскрыта в документации, здесь отмечу только, что все развёртывания ведутся в неймспейс kafka.
Достаточно быстро становится понятно, что произвольную версию указать нельзя, и про версии выше 3.9.0 strimzi версии 0.4.5 не знает. Просто поменять переменную, и версии »3.9.0» сопоставить какой-то «левый» образ тоже нельзя, кластер не заводится. Разработчики strimzi про 4.0 пока в раздумьях.
В результате приходим к следующей задаче:
Собрать совместимый с оператором strimzi образ на последнем коммите kafka и java 23;
Настроить кластер на основе более-менее стандартных настроек;
Не наделать по дороге слишком много форков.
С чего начать?
Пойти можно двумя путями: взять за основу strimzi-образ или образы от Apache. Более и гибким интересным подходом мне представляется разобраться, что именно составляет специфику strimzi-образов и добавить соответствующие элементы в канонические сборки от Apache, у которой 2 варианта, jvm и native. Мы опробуем только первый. В качестве базы я возьму eclipse-temurin:23-jre-alpine (по аналогии с «ванильным» образом от Apache Kafka, у которых пока ещё консервативный eclipse-temurin:21-jre-alpine).
jvm-образ включает в себя стадию «прогрева», с помощью скрипта jsa_launch предварительно запускается кластер kafka, создаются топик, producer и consumer. По результатам формируются CDS-файлы /opt/kafka/storage.jsa и /opt/kafka/kafka.jsa, использование которых позволяет сократить время запуска контейнера. Это вещь хорошая и нужная, оставляем.
Оба dockerfile выкачивают сборку kafka из удалённого репозитория. Это нам не надо, мы со своим, убираем. Если вы, как и я, собираете в windows, на забывайте про core.autocrlf = false в конфигурацию git и добавляйте chmod +x в dockerfile.
Разбираемся со strimzi
Для начала отмечу, что в документации я спецификации strimzi-образов не нашёл (может, где-то оно и есть, но мне не попалось), и дальнейшие выводы были сделаны на основе анализа кода. Код выкачиваем из репозитория на Github https://github.com/strimzi/strimzi-kafka-operator.
Для сборки образов strimzi предоставляет набор скриптов в подпроекте docker-images. Сборка образа оператора нас не интересует, а kafka-образ желающие могут собрать скриптом docker-images/kafka-based/build.sh. Но, во-первых, у меня лапки windows, а во-вторых мне больше нравится «ванильная» компоновка от Apache Kafka и их скрипт сборки на python. Так или иначе, в нашем образе должно быть:
Скрипт запуска /opt/kafka/kafka_run.sh;
Скрипт проверки readiness /opt/kafka/kafka_readiness.sh;
Скрипт проверки liveness /opt/kafka/kafka_liveness.sh.
Расположение скриптов изменить нельзя, оно прибито гвоздями в коде, но содержимое их можно, конечно же, поменять. Мы постараемся сделать наш образ максимально strimzi-обра́зным и сильно менять скрипты не будем, но т.к. мы выше прогревали JVM, надо добавить в параметры запуска использование CDS-файлов в kafka_run.sh (во всех прочих отношениях оставшийся неизменным).
Далее, нам надо добавить вот что:
Это всё вещи, нужность которых понятна. Необходимость «Cruise Control for Apache Kafka» от linkedin/cruise-control не столь очевидна, и его я брать не стал. Может, в другой раз.
Далее, образу жизненно необходим kafka-agent, запускающийся как javaagent и реализующий jetty-servlet, возвращающий проверки readiness и liveness. Как было сказано выше, мы можем данные проверки переопределить, сохранив базовый контракт. Для readiness это возвращать пустой ответ и код HTTP 204, для liveness более сложная схема. kafka-agent подключается к JMX метрикам kafka, опираясь на которые формирует статус брокера. Для реализации функции обновления кластера strimzi приняли во внимание возможное «подвисание» брокеров в ходе восстановления логов.
К сожалению, тут нас подстерегает серьёзная проблема: в версии 4.0 была реализована доработка KIP-1032, поднявшая версию jetty с 9.4.56 до 12.0.15 и совершившую переход на Jakarta и JavaEE 10. В результате нарушается совместимость со всеми приложениями, не готовыми перейти на эти версии. Если кто-то использует embedded kafka — самое время задуматься об апгрейде! В результате мне пришлось переписать kafka-agent на jetty 12, благо в нём всего лишь один класс. Фикс выложен в папку strimzi/patch репозитория kvmorozov/kafka-utils.
Сборка
Для сборки нам надо:
git pull trunk
скрипт сборки docker_build_test.py из поставки от Apache
выполнить gradle таску releaseTarGz из kafka: core, в результате чего получим .tgz, который пойдёт на вход образа
копируем собранный файл из \kafka\core\build\distributions в папку, где лежит нужный dockerfile
запускаем python docker_build_test.py kmorozov/kafka --image-tag=4.1.0-jvm --image-type=jvm --kafka-url=kafka_2.13–4.1.0-SNAPSHOT.tgz -b
Проверяем
kubectl create -f 'https://strimzi.io/install/latest? namespace=kafka' -n kafka
kubectl edit deployment strimzi-cluster-operator -n kafka
заменяем quay.io/strimzi/kafka:0.45.0-kafka-3.9.0 на kmorozov/kafka:4.1.0-jvm
ждём, пока оператор перезапустится
kubectl apply -f
\examples\kafka\kraft\kafka-ephemeral.yaml -n kafka kubectl wait kafka/my-cluster --for=condition=Ready --timeout=300s -n kafka
настройка prometheus и grafana имеет некоторые нюансы, их пропустим
kubectl get pods -n kafka
Далее идём в pod my-cluster-broker-0. Из него мы создадим два топика и выполним нагрузочное тестирование:
./bin/kafka-topics.sh --create --bootstrap-server=localhost:9092 --topic test-3p-1rf --partitions 3 --replication-factor 1
./bin/kafka-topics.sh --create --bootstrap-server=localhost:9092 --topic test-3p-3rf --partitions 3 --replication-factor 3
./bin/kafka-producer-perf-test.sh
--topic test-3p-1rf
--num-records 50000000
--record-size 100
--throughput -1
--producer-props acks=1
bootstrap.servers=localhost:9092
buffer.memory=67108864
batch.size=8196./bin/kafka-producer-perf-test.sh
--topic test-3p-3rf
--num-records 50000000
--record-size 100
--throughput -1
--producer-props acks=1
bootstrap.servers=localhost:9092
buffer.memory=67108864
batch.size=8196
В обоих случаях producer ждёт подтверждения записи (acks=1), во втором случае — от всех трёх брокеров.
После проверки каждого образа всё удаляем через kubectl delete-f
Результаты
Поскольку Дед Мороз мне не положил под ёлочку ни мощный сервер, ни оплату вычислительных мощностей в облаке, измерения я проводил на своей локальной машине в попугаях, располагая 16 ядрами и 12 Gi RAM (всего у меня 16 Gi RAM, но для WSL я отдал 12). Скромность ресурсов сделала бессмысленной тюнинг, поэтому ниже представлены результаты забега на моём strimzi-кластере без какой-либо дополнительной настройки. Strimzi позволяет задать настройки JVM, примерно так:
apiVersion: kafka.strimzi.io/v1beta2
kind: Kafka
metadata:
name: strimzi
spec:
kafka:
resources:
limits:
memory: "5Gi"
jvmOptions:
-Xmx: "4G"
-Xms: "2G"
-XX:
foo: bar
Но мы это тоже делать не будем, соревноваться будем с дефолтными настройками. В зачёт шла лучшая из трёх попыток.
Образ | OS | JVM | test-3p-1rf | test-3p-3rf | ||
rps | avg, ms | rps | avg, ms | |||
Strimzi 3.8.0 | RHEL 9.5 | 17.0.13 | 1017438 | 5.20 | 755161 | 116.41 |
Strimzi 3.9.0 | RHEL 9.5 | 17.0.13 | 984174 | 2.45 | 777786 | 49.21 |
kmorozov-jvm | Alpine Linux v3.20 | 23.0.1 | 977823 | 2.71 | Работает не стабильно |
В первом тесте мой образ выступил вполне достойно, а во втором ему иногда (обычно, во время третьего прогона второго теста) не хватало ресурсов :(
Потребление ресурсов хоста во время нагрузки
Но когда работает, то прекрасно отдаёт метрики в prometheus, и они прекрасно отображаются в grafana:
Во время нагрузки по сценарию topic test-3p-3rf
Выводы
Цель достигнута, образ собран и работает. Было непросто пофиксить проблемы совместимости, но с использованием strimzi я был хотя бы избавлен от сложностей с развёртыванием кластера (мне это потребовалось сделать раз 100). Однако, с учётом нагрузочного тестирования, версией 4.0 пока можно пользоваться только на свой страх и риск. Рано или поздно она станет лучше и победит предшественников, ибо прогресс не остановить.
Как видно, репликацию 50 млн сообщений на 3 брокера 4.0 без дополнительных настроек не тянет. Наверное, если её оттюнить и, возможно, выбрать более удачную операционную систему, она побьёт 3.9.0 уже сейчас. Но об этом в следующей статье.
Habrahabr.ru прочитано 1906 раз