- Home »

Настройка кластера Kafka из нескольких узлов с Kraft
Kafka с KRaft — это не просто модная тема. Это революция в архитектуре Apache Kafka, которая наконец-то избавляет нас от обязательной зависимости от ZooKeeper. Если вы раньше сталкивались с настройкой кластера Kafka, то знаете, какая это боль — поднимать сначала ZooKeeper, потом Kafka, следить за их синхронизацией, разбираться с квотами и partitions. KRaft (Kafka Raft) меняет правила игры, делая Kafka самодостаточным и значительно упрощая развёртывание.
В этой статье мы пройдём весь путь от теории к практике: разберём, как работает KRaft, настроим полноценный кластер из нескольких узлов, посмотрим на подводные камни и обсудим, когда это решение действительно стоит использовать. Если вы администратор, DevOps или просто интересуетесь современными подходами к streaming-платформам, то эта статья для вас.
Что такое KRaft и зачем оно нужно
KRaft (Kafka Raft) — это внутренний консенсус-протокол, который заменяет ZooKeeper в Apache Kafka. Основная идея в том, что метаданные кластера (информация о топиках, партициях, репликах) теперь хранятся в специальных внутренних топиках Kafka, а не в отдельном ZooKeeper-кластере.
Преимущества KRaft:
- Упрощение архитектуры — один сервис вместо двух
- Лучшая производительность — нет сетевых задержек между Kafka и ZooKeeper
- Более простое масштабирование
- Единая модель безопасности
- Меньше точек отказа
В KRaft-режиме узлы Kafka могут выполнять три роли:
- Controller — управляет метаданными кластера (аналог ZooKeeper)
- Broker — обрабатывает сообщения (традиционная роль)
- Combined — выполняет обе роли одновременно
Сравнение с классической архитектурой
Параметр | Kafka + ZooKeeper | Kafka + KRaft |
---|---|---|
Количество сервисов | 2 (Kafka + ZooKeeper) | 1 (только Kafka) |
Время запуска кластера | Медленнее (сначала ZK, потом Kafka) | Быстрее |
Максимальное количество партиций | ~200,000 | Миллионы |
Сложность настройки | Высокая | Средняя |
Производительность метаданных | Ограничена ZooKeeper | Выше |
Статус | Deprecated с версии 3.3 | Production-ready с версии 3.3 |
Подготовка окружения
Для демонстрации настроим кластер из 3 узлов. Каждый узел будет выполнять роль combined (controller + broker). Для продакшна лучше разделять роли, но для начала этого достаточно.
Потребуется минимум 3 VPS с характеристиками:
- 2 CPU
- 4 GB RAM
- 20 GB SSD
- Ubuntu 22.04 или CentOS 8+
Для высоконагруженных систем рекомендуется использовать выделенные серверы с NVMe дисками.
Установка и первичная настройка
Начнём с установки Java и Kafka на всех узлах:
# Обновляем систему
sudo apt update && sudo apt upgrade -y
# Устанавливаем Java 11
sudo apt install openjdk-11-jdk -y
# Проверяем установку
java -version
# Создаём пользователя для Kafka
sudo useradd -m -s /bin/bash kafka
sudo mkdir -p /opt/kafka
sudo chown kafka:kafka /opt/kafka
# Скачиваем и распаковываем Kafka
cd /tmp
wget https://downloads.apache.org/kafka/2.8.2/kafka_2.13-2.8.2.tgz
tar -xzf kafka_2.13-2.8.2.tgz
sudo mv kafka_2.13-2.8.2/* /opt/kafka/
sudo chown -R kafka:kafka /opt/kafka
# Создаём директории для данных
sudo mkdir -p /var/lib/kafka-logs
sudo mkdir -p /var/log/kafka
sudo chown -R kafka:kafka /var/lib/kafka-logs /var/log/kafka
Генерация cluster ID
В KRaft всем узлам нужен одинаковый cluster ID. Генерируем его на одном из узлов:
# Переходим в директорию Kafka
cd /opt/kafka
# Генерируем cluster ID
./bin/kafka-storage.sh random-uuid
Сохраните полученный ID — он понадобится для всех узлов. Например: xtzWWN4bTjitpL3kfd9s5g
Настройка конфигурации
Создаём конфигурационный файл для каждого узла. Основное различие — node.id и advertised.listeners.
Узел 1 (IP: 10.0.0.1):
# /opt/kafka/config/kraft/server.properties
# Уникальный ID узла
node.id=1
# Роль узла (controller, broker, или combined)
process.roles=controller,broker
# Список всех controller-узлов
controller.quorum.voters=1@10.0.0.1:9093,2@10.0.0.2:9093,3@10.0.0.3:9093
# Настройки листенеров
listeners=PLAINTEXT://10.0.0.1:9092,CONTROLLER://10.0.0.1:9093
advertised.listeners=PLAINTEXT://10.0.0.1:9092
controller.listener.names=CONTROLLER
# Межброкерная коммуникация
inter.broker.listener.name=PLAINTEXT
# Директория для логов
log.dirs=/var/lib/kafka-logs
# Настройки топиков по умолчанию
num.network.threads=8
num.io.threads=16
socket.send.buffer.bytes=102400
socket.receive.buffer.bytes=102400
socket.request.max.bytes=104857600
# Настройки партиций
num.partitions=3
default.replication.factor=3
min.insync.replicas=2
# Настройки retention
log.retention.hours=168
log.retention.bytes=1073741824
log.segment.bytes=1073741824
log.retention.check.interval.ms=300000
# Настройки для метаданных
metadata.log.dir=/var/lib/kafka-logs/__cluster_metadata
metadata.log.segment.bytes=1073741824
metadata.log.retention.bytes=1073741824
metadata.max.retention.ms=604800000
# Настройки JVM
heap.opts=-Xmx2G -Xms2G
Узел 2 (IP: 10.0.0.2):
# Аналогично узлу 1, но меняем:
node.id=2
listeners=PLAINTEXT://10.0.0.2:9092,CONTROLLER://10.0.0.2:9093
advertised.listeners=PLAINTEXT://10.0.0.2:9092
Узел 3 (IP: 10.0.0.3):
# Аналогично узлу 1, но меняем:
node.id=3
listeners=PLAINTEXT://10.0.0.3:9092,CONTROLLER://10.0.0.3:9093
advertised.listeners=PLAINTEXT://10.0.0.3:9092
Инициализация хранилища
На каждом узле необходимо инициализировать хранилище с тем же cluster ID:
# Переходим в директорию Kafka
cd /opt/kafka
# Инициализируем хранилище (замените на ваш cluster ID)
./bin/kafka-storage.sh format -t xtzWWN4bTjitpL3kfd9s5g -c config/kraft/server.properties
# Проверяем создание метаданных
ls -la /var/lib/kafka-logs/
Создание systemd сервиса
Создаём systemd unit для автоматического запуска:
# /etc/systemd/system/kafka.service
[Unit]
Description=Apache Kafka Server (KRaft)
Documentation=https://kafka.apache.org/documentation/
After=network.target
[Service]
Type=simple
User=kafka
Group=kafka
Environment=JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
ExecStart=/opt/kafka/bin/kafka-server-start.sh /opt/kafka/config/kraft/server.properties
ExecStop=/opt/kafka/bin/kafka-server-stop.sh
Restart=always
RestartSec=10
# Настройки для производительности
LimitNOFILE=65536
LimitNPROC=32768
[Install]
WantedBy=multi-user.target
Запускаем сервис:
# Перезагружаем systemd
sudo systemctl daemon-reload
# Включаем автозапуск
sudo systemctl enable kafka
# Запускаем сервис
sudo systemctl start kafka
# Проверяем статус
sudo systemctl status kafka
# Смотрим логи
sudo journalctl -u kafka -f
Проверка работоспособности кластера
После запуска всех узлов проверим состояние кластера:
# Проверка метаданных кластера
./bin/kafka-metadata-shell.sh --snapshot /var/lib/kafka-logs/__cluster_metadata/00000000000000000000.log
# Список узлов в кластере
./bin/kafka-broker-api-versions.sh --bootstrap-server localhost:9092
# Создание тестового топика
./bin/kafka-topics.sh --create --topic test-topic --bootstrap-server localhost:9092 --partitions 6 --replication-factor 3
# Просмотр информации о топике
./bin/kafka-topics.sh --describe --topic test-topic --bootstrap-server localhost:9092
# Тестовая отправка сообщения
echo "Hello KRaft!" | ./bin/kafka-console-producer.sh --topic test-topic --bootstrap-server localhost:9092
# Чтение сообщения
./bin/kafka-console-consumer.sh --topic test-topic --bootstrap-server localhost:9092 --from-beginning
Мониторинг и отладка
Полезные команды для мониторинга KRaft кластера:
# Просмотр состояния контроллеров
./bin/kafka-metadata-shell.sh --snapshot /var/lib/kafka-logs/__cluster_metadata/00000000000000000000.log --print-brokers
# Проверка лидера контроллера
./bin/kafka-log-dirs.sh --bootstrap-server localhost:9092 --describe
# Просмотр метрик через JMX
./bin/kafka-run-class.sh kafka.tools.JmxTool --object-name kafka.controller:type=KafkaController,name=ActiveControllerCount --jmx-url service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi
# Проверка репликации
./bin/kafka-topics.sh --bootstrap-server localhost:9092 --describe --under-replicated-partitions
# Проверка consumer groups
./bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --list
Производственные настройки
Для продакшна рекомендуется разделить роли controller и broker. Пример конфигурации:
Dedicated Controller (узлы 1-3):
# Controller-only конфигурация
process.roles=controller
controller.quorum.voters=1@controller-1:9093,2@controller-2:9093,3@controller-3:9093
listeners=CONTROLLER://controller-1:9093
# Отключаем broker-функции
num.network.threads=3
num.io.threads=4
Dedicated Broker (узлы 4-6):
# Broker-only конфигурация
process.roles=broker
controller.quorum.voters=1@controller-1:9093,2@controller-2:9093,3@controller-3:9093
listeners=PLAINTEXT://broker-1:9092
advertised.listeners=PLAINTEXT://broker-1:9092
# Увеличиваем ресурсы для broker
num.network.threads=16
num.io.threads=32
Настройка безопасности
Для продакшна обязательно настройте SSL/TLS и SASL аутентификацию:
# Генерация SSL сертификатов
keytool -keystore kafka.server.keystore.jks -alias localhost -validity 365 -genkey -keyalg RSA -storetype pkcs12
# Настройка в server.properties
listeners=PLAINTEXT://localhost:9092,SSL://localhost:9093,CONTROLLER://localhost:9094
advertised.listeners=PLAINTEXT://localhost:9092,SSL://localhost:9093
# SSL настройки
ssl.keystore.location=/opt/kafka/config/kafka.server.keystore.jks
ssl.keystore.password=password
ssl.key.password=password
ssl.truststore.location=/opt/kafka/config/kafka.server.truststore.jks
ssl.truststore.password=password
ssl.client.auth=required
ssl.enabled.protocols=TLSv1.2,TLSv1.3
# SASL настройки
sasl.enabled.mechanisms=PLAIN
sasl.mechanism.inter.broker.protocol=PLAIN
security.inter.broker.protocol=SASL_SSL
Масштабирование кластера
Добавление нового broker-узла в существующий кластер:
# 1. Настраиваем новый узел с уникальным node.id
node.id=4
process.roles=broker
controller.quorum.voters=1@controller-1:9093,2@controller-2:9093,3@controller-3:9093
# 2. Инициализируем хранилище с тем же cluster ID
./bin/kafka-storage.sh format -t xtzWWN4bTjitpL3kfd9s5g -c config/kraft/server.properties
# 3. Запускаем новый узел
sudo systemctl start kafka
# 4. Проверяем добавление узла
./bin/kafka-broker-api-versions.sh --bootstrap-server localhost:9092
# 5. Перебалансируем партиции
./bin/kafka-reassign-partitions.sh --bootstrap-server localhost:9092 --topics-to-move-json-file topics.json --broker-list "1,2,3,4" --generate
Сравнение с альтернативами
KRaft — не единственное решение для streaming платформ. Рассмотрим альтернативы:
Решение | Преимущества | Недостатки | Подходит для |
---|---|---|---|
Apache Kafka + KRaft | Зрелая экосистема, высокая производительность, отсутствие ZooKeeper | Сложность настройки, высокие требования к ресурсам | Крупные проекты, высокая нагрузка |
Apache Pulsar | Встроенная мультитенантность, лучшая geo-репликация | Менее зрелая экосистема, сложность архитектуры | Облачные решения, IoT |
Redis Streams | Простота, низкие задержки, встроенная персистентность | Ограниченная масштабируемость, нет компакции | Реалтайм приложения, небольшие проекты |
Apache EventStore | Event sourcing из коробки, простота разработки | Ограниченная экосистема, performance | CQRS/ES архитектуры |
Интеграция с инфраструктурой
Kafka KRaft отлично интегрируется с современными инструментами:
Kubernetes:
# Используем Strimzi Operator
kubectl apply -f 'https://strimzi.io/install/latest?namespace=kafka'
# Создаём кластер с KRaft
apiVersion: kafka.strimzi.io/v1beta2
kind: Kafka
metadata:
name: my-cluster
spec:
kafka:
version: 3.3.0
replicas: 3
listeners:
- name: plain
port: 9092
type: internal
tls: false
config:
offsets.topic.replication.factor: 3
transaction.state.log.replication.factor: 3
transaction.state.log.min.isr: 2
storage:
type: jbod
volumes:
- id: 0
type: persistent-claim
size: 100Gi
deleteClaim: false
Prometheus мониторинг:
# JMX экспортер для Kafka
java -javaagent:jmx_prometheus_javaagent-0.17.0.jar=8080:kafka.yml -jar kafka_2.13-3.3.0.jar
# Grafana дашборд для KRaft
# Импортируем дашборд ID: 721
# https://grafana.com/grafana/dashboards/721
Terraform для автоматизации:
# terraform/kafka-cluster.tf
resource "aws_instance" "kafka_nodes" {
count = 3
ami = "ami-0c02fb55956c7d316"
instance_type = "t3.medium"
vpc_security_group_ids = [aws_security_group.kafka.id]
user_data = templatefile("${path.module}/kafka-install.sh", {
node_id = count.index + 1
cluster_id = var.kafka_cluster_id
})
tags = {
Name = "kafka-node-${count.index + 1}"
Role = "kafka-kraft"
}
}
Автоматизация и скрипты
Полезные скрипты для автоматизации работы с KRaft:
#!/bin/bash
# kafka-health-check.sh
# Скрипт для проверки состояния кластера
KAFKA_HOME="/opt/kafka"
BOOTSTRAP_SERVER="localhost:9092"
# Проверка доступности брокеров
echo "Checking broker availability..."
$KAFKA_HOME/bin/kafka-broker-api-versions.sh --bootstrap-server $BOOTSTRAP_SERVER >/dev/null 2>&1
if [ $? -eq 0 ]; then
echo "✓ Brokers are accessible"
else
echo "✗ Brokers are not accessible"
exit 1
fi
# Проверка контроллеров
echo "Checking controller status..."
ACTIVE_CONTROLLER=$($KAFKA_HOME/bin/kafka-metadata-shell.sh --snapshot /var/lib/kafka-logs/__cluster_metadata/00000000000000000000.log --print-brokers 2>/dev/null | grep -c "ACTIVE")
if [ $ACTIVE_CONTROLLER -eq 1 ]; then
echo "✓ Controller is active"
else
echo "✗ Controller issues detected"
exit 1
fi
# Проверка under-replicated партиций
echo "Checking under-replicated partitions..."
UNDER_REPLICATED=$($KAFKA_HOME/bin/kafka-topics.sh --bootstrap-server $BOOTSTRAP_SERVER --describe --under-replicated-partitions 2>/dev/null | wc -l)
if [ $UNDER_REPLICATED -eq 0 ]; then
echo "✓ All partitions are properly replicated"
else
echo "⚠ Found $UNDER_REPLICATED under-replicated partitions"
fi
echo "Health check completed"
Скрипт для автоматического создания топиков:
#!/bin/bash
# create-topics.sh
# Массовое создание топиков
KAFKA_HOME="/opt/kafka"
BOOTSTRAP_SERVER="localhost:9092"
# Список топиков
declare -A TOPICS=(
["user-events"]="partitions=12,replication-factor=3"
["order-events"]="partitions=6,replication-factor=3"
["notification-events"]="partitions=3,replication-factor=3"
)
for topic in "${!TOPICS[@]}"; do
echo "Creating topic: $topic"
IFS=',' read -ra PARAMS <<< "${TOPICS[$topic]}"
PARTITIONS=""
REPLICATION=""
for param in "${PARAMS[@]}"; do
if [[ $param == partitions=* ]]; then
PARTITIONS="${param#*=}"
elif [[ $param == replication-factor=* ]]; then
REPLICATION="${param#*=}"
fi
done
$KAFKA_HOME/bin/kafka-topics.sh --create \
--topic "$topic" \
--bootstrap-server $BOOTSTRAP_SERVER \
--partitions $PARTITIONS \
--replication-factor $REPLICATION \
--if-not-exists
echo "✓ Topic $topic created"
done
Распространённые проблемы и их решения
Проблема: Узлы не могут подключиться к кластеру
# Проверка сетевой доступности
telnet controller-1 9093
telnet controller-2 9093
telnet controller-3 9093
# Проверка DNS разрешения
nslookup controller-1
nslookup controller-2
nslookup controller-3
# Проверка портов
netstat -tlnp | grep :909
Решение: Убедитесь, что все порты открыты и DNS настроен правильно.
Проблема: Метаданные не синхронизируются
# Проверка состояния метаданных
./bin/kafka-metadata-shell.sh --snapshot /var/lib/kafka-logs/__cluster_metadata/00000000000000000000.log
# Проверка логов
tail -f /var/log/kafka/server.log | grep -i "metadata"
Решение: Проверьте права доступа к директории метаданных и синхронизацию времени между узлами.
Проблема: Высокая нагрузка на диск
# Мониторинг дисковой активности
iotop -a -o -d 1
# Проверка размера логов
du -sh /var/lib/kafka-logs/*
# Настройка более агрессивного retention
log.retention.hours=24
log.retention.bytes=536870912
log.segment.bytes=536870912
Интересные возможности KRaft
KRaft открывает новые возможности, которые были недоступны с ZooKeeper:
- Миллионы партиций — теоретически KRaft может управлять миллионами партиций, что было невозможно с ZooKeeper
- Faster failover — переключение лидера контроллера происходит значительно быстрее
- Упрощённая репликация — метаданные реплицируются как обычные Kafka сообщения
- Лучшая observability — все операции с метаданными видны в обычных Kafka metrics
Нестандартные способы использования:
# Использование internal топиков для custom метаданных
./bin/kafka-topics.sh --create --topic __my_custom_metadata --bootstrap-server localhost:9092 --partitions 1 --replication-factor 3 --config cleanup.policy=compact
# Мониторинг изменений метаданных в реальном времени
./bin/kafka-console-consumer.sh --topic __cluster_metadata --bootstrap-server localhost:9092 --from-beginning --property print.key=true
Заключение и рекомендации
KRaft — это будущее Apache Kafka. Уже сейчас он показывает отличные результаты в продакшне и значительно упрощает операционную работу. Если вы планируете новый проект на Kafka или думаете о миграции, KRaft — однозначно правильный выбор.
Когда использовать KRaft:
- Новые проекты — без сомнений
- Высоконагруженные системы с большим количеством партиций
- Когда важна простота развёртывания и обслуживания
- Облачные deployment'ы
Когда пока повременить:
- Критичные legacy системы в продакшне
- Если используются инструменты, несовместимые с KRaft
- Команда не готова к изменениям в архитектуре
Основные рекомендации:
- Используйте dedicated роли (controller/broker) для продакшна
- Настройте мониторинг и alerting с первого дня
- Не экономьте на ресурсах — особенно на диске и сети
- Изучите официальную документацию: https://kafka.apache.org/documentation/#kraft
- Тестируйте на нагрузке, максимально приближенной к продакшну
KRaft — это не просто техническое улучшение, это смена парадигмы. Он делает Kafka более современным, производительным и простым в использовании. Если вы работаете с большими данными и событийными архитектурами, освоение KRaft — это инвестиция в будущее.
В этой статье собрана информация и материалы из различных интернет-источников. Мы признаем и ценим работу всех оригинальных авторов, издателей и веб-сайтов. Несмотря на то, что были приложены все усилия для надлежащего указания исходного материала, любая непреднамеренная оплошность или упущение не являются нарушением авторских прав. Все упомянутые товарные знаки, логотипы и изображения являются собственностью соответствующих владельцев. Если вы считаете, что какой-либо контент, использованный в этой статье, нарушает ваши авторские права, немедленно свяжитесь с нами для рассмотрения и принятия оперативных мер.
Данная статья предназначена исключительно для ознакомительных и образовательных целей и не ущемляет права правообладателей. Если какой-либо материал, защищенный авторским правом, был использован без должного упоминания или с нарушением законов об авторском праве, это непреднамеренно, и мы исправим это незамедлительно после уведомления. Обратите внимание, что переиздание, распространение или воспроизведение части или всего содержимого в любой форме запрещено без письменного разрешения автора и владельца веб-сайта. Для получения разрешений или дополнительных запросов, пожалуйста, свяжитесь с нами.