Изучаем Apache Kafka с нуля. Урок 3. Kafka Docker KRaft. Однонодовый кластер

Изучаем Apache Kafka с нуля. Урок 3. Kafka Docker KRaft. Однонодовый кластер

Изучаем Apache Kafka с нуля. Урок 3. Kafka Docker KRaft. Однонодовый кластер

 

ПО: Apache Kafka 4.2.0 (образ apache/kafka:4.2.0)

Docker: Engine 28.1, Compose v2.35  \

Окружение: Ubuntu 22.04 LTS / macOS 14+

Уровень: начинающий

В прошлом уроке мы установили Apache Kafka 4.2.0 вручную прямо на хост. Получилось рабочее окружение, но у него есть один минус: оно живёт только на вашей машине и требует ручной настройки при каждом переезде. Kafka Docker решает эту проблему — однажды написанный docker-compose.yml запустит Kafka на любом хосте одной командой.

В этом уроке мы поднимем однонодовый Kafka-кластер в KRaft-режиме через Docker Compose. Разберём официальный образ apache/kafka, объясним ключевые переменные окружения и проверим работу брокера теми же командами, что использовали в уроке 2. В конце — отдельный cheatsheet с командами Docker для работы с Kafka.

Если хотите углубиться в администрирование кластеров — загляните на курс KAFKA. Администрирование кластера Kafka. Там разбирают продакшн-топологии, мониторинг и сетевую конфигурацию на живых примерах.

 

Зачем запускать Kafka в Docker?

Ручная установка из урока 2 отлично подходит для знакомства с внутренностями Kafka. Но в реальной разработке она неудобна по нескольким причинам.

  • Изоляция. Docker-контейнер не засоряет хост-систему. Kafka со всеми данными живёт внутри контейнера и удаляется одной командой.
  • Воспроизводимость. docker-compose.yml фиксирует версию образа, порты и конфигурацию. Любой разработчик в команде поднимет точно такое же окружение.
  • Скорость. Команда docker compose up -d запускает брокер за 10-15 секунд — без ручного форматирования хранилища и настройки PATH.
  • Параллельность. На одной машине можно держать несколько изолированных Kafka-кластеров с разными версиями.

Кроме того, Docker — стандарт де-факто для локального стека разработки. Если в вашем проекте уже есть PostgreSQL или Redis в Docker, добавить туда Kafka займёт несколько строк в существующем compose-файле.

 

Требования и подготовка к установке Apache Kafka KRaft кластера в Docker

Перед началом убедитесь, что Docker установлен и работает. Проверить просто.

docker --version
docker compose version

Нужные версии — Docker Engine 20.10+ и Docker Compose v2.x (встроен в Docker Desktop). Если у вас старый docker-compose (v1, отдельная утилита) — рекомендую обновить до Compose v2, где команда пишется через пробел: docker compose вместо docker-compose.

Порты 9092 и 9093 должны быть свободны. Если в прошлом уроке Kafka ещё запущена — остановите её через kafka-server-stop.sh перед тем, как поднимать контейнер.

 

Официальный образ apache/kafka

Apache Software Foundation публикует официальные Docker-образы Kafka начиная с версии 3.7.0. Образ доступен на Docker Hub как apache/kafka. Для нашего курса используем тег 4.2.0.

Этот образ работает в KRaft-режиме по умолчанию — ZooKeeper внутри него нет. При старте контейнера скрипт-обёртка автоматически форматирует хранилище метаданных, если видит переменную CLUSTER_ID. Нам не нужно вручную вызывать kafka-storage.sh format, как в уроке 2.

Образ содержит полный дистрибутив Kafka 4.2.0 с папкой bin/ внутри. Все утилиты из уроков 5-33 доступны через docker exec — об этом подробнее в разделе проверки ниже.

 

Структура проекта для развертывания Apache Kafka Kraft в Docker

Создаём рабочую директорию для урока и один файл внутри неё.

mkdir kafka-lesson3
cd kafka-lesson3

Всё, что нам нужно — один файл docker-compose.yml. Никаких дополнительных конфигов, скриптов или Dockerfile. Образ apache/kafka принимает конфигурацию через переменные окружения.

docker-compose.yml для однонодового кластера

