Как запустить Kafka вместе с ZooKeeper в Docker

Legacy-памятка

ZooKeeper mode нужен только для воспроизведения старого окружения. Apache Kafka 4.0 и новее работают только в KRaft mode. Для нового локального стенда следует запускать KRaft и не добавлять ZooKeeper.

Эта заметка сохраняет знакомый сценарий для Kafka 3.9.x — последней ветки, где ZooKeeper mode ещё поддерживался. Не подставляйте Kafka 4.x в этот compose.

Что хранит ZooKeeper

В старом режиме ZooKeeper участвует в хранении metadata кластера и выборе controller. Сообщения он не хранит: records находятся в log partitions брокеров.

В KRaft metadata записывается в собственный Raft quorum контроллеров Kafka. Это убирает отдельную систему координации.

Legacy compose

compose.yml:

services:
  zookeeper:
    image: zookeeper:3.9
    hostname: zookeeper
    environment:
      ZOO_4LW_COMMANDS_WHITELIST: ruok
    volumes:
      - zk-data:/data
 
  kafka:
    image: apache/kafka:3.9.0
    hostname: kafka
    depends_on:
      - zookeeper
    command:
      - /opt/kafka/bin/kafka-server-start.sh
      - /mnt/server.properties
    ports:
      - "9092:9092"
    volumes:
      - ./server.properties:/mnt/server.properties:ro
      - kafka-data:/var/lib/kafka/data
 
volumes:
  zk-data:
  kafka-data:

server.properties:

broker.id=1
zookeeper.connect=zookeeper:2181
 
listeners=INTERNAL://0.0.0.0:29092,EXTERNAL://0.0.0.0:9092
advertised.listeners=INTERNAL://kafka:29092,EXTERNAL://localhost:9092
listener.security.protocol.map=INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT
inter.broker.listener.name=INTERNAL
 
log.dirs=/var/lib/kafka/data
offsets.topic.replication.factor=1
transaction.state.log.replication.factor=1
transaction.state.log.min.isr=1

Два listener нужны потому, что адрес зависит от точки входа:

  • контейнеры обращаются к kafka:29092;
  • программы на host — к localhost:9092.

advertised.listeners сообщает адрес клиенту через metadata. Если там указан недоступный для клиента hostname, bootstrap соединение пройдёт, а дальнейшая работа сломается.

Запуск и проверка

docker compose up -d
docker compose ps
docker compose exec kafka \
  /opt/kafka/bin/kafka-topics.sh \
  --bootstrap-server kafka:29092 \
  --create --topic demo --partitions 1 --replication-factor 1
docker compose exec kafka \
  /opt/kafka/bin/kafka-topics.sh \
  --bootstrap-server kafka:29092 --describe --topic demo

Административные команды работают через --bootstrap-server. Старые примеры с аргументом --zookeeper не подходят современным CLI.

Остановить контейнеры с сохранением volumes:

docker compose down

Удаление volumes уничтожит локальные metadata и records, поэтому его выполняют только осознанно.

Современный KRaft

Для Kafka 4.x выбирают официальный image одной фиксированной версии и KRaft-конфигурацию. В single-node dev-режиме broker и controller могут совмещаться; production quorum проектируют отдельно. При миграции старого кластера нельзя просто убрать ZooKeeper из compose — metadata переносят по процедуре версии 3.x до обновления на 4.x.

На собеседовании

Зачем advertised.listeners? Broker возвращает клиенту адреса из metadata; они должны быть достижимы из сети клиента.

Можно ли Kafka 4.x запустить с ZooKeeper? Нет, ZooKeeper mode удалён; используется KRaft.

Связанные заметки: Kafka, Kafka Broker, Команды Kafka, Kafka Consumer.