Kafka в режиме KRaft на Docker Swarm: пошаговая настройка кластера на Debian 12
Разворачиваем отказоустойчивый Kafka‑кластер в режиме KRaft поверх Docker Swarm на двух виртуальных машинах с Debian 12, с доступом через Kafka UI и SSH‑туннели.
Михаил Ровнягин
5 октября 2024 г.

Задача
В этой статье разберём, как с нуля развернуть минимальный Kafka‑кластер в режиме KRaft поверх Docker Swarm на двух виртуальных машинах с Debian 12. В результате мы получим:
- два узла
hl1.zilиhl2.zilс Docker Swarm; - кластер Kafka из двух брокеров/контроллеров в режиме KRaft;
- UI для управления кластером (Kafka UI);
- возможность безопасно работать с топиками (создавать, читать, писать) как с серверов, так и с локальной машины через SSH‑туннель.
Статья рассчитана на инженеров, которые уже уверенно работают с Linux и Docker, но только начинают погружаться в Kafka и KRaft.
1. Подготовка виртуальных машин Debian 12
Для примера будем использовать две виртуальные машины с Debian 12:
hl1.zil— первый узел кластера Kafka;hl2.zil— второй узел кластера Kafka и менеджер Docker Swarm.
1.1. Базовые параметры VM
Минимальные рекомендуемые ресурсы (на каждую VM):
- CPU: 2 vCPU
- RAM: 4 ГБ (лучше 8 ГБ)
- Диск: от 40 ГБ
- Сеть: одна сетевая карта в одной подсети (например,
10.0.0.0/24)
Предположим, что IP‑адреса:
hl1.zil→10.0.0.11hl2.zil→10.0.0.12
1.2. Настройка имён хостов и /etc/hosts
После установки Debian 12 на каждом узле зададим корректное доменное имя.
На hl1:
sudo hostnamectl set-hostname hl1.zil
На hl2:
sudo hostnamectl set-hostname hl2.zil
Перелогиньтесь в сессию, чтобы prompt отображал новое имя.
Далее пропишем имена узлов в /etc/hosts на каждом сервере:
sudo nano /etc/hosts
Пример содержимого:
127.0.0.1 localhost
10.0.0.11 hl1.zil hl1
10.0.0.12 hl2.zил hl2
Если у вас есть внутренняя DNS‑зона, можно завести A‑записи для hl1.zil и hl2.zil, но для простоты достаточно /etc/hosts.
1.3. Обновление системы и установка Docker
На обоих узлах обновим пакеты и установим Docker:
sudo apt update && sudo apt upgrade -y
# Утилиты
sudo apt install -y ca-certificates curl gnupg lsb-release
# Добавляем официальный репозиторий Docker
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) \
signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/debian \
$(lsb_release -cs) stable" \
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
# Docker Engine + CLI + плагин docker compose
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# (опционально) Добавим текущего пользователя в группу docker
sudo usermod -aG docker "$USER"
После добавления в группу docker перелогиньтесь или пересоздайте SSH‑сессию.
Проверим, что Docker работает:
docker info
2. Настройка Docker Swarm
Мы будем использовать hl2.zil как менеджер Swarm, а hl1.zil — как воркер.
2.1. Инициализация Swarm на менеджере
На hl2.zil:
sudo docker swarm init --advertise-addr <HL2_IP>
Подставьте реальный IP hl2.zil, например:
sudo docker swarm init --advertise-addr 10.0.0.12
Команда выведет пример строки для подключения воркеров, вида:
docker swarm join --token <SWARM_TOKEN> 10.0.0.12:2377
2.2. Подключение worker‑узла
На hl1.zil (worker) выполним команду с токеном, полученным на шаге выше:
sudo docker swarm join --token <SWARM_TOKEN> <HL2_IP>:2377
Пример:
sudo docker swarm join --token SWMTKN-1-... 10.0.0.12:2377
Проверяем состояние кластера на менеджере hl2.zil:
docker node ls
Вы должны увидеть два нода — один в роли Leader, второй как Worker.
3. Брандмауэр и открытие портов
Для работы Swarm, Kafka и Kafka UI откроем необходимые порты на обоих узлах.
Устанавливаем UFW:
sudo apt install ufw -y
Разрешаем нужные порты:
# Kafka internal port (broker communication)
sudo ufw allow 9092/tcp
# Kafka controller communication (KRaft)
sudo ufw allow 9093/tcp
# Kafka external port (клиентские подключения)
sudo ufw allow 9094/tcp
# Kafka UI
sudo ufw allow 8080/tcp
# Docker Swarm management
sudo ufw allow 2377/tcp
# Node‑to‑node communication (gossip)
sudo ufw allow 7946/tcp
# Overlay network (VXLAN)
sudo ufw allow 4789/udp
# Включаем UFW
sudo ufw enable
Статус:
sudo ufw status
4. Docker‑стек с Kafka в режиме KRaft и Kafka UI
Теперь опишем стек docker-compose.yml, который будет развёрнут в Docker Swarm через docker stack deploy.
Архитектура:
- два сервиса
kafka-1иkafka-2на основеconfluentinc/cp-kafka:7.6.1; - оба узла выполняют роли
broker,controllerв режиме KRaft; - внешний листенер (
EXTERNAL://) публикуется на 9094 в режимеhost; - хосты:
kafka-1закреплён заhl1.zil;kafka-2закреплён заhl2.zil;
- Kafka UI (
provectuslabs/kafka-ui) развёрнут на менеджере.
4.1. Генерация CLUSTER_ID
Kafka в режиме KRaft использует CLUSTER_ID. Сгенерируем его один раз и используем на обоих брокерах:
docker run --rm confluentinc/cp-kafka:7.6.1 kafka-storage random-uuid
Пример вывода:
2Sy4fmVPRlyy6abZuLghoA
Это значение должно быть одинаковым для всех брокеров кластера. Далее будем использовать его в переменной CLUSTER_ID.
4.2. Файл docker-compose.yml
На менеджере (hl2.zil) создадим файл docker-compose.yml:
nano docker-compose.yml
И поместим в него следующее содержимое:
version: '3.8'
networks:
kafka-net:
driver: overlay
volumes:
kafka-1-data:
kafka-2-data:
services:
kafka-1:
image: confluentinc/cp-kafka:7.6.1
container_name: kafka-1
networks:
- kafka-net
ports:
- target: 9094
published: 9094
protocol: tcp
mode: host
environment:
KAFKA_NODE_ID: 1
KAFKA_PROCESS_ROLES: 'broker,controller'
# docker run --rm confluentinc/cp-kafka:7.6.1 kafka-storage random-uuid
CLUSTER_ID: '2Sy4fmVPRlyy6abZuLghoA'
KAFKA_CONTROLLER_QUORUM_VOTERS: '1@kafka-1:9093,2@kafka-2:9093'
KAFKA_LISTENERS: 'INTERNAL://:9092,CONTROLLER://:9093,EXTERNAL://:9094'
KAFKA_ADVERTISED_LISTENERS: 'INTERNAL://kafka-1:9092,EXTERNAL://hl1.zil:9094'
KAFKA_INTER_BROKER_LISTENER_NAME: 'INTERNAL'
KAFKA_CONTROLLER_LISTENER_NAMES: 'CONTROLLER'
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: 'INTERNAL:PLAINTEXT,CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT'
KAFKA_LOG_DIRS: '/var/lib/kafka/data'
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 2
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 2
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
KAFKA_HEAP_OPTS: "-Xms1024m -Xmx1024m"
volumes:
- kafka-1-data:/var/lib/kafka/data
deploy:
mode: replicated
replicas: 1
placement:
constraints:
- node.hostname == hl1.zil
kafka-2:
image: confluentinc/cp-kafka:7.6.1
hostname: kafka-2
container_name: kafka-2
networks:
- kafka-net
ports:
- target: 9094
published: 9094
protocol: tcp
mode: host
environment:
KAFKA_NODE_ID: 2
KAFKA_PROCESS_ROLES: 'broker,controller'
CLUSTER_ID: '2Sy4fmVPRlyy6abZuLghoA'
KAFKA_CONTROLLER_QUORUM_VOTERS: '1@kafka-1:9093,2@kafka-2:9093'
KAFKA_LISTENERS: 'INTERNAL://:9092,CONTROLLER://:9093,EXTERNAL://:9094'
KAFKA_ADVERTISED_LISTENERS: 'INTERNAL://kafka-2:9092,EXTERNAL://hl2.zil:9094'
KAFKA_INTER_BROKER_LISTENER_NAME: 'INTERNAL'
KAFKA_CONTROLLER_LISTENER_NAMES: 'CONTROLLER'
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: 'INTERNAL:PLAINTEXT,CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT'
KAFKA_LOG_DIRS: '/var/lib/kafka/data'
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 2
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 2
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
KAFKA_HEAP_OPTS: "-Xms1024m -Xmx1024m"
volumes:
- kafka-2-data:/var/lib/kafka/data
deploy:
mode: replicated
replicas: 1
placement:
constraints:
- node.hostname == hl2.zil
kafka-ui:
image: provectuslabs/kafka-ui:latest
container_name: kafka-ui
networks:
- kafka-net
ports:
- "8080:8080"
environment:
KAFKA_CLUSTERS_0_NAME: 'Swarm KRaft Cluster'
KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: 'kafka-1:9092,kafka-2:9092'
DYNAMIC_CONFIG_ENABLED: 'true'
depends_on:
- kafka-1
- kafka-2
deploy:
mode: replicated
replicas: 1
placement:
constraints:
- node.role == manager
Обратите внимание, что для обоих брокеров используется один и тот же CLUSTER_ID, а сервисы закреплены за конкретными узлами Swarm с помощью placement.constraints.
5. Запуск и управление Kafka‑кластером в Swarm
На менеджере (hl2.zil) разворачиваем стек:
docker stack deploy -c docker-compose.yml kafka_stack
Проверяем список сервисов стека:
docker stack services kafka_stack
Смотрим логи одного из брокеров (например, kafka-1):
docker service logs kafka_stack_kafka-1 -f
Просматриваем список задач (контейнеров) со всеми нодами:
docker stack ps --no-trunc kafka_stack
При необходимости остановить и удалить стек:
docker stack rm kafka_stack
После успешного запуска вы должны видеть оба брокера в состоянии Running, а Kafka UI — в статусе Running на порту 8080.
6. Доступ к Kafka UI
По умолчанию Kafka UI публикуется на порту 8080 менеджера кластера.
6.1. Доступ с внутренней сети
Если вы находитесь в той же сети, что и hl2.zil, достаточно открыть в браузере:
http://hl2.zil:8080/
6.2. Доступ через SSH‑туннель
Если кластер доступен только через bastion‑хост, можно использовать SSH‑туннель. Например:
ssh -p 2302 -L 8080:hl2.zil:8080 hl@hlssh.zil.digital
После установления туннеля UI будет доступен локально по адресу:
http://localhost:8080/
7. Работа с топиками: сервер‑сервер
Для работы с топиками удобно использовать дистрибутив Apache Kafka, совпадающий по версии с вашим кластером.
7.1. Загрузка и распаковка Kafka CLI
На одном из серверов (например, hl2.zil) скачиваем подходящую версию с официального сайта:
wget https://archive.apache.org/dist/kafka/3.7.1/kafka_2.13-3.7.1.tgz
tar -xzf kafka_2.13-3.7.1.tgz
cd kafka_2.13-3.7.1
7.2. Создание топика
Создадим тестовый топик test-topic с одной партицией и фактором репликации 1 (для продакшена обычно увеличивают оба параметра):
bin/kafka-topics.sh --create \
--topic test-topic \
--partitions 1 \
--replication-factor 1 \
--bootstrap-server hl2.zil:9094
Проверим параметры топика:
bin/kafka-topics.sh --describe \
--topic test-topic \
--bootstrap-server hl2.zil:9094
7.3. Отправка сообщений (producer)
Запустим консольный продюсер:
bin/kafka-console-producer.sh \
--topic test-topic \
--bootstrap-server hl2.zil:9094
В открывшейся консоли введите несколько сообщений:
test message 1
test message 2
Завершите ввод Ctrl+D.
7.4. Чтение сообщений (consumer)
Откроем консольного консюмера и прочитаем сообщения с начала топика:
bin/kafka-console-consumer.sh \
--topic test-topic \
--bootstrap-server hl2.zil:9094 \
--from-beginning \
--group tunnel-test-$(date +%s)
Вы увидите ранее отправленные test message 1 и test message 2.
8. SSH‑туннелирование для удалённой работы с Kafka
Часто Kafka‑кластер развёрнут во внутренней сети, а администратору нужно работать с ним с локальной машины, используя те же имена хостов (hl1.zil, hl2.zil). Решение — дополнительные loopback‑интерфейсы и SSH‑туннели.
8.1. Создание дополнительных loopback‑адресов
Linux
# Создание
sudo ip addr add 127.0.0.2/8 dev lo
sudo ip addr add 127.0.0.3/8 dev lo
# Удаление
sudo ip addr del 127.0.0.2/8 dev lo
sudo ip addr del 127.0.0.3/8 dev lo
macOS
# Создание
sudo ifconfig lo0 alias 127.0.0.2 netmask 255.0.0.0
sudo ifconfig lo0 alias 127.0.0.3 netmask 255.0.0.0
# Удаление
sudo ifconfig lo0 -alias 127.0.0.2
sudo ifconfig lo0 -alias 127.0.0.3
8.2. Настройка /etc/hosts на локальной машине
Добавьте в локальный /etc/hosts следующие строки:
127.0.0.2 hl2.zil
127.0.0.3 hl1.zil
Так мы «привяжем» имена hl1.zil и hl2.zil к локальным loopback‑адресам.
8.3. SSH‑туннель для Kafka и Kafka UI
Теперь настроим SSH‑туннель через bastion‑хост, пробросив порты Kafka и Kafka UI:
ssh -p 2302 \
-L 8080:hl2.zil:8080 \
-L 127.0.0.2:9094:hl2.zil:9094 \
-L 127.0.0.3:9094:hl1.zil:9094 \
hl@hlssh.zil.digital
После установления туннеля любые обращения к hl1.zil:9094 и hl2.zil:9094 на вашей локальной машине будут прозрачно проксироваться к соответствующим брокерам внутри кластера.
8.4. Чтение сообщений по туннелю
Теперь вы можете использовать локально установленный Kafka CLI (например, тот же kafka_2.13-3.7.1) и подключаться к кластеру по тем же именам хостов:
bin/kafka-console-consumer.sh \
--topic test-topic \
--bootstrap-server hl2.zil:9094 \
--from-beginning \
--group tunnel-test-$(date +%s)
Сообщения будут читаться через SSH‑туннель, при этом для приложений и инструментов всё выглядит так, будто кластер доступен напрямую.
9. Итоги
В этой статье мы:
- подготовили две виртуальные машины на Debian 12 и настроили имена хостов
hl1.zilиhl2.zil; - развернули кластер Docker Swarm с выделением ролей manager/worker;
- настроили брандмауэр и необходимые порты для Swarm, Kafka и Kafka UI;
- подняли кластер Kafka в режиме KRaft в виде Docker‑стека с двумя брокерами и Kafka UI;
- научились управлять топиками и сообщениями через Kafka CLI;
- настроили SSH‑туннели и дополнительные loopback‑интерфейсы для удобной удалённой работы с кластером.
Такой подход хорошо подходит для лабораторной среды, пилотных инсталляций и внутренних сервисов. При переносе в продакшен имеет смысл добавить аутентификацию и шифрование (SASL, TLS), мониторинг (Prometheus, Grafana) и бэкапы конфигурации.