Вот полный файл для запуска одного брокера Kafka 4.2.0 в KRaft-режиме.

# apache/kafka:4.2.0, Docker Compose v2.35

services:
  kafka:
    image: apache/kafka:4.2.0
    container_name: kafka-kraft-single
    ports:
      - "9092:9092"
    environment:
      KAFKA_NODE_ID: 1
      KAFKA_PROCESS_ROLES: broker,controller
      KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
      KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,CONTROLLER:PLAINTEXT
      KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
      KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
      KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
      KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
      CLUSTER_ID: MkU3OEVBNTcwNTJENDM2Qk
    volumes:
      - kafka-data:/var/lib/kafka/data

volumes:
  kafka-data:

Разберём переменные окружения, которые отличаются от конфига из урока 2.

Ключевые переменные окружения

Каждая переменная в блоке environment соответствует параметру из server.properties, только с префиксом KAFKA_ и в верхнем регистре. Разберём те, что требуют пояснений.

  • KAFKA_NODE_ID. Уникальный ID этого узла в кластере. Эквивалент node.id=1 из server.properties.
  • KAFKA_PROCESS_ROLES. Роли этого процесса. Значение broker,controller означает комбинированный режим — тот самый, что мы настраивали вручную в уроке 2.
  • KAFKA_LISTENERS. На каких адресах и портах слушать. Порт 9092 для клиентов, 9093 для контроллерного трафика внутри кластера.
  • KAFKA_ADVERTISED_LISTENERS. Адрес, который брокер сообщает клиентам при подключении. Для локальной разработки — localhost:9092. При работе на удалённом сервере замените на реальный IP или hostname.
  • KAFKA_CONTROLLER_QUORUM_VOTERS. Список узлов кворума. Формат: node_id@host:port. Здесь указан hostname kafka, а не localhost — это имя сервиса внутри Docker-сети, через которое контейнер обращается сам к себе по внутреннему трафику.
  • CLUSTER_ID. Уникальный идентификатор кластера в формате base64 UUID. Образ использует его для автоматического форматирования хранилища при первом запуске. Значение можно сгенерировать через kafka-storage.sh random-uuid или взять любое корректное — для учебного стенда подходит то, что в примере.
  • KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1. Для однонодового кластера replication factor системных топиков должен быть 1, иначе Kafka откажется стартовать. В продакшне с несколькими брокерами это значение ставят 3.

Значение KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0 отключает задержку перед первым ребалансом consumer group. В продакшне этот параметр дают время подключиться всем консьюмерам перед стартом. Для разработки задержка только мешает.

Volumes. Почему это важно

Строка kafka-data:/var/lib/kafka/data монтирует именованный Docker volume в директорию данных брокера. Без этого все данные топиков и метаданные хранятся внутри контейнера и исчезают при docker compose down.

С volume данные сохраняются на хосте между перезапусками. Docker управляет этим volume самостоятельно — смотреть, где он физически лежит, обычно не нужно, но можно через docker volume inspect kafka-lesson3_kafka-data.

Шаг 1. Запуск кластера

Находясь в директории kafka-lesson3, запускаем контейнер в фоновом режиме.

docker compose up -d

При первом запуске Docker скачает образ apache/kafka:4.2.0 — около 600 МБ. Последующие запуски моментальные, образ кешируется локально. Смотрим логи, чтобы убедиться в успешном старте.

docker compose logs -f kafka

Брокер готов к работе, когда в логах появится строка KafkaServer id=1] started. Обычно это занимает 10-15 секунд. После этого нажимайте Ctrl+C, чтобы выйти из слежения за логами — контейнер продолжит работу в фоне.

Шаг 2. Проверка брокера

Проверяем статус контейнера и подключаемся к брокеру изнутри.

docker compose ps

В колонке Status должно быть running. Теперь заходим в контейнер и используем стандартные утилиты Kafka для проверки.

# Создаём тестовый топик изнутри контейнера
docker exec -it kafka-kraft-single kafka-topics.sh \
  --bootstrap-server localhost:9092 \
  --create \
  --topic test-topic \
  --partitions 1 \
  --replication-factor 1

# Проверяем список топиков
docker exec -it kafka-kraft-single kafka-topics.sh \
  --bootstrap-server localhost:9092 \
  --list

