Home » Настройка кластера Kafka из нескольких узлов с Kraft
Настройка кластера Kafka из нескольких узлов с Kraft

Настройка кластера 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 — это инвестиция в будущее.


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

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

Leave a reply

Your email address will not be published. Required fields are marked