Kafka KRaft Mode: Single-Node Setup and Operations - 夜莺博客

Kafka KRaft Mode: Single-Node Setup and Operations

KRaft replaced ZooKeeper as Kafka's metadata layer, and with it comes a simpler deployment: one process, one configuration file, no second distributed system to keep alive. For a lab, a staging environment or a small internal log pipeline, a single-node KRaft broker is entirely reasonable. This guide covers the configuration, the topics and retention commands you will actually use, and the operational checks worth automating.

Architecture in one paragraph

In KRaft mode, metadata (brokers, topics, partitions, leaders) lives in an internal Raft quorum instead of ZooKeeper. A node can act as broker, controller or both. A controller-only node manages metadata and does not serve clients; a combined node does both, which is what a small deployment uses. Production clusters typically run three dedicated controllers plus N brokers.

Single-node configuration

# config/kraft/server.properties
process.roles=broker,controller
node.id=1
controller.quorum.voters=1@localhost:9093

listeners=PLAINTEXT://:9092,CONTROLLER://:9093
advertised.listeners=PLAINTEXT://kafka1.example.com:9092
controller.listener.names=CONTROLLER
listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
inter.broker.listener.name=PLAINTEXT

log.dirs=/var/lib/kafka/data
num.partitions=3
default.replication.factor=1
offsets.topic.replication.factor=1
transaction.state.log.replication.factor=1
transaction.state.log.min.isr=1

The replication-factor trio matters on a single node: the defaults assume three brokers and will refuse to create internal topics. Setting them to 1 keeps a one-node cluster working, at the cost of redundancy — document that trade-off so nobody later assumes the data is protected.

Format and start

# Generate a cluster ID once, then format the storage directory
KAFKA_CLUSTER_ID="$(bin/kafka-storage.sh random-uuid)"
bin/kafka-storage.sh format -t $KAFKA_CLUSTER_ID -c config/kraft/server.properties

bin/kafka-server-start.sh -daemon config/kraft/server.properties
tail -f logs/server.log

Formatting wipes existing data, so it is a first-install step only. With systemd, wrap it as a service and add Restart=always so a broker restart after an OOM does not need a human.

Topics, partitions and retention

bin/kafka-topics.sh --create --topic app-logs \
  --bootstrap-server localhost:9092 \
  --partitions 6 --replication-factor 1 \
  --config retention.ms=604800000 \
  --config cleanup.policy=delete

bin/kafka-topics.sh --list --bootstrap-server localhost:9092
bin/kafka-topics.sh --describe --topic app-logs --bootstrap-server localhost:9092

# Change retention after the fact
bin/kafka-configs.sh --alter --bootstrap-server localhost:9092 \
  --entity-type topics --entity-name app-logs \
  --add-config retention.ms=1209600000

# Compaction instead of time-based deletion (state/log-compacted topics)
bin/kafka-configs.sh --alter --bootstrap-server localhost:9092 \
  --entity-type topics --entity-name app-state \
  --add-config cleanup.policy=compact,min.cleanable.dirty.ratio=0.5

Retention is per topic by design: one cluster can hold time-expired log topics and compacted state topics side by side. Partition count is the harder decision — you can add partitions but never remove them without recreating the topic, and consumer-side ordering guarantees are per partition.

Produce, consume and verify

# Write a few messages
bin/kafka-console-producer.sh --bootstrap-server localhost:9092 --topic app-logs
> hello kafka
> second message

# Read from the beginning
bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 \
  --topic app-logs --from-beginning --max-messages 2 \
  --property print.timestamp=true --property print.partition=true

# Consumer group position and lag
bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --list
bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
  --describe --group log-shipper

The LAG column in the group description is the single most useful health indicator: a group whose lag grows steadily has a consumer that cannot keep up, not a broker problem.

Day-two operations

# Broker and controller state
bin/kafka-broker-api-versions.sh --bootstrap-server localhost:9092 | head
bin/kafka-metadata-quorum.sh --bootstrap-server localhost:9092 describe --status

# Disk usage per log directory - Kafka keeps segments until retention expires
du -sh /var/lib/kafka/data/* | sort -h | tail

# Topic-level disk accounting
bin/kafka-log-dirs.sh --bootstrap-server localhost:9092 --describe --topic-list app-logs
  1. Disk is the real constraint. Retention plus message rate equals growth; alert on filesystem usage per log.dir, not on cluster-wide metrics only.
  2. Watch under-replicated and offline partitions — on a single node, an offline partition means data loss is already in play.
  3. Do not enable auto topic creation in production. Set auto.create.topics.enable=false and create topics deliberately, so retention and partition counts are intentional.
  4. Keep one configuration source. Whether that is a Helm chart, Ansible, or an IaC module, do not hand-edit server.properties on a running estate.
  5. Size the heap and page cache knowingly. Kafka leans on the OS page cache; leaving it the default and raising the JVM heap to "be safe" is the classic misconfiguration.

KRaft single-node is the right starting point for internal pipelines, and the operational muscle you build — topics, retention, lag — transfers unchanged when the cluster grows to three controllers and many brokers.

Related Reading on This Site

原文链接:https://kafka.apache.org/documentation/ (Apache Kafka Documentation)