Команды выполняются так же, как и в уроке 2, только через docker exec. Это важный момент — все утилиты из папки bin/, которые мы разбираем в уроках 5-33, работают внутри контейнера точно так же, как на голом хосте. Разница только в том, как мы до них добираемся.

Шаг 3. Отправка и чтение сообщений

Запускаем продюсера и проверяем, что брокер принимает и отдаёт сообщения.

# Отправляем тестовое сообщение
echo "hello from docker" | docker exec -i kafka-kraft-single kafka-console-producer.sh \
  --bootstrap-server localhost:9092 \
  --topic test-topic
# Читаем сообщение
docker exec -it kafka-kraft-single kafka-console-consumer.sh \
  --bootstrap-server localhost:9092 \
  --topic test-topic \
  --from-beginning \
  --max-messages 1

Если в терминале появилась строка hello from docker — брокер работает корректно. Продюсер и консьюмер успешно взаимодействуют через топик.

Подключение к брокеру с хоста

Порт 9092 пробрасывается на хост-машину. Это значит, что к брокеру можно подключаться напрямую с хоста, если у вас установлены утилиты Kafka из урока 2.

# Команда выполняется НА ХОСТЕ, не внутри контейнера
kafka-topics.sh \
  --bootstrap-server localhost:9092 \
  --list

С точки зрения клиента брокер в контейнере и брокер на голом хосте выглядят одинаково — один и тот же адрес localhost:9092. Это удобно при разработке приложений — меняете только способ развёртывания Kafka, код приложения не трогаете.

Остановка и очистка

Docker Compose даёт три уровня «остановки» в зависимости от того, что нужно сделать.

  • Пауза без удаления данных. Команда docker compose stop останавливает контейнер, но сохраняет его состояние и данные в volume. Следующий docker compose start поднимет его снова с теми же данными.
  • Удаление контейнера, сохранение данных. Команда docker compose down удаляет контейнер и сеть, но оставляет volume нетронутым. Данные топиков сохраняются.
  • Полная очистка. Команда docker compose down -v удаляет всё вместе с volume. Брокер при следующем запуске стартует с чистого листа.

Для разработки обычно используют docker compose down при завершении работы и docker compose up -d при следующем сеансе. Данные при этом сохраняются между сессиями.

# Остановить без удаления данных
docker compose down

# Полная очистка со сбросом всех данных
docker compose down -v

Архитектура кластера Apache Kafka Kraft в Docker. Что происходит внутри?

Диаграмма показывает, как связаны хост, Docker-сеть и процесс Kafka внутри контейнера.

Схема однонодового Kafka KRaft кластера в Docker - проброс портов с хоста в контейнер, роли брокера и контроллера, volume для хранения данных

Клиент на хосте подключается к localhost:9092. Docker перенаправляет трафик в контейнер на тот же порт. Внутри один процесс Kafka выполняет роли брокера и контроллера одновременно — комбинированный KRaft-режим, как в уроке 2. Данные пишутся в volume, который живёт на хосте вне зависимости от жизненного цикла контейнера.

 

Сравнение. Ручная установка кластера Apache Kafka vs Kafka Docker

Параметр Ручная установка (урок 2) Docker Compose (урок 3)
Время первого запуска 10-15 мин 2-3 мин (скачать образ + старт)
Время повторного запуска ~30 сек 10-15 сек
Изоляция от хост-системы нет полная
Форматирование хранилища вручную (kafka-storage.sh) автоматически по CLUSTER_ID
Настройка PATH нужна не нужна
Сохранность данных при перезапуске да (в /tmp/kraft-combined-logs) да (Docker volume)
Воспроизводимость на другой машине низкая высокая
Параллельные версии Kafka неудобно легко

Для локальной разработки Docker-подход предпочтительнее почти всегда. Ручная установка полезна, когда нужно понять внутреннее устройство Kafka — именно поэтому мы начали с неё в уроке 2.

Заключение

Мы запустили однонодовый кластер Kafka в Docker и проверили, что брокер принимает подключения. Один узел — это удобно для разработки, но не даёт никакой отказоустойчивости.

