Skip to content

KRaft 模式集群:不再需要 Zookeeper ​

KRaft 把元数据管理收进 Kafka 自己(基于 Raft 的 controller quorum)。Kafka 4.0 起只剩这一种模式,新集群没有理由再用 ZK。

场景 ​

  • 新建集群(首选)。
  • 想把三节点 ZK + 三节点 broker 精简成三节点。
  • 老集群在 3.6+,评估原地迁移。

相比 ZK 模式的好处:

维度ZK 模式KRaft
组件ZK 集群 + Kafka只有 Kafka
元数据规模上限分区数 ~20 万时 ZK 成为瓶颈百万级分区
controller 切换ZK 选举,秒级到几十秒Raft,通常 < 1s
部署复杂度两套配置、两套端口一套
监控ZK + Kafka只有 Kafka

一、拓扑 ​

三台机器,每台同时是 broker 和 controller(combined 模式),小集群最省机器。

机器node.idbroker 端口controller 端口
10.0.0.31190929093
10.0.0.32290929093
10.0.0.33390929093

规模大(broker > 10 或分区 > 10 万)时改成 isolated 模式:3 台只做 controller,其余只做 broker。controller 不处理客户端请求,抖动更小。

二、配置 ​

ini
# /opt/kafka/config/kraft/server.properties  (10.0.0.31)
process.roles=broker,controller
node.id=1

# 三台的投票成员列表,写成 <node.id>@<host>:<controller端口>
controller.quorum.voters=1@10.0.0.31:9093,2@10.0.0.32:9093,3@10.0.0.33:9093

# 两个监听器:客户端用 PLAINTEXT,controller 用 CONTROLLER
listeners=PLAINTEXT://10.0.0.31:9092,CONTROLLER://10.0.0.31:9093
advertised.listeners=PLAINTEXT://10.0.0.31:9092
inter.broker.listener.name=PLAINTEXT
controller.listener.names=CONTROLLER
listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT

log.dirs=/data/kafka/logs
num.partitions=3
default.replication.factor=3
min.insync.replicas=2
offsets.topic.replication.factor=3
transaction.state.log.replication.factor=3
transaction.state.log.min.isr=2
auto.create.topics.enable=false
log.retention.hours=168

三处必须逐台改:node.id、listeners、advertised.listeners。

配置说明
process.rolesbroker,controller 混部;纯 broker 写 broker,纯 controller 写 controller
node.id集群内唯一,和 ZK 模式的 broker.id 是两套东西,不要混
controller.quorum.voters必须包含全部 controller;漏一个会导致 quorum 永远选不出 leader
controller.listener.names名字要和 listener.security.protocol.map 里的一致
inter.broker.listener.namebroker 之间通信走哪个监听器

CONTROLLER 端口绝不能暴露公网

controller 监听器没有认证,能连上就等于能改整个集群的元数据。安全组只放通内网。也不要把 9093 配成 0.0.0.0 后直接 advertised.listeners 里公开。

三、格式化存储目录 ​

只在第一次部署时做,且每台都要做一次。

bash
export KAFKA_HOME=/opt/kafka

# 1. 生成集群 ID(只生成一次,三台共用同一个)
KAFKA_CLUSTER_ID=$($KAFKA_HOME/bin/kafka-storage.sh random-uuid)
echo $KAFKA_CLUSTER_ID
text
hB1q2wErTz6Y8uI0oP3aSg
bash
# 2. 每台执行格式化
$KAFKA_HOME/bin/kafka-storage.sh format -t $KAFKA_CLUSTER_ID -c $KAFKA_HOME/config/kraft/server.properties
text
Formatting /data/kafka/logs with metadata.version 3.9-IV0.

解释:

  • metadata.version 是元数据版本,决定能用哪些特性。生产要显式指定成目标版本(如 3.9-IV0),不指定会用 jar 里最高的,之后降版本会起不来。
  • 三台的 KAFKA_CLUSTER_ID 必须一致,否则节点之间拒绝组成集群,日志报 Cluster ID mismatch。
  • 格式化会写 meta.properties 到 log.dirs。目录非空时格式化会失败,这是保护机制。

想锁定元数据版本:

bash
$KAFKA_HOME/bin/kafka-storage.sh format -t $KAFKA_CLUSTER_ID \
  -c $KAFKA_HOME/config/kraft/server.properties \
  --release-version 3.9

四、启动 ​

bash
chown -R kafka:kafka /data/kafka
systemctl enable --now kafka
journalctl -u kafka -n 40 --no-pager

systemd 与 ZK 模式基本一致,只是 ExecStart 换成 KRaft 配置:

ini
[Service]
Type=simple
User=kafka
Group=kafka
Environment="JAVA_HOME=/usr/local/jdk-17"
ExecStart=/opt/kafka/bin/kafka-server-start.sh /opt/kafka/config/kraft/server.properties
ExecStop=/opt/kafka/bin/kafka-server-stop.sh
Restart=on-failure
RestartSec=5
LimitNOFILE=65535

正常启动日志:

text
[2026-10-27 19:40:11,204] INFO [RaftManager nodeId=1] Completed transition to Leader with epoch 1 (kafka.raft.KafkaRaftManager)
[2026-10-27 19:40:11,662] INFO [KafkaRaftServer nodeId=1] Kafka Server started (kafka.server.KafkaRaftServer)

Completed transition to Leader 只会在 leader 那台出现,其余两台是 Completed transition to Follower。

五、验证 quorum ​

