从 2009 年诞生到 2021 年,Kafka 的元数据一直由 ZooKeeper 管理。但 ZooKeeper 本身就是一个要单独运维的分布式系统——它成了 Kafka 高可用链条上最脆、也最难扩的一环。KRaft(Kafka Raft) 自 Kafka 3.3 起成为生产可用选项,把元数据管理内嵌进 Kafka 自身,用 Raft 共识协议实现控制器自协调。本文讲透 KRaft 的设计原理、对比 ZooKeeper 的取舍,并给出从 ZooKeeper 平滑迁移到 KRaft 的完整实战路径。
1. ZooKeeper 时代的架构与瓶颈
1.1 经典三角色架构
┌──────────────────────────────────────┐
│ ZooKeeper(元数据存储) │
│ Broker 注册 / Controller 选举 / ACL │
│ Topic 元数据 / ISR 变更 / 配额 │
└──────────────────────────────────────┘
↑ 读写 ↑ 上报
┌─────────┴─────────┐ ┌────────┴────────┐
│ Controller │ │ Brokers │
│ (从 ZK 选出) │◄─────│ 数据读写平面 │
└──────────────────┘ └─────────────────┘
- Broker 注册:每个 Broker 在 ZK 注册临时节点,异常即被踢出;
- Controller 选举:ZK 提供选举原语,选出一个 Controller 负责分区 Leader 分配;
- 元数据读写:Topic 创建、ISR 变更、ACL 全部落在 ZK。
1.2 瓶颈与痛点
| 痛点 | 表现 |
|---|---|
| 额外运维 | ZK 是独立系统,要单独扩缩、监控、备份 |
| 元数据单点延迟 | 百万级分区时,ZK 写入成为瓶颈 |
| 脑裂风险 | ZK 与 Kafka 两套共识,协调复杂 |
| 迁移/升级复杂度 | 升级 Kafka 常需同步升级 ZK |
| Controller 切换慢 | 控制器故障时,元数据同步耗时数秒级 |
核心矛盾:Kafka 的横向扩展能力越来越强(百万分区、十万 Broker),而 ZK 集群本身却很难平滑扩容,元数据平面成了「最先卡脖子」的地方。
一句话:ZooKeeper 时代 = 两套分布式系统拼一起——Kafka 数据平面 + ZK 元数据平面,运维复杂、扩展受限、脑裂风险高,正是 KRaft 要根治的。
2. KRaft 设计原理:控制器仲裁与元数据日志
2.1 一句话架构
KRaft 把 ZK 的职责搬进 Kafka 内部,由一个 控制器仲裁(Controller Quorum) 用 Raft 共识 直接管理元数据日志:
Controller Quorum(3 或 5 个节点):
一个 Active Controller(Leader)+ 若干 Follower
所有元数据变更 → 追加到「元数据日志」(内部 Topic __cluster_metadata)
→ 提交后同步给所有 Broker
Broker:
本地缓存元数据(不再依赖 ZK),通过内部协议与控制器同步
2.2 元数据日志(Metadata Log)
KRaft 的核心抽象是不可变元数据日志,所有集群状态变更(建 Topic、改配置、ISR 变化、ACL)都作为记录追加:
记录示例:
[RegisterBrokerRecord, brokerId=1, ...]
[CreateTopicRecord, topic=orders, partitions=6, ...]
[UpdatePartitionRecord, partition=orders-0, leader=3, isr=[3,1], ...]
Broker 启动 → 从日志回放 → 重建完整集群状态
快照(Snapshot) 机制定期压缩日志,控制回放成本。日志本身只存控制器仲裁(3/5 副本),Broker 通过 Metadata RPC 拉取变更。
2.3 选举与角色
- Active Controller(Leader):处理所有元数据读写、分区 Leader 分配、与 Broker 同步;
- Follower:只复制日志,Leader 故障时参与选举,替补上位;
- Broker 节点(也可兼任 Follower):数据读写平面,缓存元数据。
一句话:KRaft = 把「元数据」当成「日志」用 Raft 复制——控制器仲裁是唯一事实源,Broker 靠回放日志重建状态,一套共识搞定元数据,ZooKeeper 出局。
3. KRaft 与 ZooKeeper 全面对比
3.1 维度对比
| 维度 | ZooKeeper 模式 | KRaft 模式 |
|---|---|---|
| 元数据存储 | 独立 ZK 集群 | Kafka 内部 __cluster_metadata 日志 |
| 共识协议 | ZK 自带(ZAB) | Raft(KRaft) |
| 运维组件 | 多一套 ZK 集群 | 零外部依赖 |
| 分区规模 | 十万级开始吃力 | 设计目标百万级 |
| 控制器切换 | 秒级元数据同步 | 亚秒级回放恢复 |
| 数据平面 | Broker 写 ZK | Broker 本地缓存 + 内部协议 |
| 升级复杂度 | 需同步管 ZK | 单套系统升级 |
| 社区状态 | 将逐步淘汰 | 官方未来方向 |
3.2 关键取舍
- 一致性:KRaft 元数据一致性由 Raft 保证,比 ZK 的 ZAB 更贴合 Kafka 场景;
- 可靠性:控制器仲裁推荐 3 或 5 节点(需奇数),容忍 1/2 节点故障;
- 迁移成本:旧集群要迁移,但只要按步骤走,可平滑切换。
3.3 KRaft 的版本成熟度
| 版本 | 状态 |
|---|---|
| Kafka 2.8 | 预览(preview) |
| Kafka 3.3 | 生产可用(GA) |
| Kafka 3.5+ | 默认推荐,迁移工具成熟 |
| Kafka 4.0 | ZooKeeper 模式进入淘汰路径 |
一句话:KRaft 的收益是架构简化 + 扩展上限 + 运维减负;代价是需要一次迁移。对新建集群,KRaft 已是默认选择;对存量集群,按下面步骤平滑过渡。
4. 迁移前的评估与准备
4.1 前置条件
① Kafka 版本 ≥ 3.3(建议 ≥ 3.5,迁移工具更稳)
② 集群健康:所有分区 ISR 健康、无未完成重分配
③ 版本策略:若还跑旧客户端/工具,确认兼容 KRaft 版本
④ 备份:先备份 broker 配置、Topic 元数据、分区数据
4.2 环境评估清单
| 检查项 | 标准 |
|---|---|
| 所有 Broker 版本一致 | ✅ |
controller.quorum.voters 规划 | 3/5 节点,奇数 |
__cluster_metadata 分区副本 | 默认 3,生产建议 3 |
| 元数据日志目录权限 | Broker 本地可写 |
| 客户端最低版本 | 使用 bootstrap.servers 的现代客户端即可 |
4.3 生成控制器节点 ID
# 为每个参与仲裁的节点生成集群 ID
bin/kafka-storage.sh random-uuid
# 输出示例:sB8kXb4jTXy-2fNR2i1PWA
一句话:迁移前 = 版本对齐 + 集群健康 + 规划仲裁节点 + 备份——KRaft 迁移不可逆,前期评估比动手更重要。
5. 迁移步骤:混合模式到完整 KRaft
5.1 迁移三阶段
阶段一:混合模式(dual mode)
ZK 仍是数据源,KRaft 控制器仲裁已启动,两者并存
阶段二:Controller 切换
Kafka 元数据切换到 KRaft 控制器仲裁
阶段三:移除 ZooKeeper
确认稳定后,彻底停用 ZK
5.2 配置迁移(server.properties 关键项)
# 混合模式第一步:新增控制器仲裁配置
process.roles=controller,broker # 控制器兼任 broker(或分离)
node.id=1 # 全局唯一节点 ID
controller.quorum.voters=1@node1:9093,2@node2:9093,3@node3:9093
listeners=PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093
controller.listener.names=CONTROLLER
inter.broker.listener.name=PLAINTEXT
# 元数据日志目录初始化
bin/kafka-storage.sh format -t <cluster-id> -c server.properties
5.3 迁移流程(使用官方工具)
# 1) 在 ZK 模式集群上,先滚动升级到支持 KRaft 的版本
bin/kafka-configs.sh --bootstrap-server localhost:9092 \
--alter --entity-type broker --entity-name 1 --add-config \
"process.roles=controller,broker"
# 2) 每个节点格式化元数据日志并重启
bin/kafka-storage.sh format -t <cluster-id> -c server.properties
# 3) 执行「迁移到 KRaft」命令,一次性搬移元数据
bin/kafka-storage.sh migrate \
--release-version 3.7 \
--cluster-id <cluster-id> \
--config server.properties
# 4) 验证所有 broker 上报到 KRaft 仲裁后,停掉 ZK
bin/kafka-storage.sh controller.shutdown
注意:混合模式下 ZK 与 KRaft 仲裁同时存在,迁移工具负责把 ZK 的元数据「搬移」到 KRaft 日志,搬移完成后才能安全移除 ZK。
5.4 验证迁移成功
# 查看元数据日志提交状态
bin/kafka-metadata-quorum.sh --bootstrap-server localhost:9092 \
describe --status
# 确认无 ZooKeeper 相关进程/配置残留
# 所有 topic 的 leader/ISR 正常
bin/kafka-topics.sh --describe --bootstrap-server localhost:9092
一句话:迁移路径 = 混合模式并存 → 官方 migrate 工具搬移元数据 → 验证 → 停 ZK——关键是先升级再迁移、搬移后验证再移除,任何一步都不能跳。
6. KRaft 集群运维与故障处理
6.1 控制器仲裁运维
- 仲裁节点必须奇数(3 容忍 1 故障,5 容忍 2);
- 仲裁节点不跑业务负载(或至少不承载关键分区 Leader),保证元数据写入稳定;
- 监控仲裁的 日志 Lag 与同步。
6.2 常见运维命令
# 查看控制器仲裁状态
bin/kafka-metadata-quorum.sh --bootstrap-server localhost:9092 describe --status
# 查看元数据日志大小与快照
bin/kafka-log-dirs.sh --bootstrap-server localhost:9092 \
--describe --topic-metadata
# 查看 broker 与控制器间的同步
bin/kafka-broker-api-versions.sh --bootstrap-server localhost:9092
6.3 故障场景
| 场景 | 处理 |
|---|---|
| Active Controller 故障 | Follower 选举上位,秒级恢复 |
| 仲裁节点不足半数 | 元数据写入停止,需恢复节点 |
| Broker 与控制器失联 | Broker 只读,恢复网络后同步 |
| 元数据日志损坏 | 从快照 + 日志恢复(需备份) |
6.4 监控指标
| 指标 | 关注点 |
|---|---|
| 元数据日志提交延迟 | 控制器负载 |
| 仲裁选举次数 | 稳定性告警 |
| 元数据快照大小/频率 | 回放成本 |
| Broker 元数据同步 Lag | 数据面延迟 |
一句话:KRaft 运维 = 奇数仲裁 + 控制器专用 + 监控同步 Lag——控制器是「新的大脑」,保护它、监控它、演练故障切换,和当年对待 ZK 一样认真。
7. 常见坑与最佳实践
7.1 常见坑
| 坑 | 现象 | 对策 |
|---|---|---|
| 仲裁节点偶数 | 无法达成多数,写入卡死 | 必须奇数(3/5) |
| 迁移工具版本过低 | 元数据搬移失败 | 用与 broker 版本匹配的工具 |
| 跳过验证直接停 ZK | 数据面元数据缺失 | 先验证再移除 |
| 控制器跑业务分区 | 元数据延迟升高 | 仲裁节点专职/轻载 |
| 不设快照 | 元数据日志无限膨胀 | 配置自动快照 |
| 混用新旧配置 | 节点起不来 | 配置逐项对齐 |
7.2 最佳实践清单
- 新建集群直接用 KRaft,不给自己留历史包袱;
- 控制器仲裁 3/5 奇数节点,专职或轻载;
- 迁移按三阶段走:混合 → 切换 → 移除;
- 迁移后做完整验证(Topic、ISR、生产消费、权限);
- 监控元数据日志与选举,纳入核心告警;
- 元数据日志纳入备份/灾备范围。
8. 总结
本文完整梳理了 Kafka 从 ZooKeeper 到 KRaft 的演进:
| 环节 | 要点 |
|---|---|
| 痛点 | ZK 运维重、扩展难、脑裂风险 |
| 原理 | Raft 共识 + 元数据日志 + 快照 |
| 对比 | 简化架构、百万分区、零外部依赖 |
| 前置 | 版本 ≥3.3、集群健康、规划仲裁 |
| 迁移 | 混合模式 → 官方迁移 → 验证 → 移除 |
| 运维 | 奇数仲裁、专职控制器、监控 Lag |
一句话记住:KRaft 把 Kafka 的元数据从外部 ZK 搬进内部 Raft 日志——一套系统、一套共识、一个运维面,换来百万级分区能力与更简单的架构。对存量集群,先升级再迁移、搬移后验证、稳定后移除,是唯一安全的路径。KRaft 不是可选项,而是 Kafka 的下一个默认。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。