В уроке 4 развернём кластер из трёх брокеров с помощью Docker Compose. Там увидим, как Kafka распределяет партиции между узлами и что происходит, когда один из брокеров падает.

Референсные ссылки

Все уроки курса

Тема Ссылка
1 Установка Kafka с Zookeeper https://bigdataschool.ru/blog/news/lesson1-kafka-zookeeper-install/
2 Установка Kafka в режиме KRaft https://bigdataschool.ru/blog/news/lesson2-kafka-kraft-install/
3 Docker KRaft: однонодовый кластер https://bigdataschool.ru/blog/news/lesson3-kafka-docker-single/
4 Docker KRaft: 3-нодовый кластер https://bigdataschool.ru/blog/news/lesson4-kafka-docker-cluster/
5 Утилиты bin/: переменные окружения и основы https://bigdataschool.ru/blog/news/lesson5-kafka-bin-intro/
6 kafka-topics.sh https://bigdataschool.ru/blog/news/lesson6-kafka-topics/
7 kafka-console-producer.sh https://bigdataschool.ru/blog/news/lesson7-kafka-console-producer/
8 kafka-console-consumer.sh https://bigdataschool.ru/blog/news/lesson8-kafka-console-consumer/
9 kafka-server-start.sh / kafka-server-stop.sh https://bigdataschool.ru/blog/news/lesson9-kafka-server-start-stop/
10 kafka-storage.sh https://bigdataschool.ru/blog/news/lesson10-kafka-storage/
11 kafka-cluster.sh https://bigdataschool.ru/blog/news/lesson11-kafka-cluster/
12 kafka-metadata-quorum.sh https://bigdataschool.ru/blog/news/lesson12-kafka-metadata-quorum/
13 kafka-metadata-shell.sh https://bigdataschool.ru/blog/news/lesson13-kafka-metadata-shell/
14 kafka-features.sh https://bigdataschool.ru/blog/news/lesson14-kafka-features/
15 kafka-configs.sh https://bigdataschool.ru/blog/news/lesson15-kafka-configs/
16 kafka-log-dirs.sh https://bigdataschool.ru/blog/news/lesson16-kafka-log-dirs/
17 kafka-dump-log.sh https://bigdataschool.ru/blog/news/lesson17-kafka-dump-log/
18 kafka-delete-records.sh https://bigdataschool.ru/blog/news/lesson18-kafka-delete-records/
19 kafka-consumer-groups.sh https://bigdataschool.ru/blog/news/lesson19-kafka-consumer-groups/
20 kafka-streams-application-reset.sh https://bigdataschool.ru/blog/news/lesson20-kafka-streams-reset/
21 kafka-leader-election.sh https://bigdataschool.ru/blog/news/lesson21-kafka-leader-election/
22 kafka-reassign-partitions.sh https://bigdataschool.ru/blog/news/lesson22-kafka-reassign-partitions/
23 kafka-replica-verification.sh https://bigdataschool.ru/blog/news/lesson23-kafka-replica-verification/
24 kafka-acls.sh https://bigdataschool.ru/blog/news/lesson24-kafka-acls/
25 kafka-broker-api-versions.sh https://bigdataschool.ru/blog/news/lesson25-kafka-broker-api-versions/
26 kafka-get-offsets.sh https://bigdataschool.ru/blog/news/lesson26-kafka-get-offsets/
27 kafka-verifiable-producer/consumer.sh https://bigdataschool.ru/blog/news/lesson27-kafka-verifiable/
28 kafka-producer-perf-test.sh https://bigdataschool.ru/blog/news/lesson28-kafka-producer-perf/
29 kafka-consumer-perf-test.sh https://bigdataschool.ru/blog/news/lesson29-kafka-consumer-perf/
30 kafka-mirror-maker.sh https://bigdataschool.ru/blog/news/lesson30-kafka-mirror-maker/
31 connect-standalone.sh https://bigdataschool.ru/blog/news/lesson31-kafka-connect-standalone/
32 connect-distributed.sh https://bigdataschool.ru/blog/news/lesson32-kafka-connect-distributed/
33 kcat: альтернативный CLI https://bigdataschool.ru/blog/news/lesson33-kcat/
Изменение базового тарифа с 1 января 2026 года Подробнее