bash
/opt/kafka/bin/kafka-metadata-quorum.sh --bootstrap-server 10.0.0.31:9092 describe --status
text
ClusterId:              hB1q2wErTz6Y8uI0oP3aSg
LeaderId:               1
LeaderEpoch:            3
HighWatermark:          271
MaxFollowerLag:         0
MaxFollowerLagTimeMs:   0
CurrentVoters:          [1,2,3]
CurrentObservers:       []
bash
/opt/kafka/bin/kafka-metadata-quorum.sh --bootstrap-server 10.0.0.31:9092 describe --replication
text
NodeId	Host	                LogEndOffset	Lag	LastFetchTimestamp	LastCaughtUpTimestamp	Status
1	    10.0.0.31:9093	    271	        0	    1761550211884	    1761550211884	        Leader
2	    10.0.0.32:9093	    271	        0	    1761550211902	    1761550211902	        Follower
3	    10.0.0.33:9093	    271	        0	    1761550211891	    1761550211891	        Follower

读法:

  • CurrentVoters [1,2,3] 三票齐全。
  • 两个 follower 的 Lag 都是 0,Status 不是 Leader 以外的问题状态。
  • 如果只有一个节点,CurrentVoters 只有它自己,说明另外两台没连上——检查 controller.quorum.voters 和 9093 端口。

再确认 broker 都注册了:

bash
/opt/kafka/bin/kafka-broker-api-versions.sh --bootstrap-server 10.0.0.31:9092 | grep -c 'id:'
text
3

六、冒烟测试 ​

与 ZK 模式完全一致:

bash
/opt/kafka/bin/kafka-topics.sh --bootstrap-server 10.0.0.31:9092 \
  --create --topic smoke --partitions 3 --replication-factor 3

/opt/kafka/bin/kafka-topics.sh --bootstrap-server 10.0.0.31:9092 --describe --topic smoke
text
Topic: smoke	TopicId: 3fT8mQ2nR5vW7xY9zA1bCy	PartitionCount: 3	ReplicationFactor: 3	Configs:
	Topic: smoke	Partition: 0	Leader: 1	Replicas: 1,2,3	Isr: 1,2,3
	Topic: smoke	Partition: 1	Leader: 2	Replicas: 2,3,1	Isr: 2,3,1
	Topic: smoke	Partition: 2	Leader: 3	Replicas: 3,1,2	Isr: 3,1,2

RF=3 + ISR=3 才算真三副本

Replicas 是"分配了哪些副本",Isr 是"当前同步的副本"。只有 Isr 长度为 3 时才有三份实时数据。看到 Isr: 1,2 就该查第三台。

七、controller 挂了会怎样 ​

bash
systemctl stop kafka     # 在 leader 那台(node.id=1)
bash
/opt/kafka/bin/kafka-metadata-quorum.sh --bootstrap-server 10.0.0.32:9092 describe --status
text
ClusterId:              hB1q2wErTz6Y8uI0oP3aSg
LeaderId:               2
LeaderEpoch:            4
CurrentVoters:          [1,2,3]

LeaderId 变成 2,LeaderEpoch 加 1,切换通常在 1 秒内完成。broker 侧不受影响(数据面照常读写)。

注意:combined 模式下,挂掉的这台同时少了一个 broker,它上面的 leader 分区要在剩余两台之间重新选举,这段时间该分区不可用几百毫秒到几秒。

八、ZK 迁 KRaft ​

3.6 起提供原地迁移(preview),3.9 已可用,但单向不可回退。

大致过程(每一阶段都要停业务验证):

  1. 给所有 broker 配好 controller 相关参数,并打开 zookeeper.metadata.migration.enable=true,重启。
  2. 启动 controller quorum,等元数据从 ZK 复制到 KRaft。
  3. 切换 process.roles 去掉 zookeeper.connect,再滚动重启。
  4. 确认 kafka-metadata-quorum.sh describe --status 正常后,下线 ZK。

迁移期间不要做任何 topic 变更

扩分区、改副本、删 topic 都可能触发元数据不一致。迁移窗口内只允许读写数据。

大多数团队更稳妥的做法:新建 KRaft 集群 → 用 MirrorMaker 2 双向同步 → 业务逐个切 bootstrap 地址 → 下线老集群。虽然多花机器,但随时可回退。

迁移步骤随版本变化

zookeeper.metadata.migration.enable 的具体行为和配套命令在 3.6 / 3.7 / 3.9 之间有调整。执行前以当前版本的官方 upgrade 文档为准,本文不给出跨版本通用的完整命令序列(待验证)。

九、踩过的坑 ​

现象根因处置
启动报 Cluster ID mismatch三台 KAFKA_CLUSTER_ID 不同用同一个 ID 重新格式化(会清空数据)
格式化报 Log directory ... is not empty目录里有旧数据确认可清空后删掉 meta.properties 和日志
CurrentVoters 只有自己controller.quorum.voters 少写/写错 IP三台配置必须完全一致
一直选不出 leader9093 不通,或投票成员不足半数放通端口,确认 3 台中至少 2 台在线
客户端能连但生产失败inter.broker.listener.name 没配补上,指向 PLAINTEXT
Kafka 4.x 用 Java 11 起不来4.0 要求 Java 17+换 JDK 17
升级 jar 后起不来,报元数据版本不兼容格式化时用了过高的 metadata.version升级时用 --release-version 指定,且不可随意降
想回退到 ZK 模式KRaft 单向只能重建集群 + 数据迁移