恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Kafka Raft (KRaft) 深度解析:从 ZooKeeper 迁移到元数据自管理架构
首页
资讯中心
/
Kafka Raft (KRaft) 深度解析:从 ZooKeeper 迁移到元数据自管理架构
Kafka Raft (KRaft) 深度解析:从 ZooKeeper 迁移到元数据自管理架构
发布时间:2026/10/22 3:40:05
1. 为什么值得花时间啃透 Kafka Raft如果你正在维护一套依赖 ZooKeeper 的 Kafka 集群或者正准备把老集群升级到新版本那Kafka Raft业内也常叫它 KRaft这个词你一定绕不过去。我从两年前开始跟进这个方向先后在测试环境和几个中小规模生产环境里做过迁移验证踩过的坑不算少。这篇文章不是官方文档的翻译而是把我自己整理的知识脉络、实操记录和排查经验一次性摊开讲清楚。先说清楚它到底是什么。Kafka Raft 是 Kafka 用来替代 ZooKeeper 的一套元数据管理共识机制。在旧架构里Kafka 只负责消息的存储和收发而集群有哪些 Broker、每个 Topic 有多少分区、分区的 Leader 是谁、副本怎么分配、ACL 配置是什么这些全部存在 ZooKeeper 里。新架构把这一整套元数据管理收回到 Kafka 自己内部用 Raft 共识算法在 Controller 节点之间同步元数据。换句话说Kafka 从半自管理变成了完全自管理。它能解决什么问题最直接的三个部署复杂度下降、元数据操作延迟降低、集群规模上限提高。以前你要维护两套分布式系统ZooKeeper 有它自己的运维逻辑、监控指标、调优参数现在只剩一套。元数据变更不再需要跨系统同步Controller 故障切换从分钟级压缩到秒级。分区数量从几万级别可以往上探到百万级别。适合谁来读如果你是 Kafka 运维、后端开发、或者正在做消息中间件选型这篇内容都能用得上。我会从架构原理讲到参数配置再到实际迁移步骤和故障排查尽量让刚接触的人能看懂也让有经验的人能拿到可以直接抄的配置。2. 架构拆解KRaft 到底把 ZooKeeper 的活怎么接了2.1 旧架构的痛点到底在哪要理解 KRaft 的价值得先看清楚 ZooKeeper 架构的瓶颈。在旧模式里Kafka Controller 是集群的大脑但它这个大脑是单点的。Controller 挂了之后需要从 ZooKeeper 里重新选举一个 Controller这个过程涉及 ZooKeeper 的 Watcher 通知、多个 Broker 竞争注册实测下来通常要几秒到几十秒。集群规模越大、分区越多这个切换时间越长因为新 Controller 要把全量元数据从 ZooKeeper 拉回来重建内存状态。另一个问题是元数据写入路径太长。创建一个 Topic请求先到 ControllerController 写到 ZooKeeperZooKeeper 再通知所有 Broker 去拉取更新。这条链路上任何一环抖动都会让元数据操作变慢。我见过一个集群分区数到二十万之后创建一个 Topic 要等十几秒运维同学每次操作都得屏住呼吸。还有一个隐性成本是双系统运维。ZooKeeper 有自己的选举机制、自己的磁盘要求、自己的监控体系。你得同时盯着两套系统的健康状态排查问题时还要判断到底是 Kafka 的问题还是 ZooKeeper 的问题。这种心智负担在故障时刻特别要命。2.2 Raft 共识在 Kafka 里怎么落地KRaft 的核心思路是把元数据当成一个有序的日志来管理。所有元数据变更——建 Topic、改分区、Broker 上下线——都作为一条条记录追加到这个日志里。Raft 算法保证这个日志在多个 Controller 节点之间保持一致只要多数节点确认写入这条记录就算提交成功。这里有几个关键角色需要分清Controller 节点参与 Raft 选举和日志复制的节点负责处理元数据变更。它们同时也是 Broker可以收发消息。Leader Controller当前处理元数据写入的主节点所有变更请求都先到它这里。Follower Controller从 Leader 同步日志Leader 挂了之后参与新 Leader 选举。Broker 节点只负责消息存储和收发不参与元数据共识但会从 Controller 拉取元数据快照。Active Controller在旧架构里叫 Controller在 KRaft 里就是 Raft Leader概念上对应。元数据日志的复制用的是 Raft 的标准流程Leader 收到变更请求追加到本地日志然后并行发给所有 Follower。多数 Follower 确认写入后Leader 提交这条记录并应用到状态机同时通知 Broker 更新。整个过程在局域网内通常几毫秒到几十毫秒完成。注意KRaft 里的 Controller 数量必须是奇数推荐 3 个或 5 个。3 个能容忍 1 个故障5 个能容忍 2 个。不要用偶数因为偶数在脑裂场景下无法形成多数派反而降低可用性。2.3 元数据快照与日志压缩机制Raft 日志不能无限增长否则重启时回放会非常慢。KRaft 用了快照加日志压缩的机制。当日志积累到一定条数由metadata.log.max.record.bytes.between.snapshots控制默认 20MB 左右Controller 会把当前全量元数据打成一个快照文件然后截断旧的日志段。快照文件存在 Controller 节点的metadata.log.dir目录下命名类似snapshot_offset。重启时Controller 先加载最新的快照再回放快照之后的日志这样恢复速度就快很多。我实测过一个有五十万分区的集群全量日志回放要几分钟但加载快照加少量日志回放只要几秒。这里有个容易忽略的点快照生成是异步的不会阻塞正常的元数据写入。但如果快照生成太频繁会占用磁盘 IO。默认配置下一般不用调除非你的元数据变更极其频繁可以适当调大metadata.log.max.record.bytes.between.snapshots。3. 核心配置与实操要点从零搭一套 KRaft 集群3.1 关键配置项逐个拆解搭 KRaft 集群配置文件里最重要的几个参数必须搞清楚。下面这张表是我整理的核心配置对照配置项作用推荐值说明process.roles节点角色broker或controller或broker,controller生产环境建议分离部署node.id节点唯一 ID整数集群内唯一不要重复否则启动报错controller.quorum.votersController 投票成员1host1:9093,2host2:9093,3host3:9093格式为 idhost:portcontroller.listener.namesController 监听器名称CONTROLLER与 listeners 配置对应listeners监听地址PLAINTEXT://:9092,CONTROLLER://:9093注意协议前缀log.dirs数据目录独立磁盘路径元数据日志和消息数据分开更好metadata.log.dir元数据日志目录独立路径不配则默认在 log.dirs 下process.roles这个参数决定了节点的身份。如果配成broker,controller这个节点既参与元数据共识又存消息适合测试环境或者小集群。生产环境我强烈建议分离部署Controller 节点专门做共识Broker 节点专门存消息互不干扰。原因很简单Controller 对延迟敏感Broker 对吞吐和磁盘 IO 敏感混在一起容易互相拖累。controller.quorum.voters是 KRaft 的通讯录所有 Controller 节点都必须配置完全相同的列表。格式是节点ID主机名:端口多个用逗号分隔。这里的主机名必须能被其他节点解析到建议用稳定的内网域名或者固定 IP不要用会变的容器主机名。3.2 单节点快速验证环境搭建想快速体验 KRaft可以先在单机上跑一个合并角色的节点。步骤如下第一步生成集群 ID。KRaft 需要一个全局唯一的集群标识bin/kafka-storage.sh random-uuid这条命令会输出一个类似xtzWWN4bTjitpL3kfd9s5g的字符串记下来。第二步格式化存储目录。这一步会初始化元数据日志bin/kafka-storage.sh format -t xtzWWN4bTjitpL3kfd9s5g -c config/kraft/server.properties-t后面跟集群 ID-c指定配置文件。格式化只需要做一次重复执行会报错。第三步启动节点bin/kafka-server-start.sh config/kraft/server.properties启动日志里会看到KRaft mode相关的信息以及 Controller 选举完成的提示。如果看到Leader election completed之类的字样说明启动成功。第四步验证。创建一个测试 Topicbin/kafka-topics.sh --create --topic test-kraft --partitions 3 --replication-factor 1 --bootstrap-server localhost:9092然后列出 Topic 确认bin/kafka-topics.sh --list --bootstrap-server localhost:9092能列出test-kraft就说明元数据管理正常工作。提示单节点环境只适合功能验证没有任何容错能力。节点一挂整个集群就不可用。生产环境至少 3 个 Controller 加 3 个 Broker。3.3 三 Controller 加三 Broker 的生产级配置生产环境我推荐 3 个 Controller 节点加 3 个 Broker 节点的最小高可用架构。Controller 节点配置如下process.rolescontroller node.id1 controller.quorum.voters1ctrl1:9093,2ctrl2:9093,3ctrl3:9093 controller.listener.namesCONTROLLER listenersCONTROLLER://:9093 log.dirs/data/kraft-metadata metadata.log.dir/data/kraft-metadata三个 Controller 节点的配置只有node.id不同分别是 1、2、3。controller.quorum.voters三个节点必须完全一致。Broker 节点配置process.rolesbroker node.id101 controller.quorum.voters1ctrl1:9093,2ctrl2:9093,3ctrl3:9093 listenersPLAINTEXT://:9092 log.dirs/data/kafka-logsBroker 节点的node.id从 101 开始和 Controller 的 ID 段分开方便管理。controller.quorum.voters同样要配全三个 Controller 地址。这里有个实操细节先启动所有 Controller 节点再启动 Broker 节点。Controller 之间会先完成 Leader 选举形成可用的元数据集群。Broker 启动后会向 Controller 注册自己拉取元数据。如果先启动 Broker它会因为连不上 Controller 而反复重试虽然最终也能成功但日志里会有一堆连接错误看着糟心。3.4 从 ZooKeeper 迁移的实操路径如果你已经有 ZooKeeper 集群想迁移到 KRaft官方提供了一条滚动迁移的路径。整个过程分几个阶段不能跳步。第一阶段把 ZooKeeper 集群升级到支持迁移的版本同时把 Kafka 集群升级到支持 KRaft 的版本。这个阶段集群还在 ZooKeeper 模式下运行只是代码版本更新了。第二阶段启动一组 Controller 节点让它们以迁移模式运行。这时候 Controller 会从 ZooKeeper 同步元数据但还不接管写入。配置里需要加zookeeper.connect和controller.quorum.voters两组参数。第三阶段把 Broker 逐个切换到 KRaft 模式。每个 Broker 重启时改配置去掉zookeeper.connect加上controller.quorum.voters。重启后 Broker 会从 Controller 拉取元数据不再连 ZooKeeper。第四阶段所有 Broker 切换完成后Controller 停止从 ZooKeeper 同步正式接管元数据管理。这时候 ZooKeeper 就可以下线了。注意迁移过程中集群会经历多次滚动重启每次重启都会影响一部分分区的可用性。建议在业务低峰期操作并且确保 Topic 的副本因子至少为 3这样单 Broker 重启不会导致分区不可用。我踩过的一个坑是迁移过程中如果 Controller 和 ZooKeeper 的元数据不一致会导致部分 Topic 的分区 Leader 信息错乱。排查方法是对比 Controller 日志里的元数据版本号和 ZooKeeper 里的 znode 版本号。如果对不上需要暂停迁移手动触发一次全量同步。4. 常见问题与排查技巧实录4.1 Controller 选举卡住或反复失败这是 KRaft 集群最常见的问题。现象是 Controller 日志里反复出现Starting leader election但没有Leader election completed或者选举完成后很快又发起新选举。排查思路分三步。第一步检查controller.quorum.voters配置是否三个节点完全一致。任何一个节点配错都会导致它无法参与投票多数派凑不齐。第二步检查网络连通性。Controller 之间用的是 9093 端口用telnet或nc测试一下端口是否可达。第三步检查node.id是否重复。如果两个节点用了同一个 IDRaft 会认为它们是同一个节点导致投票混乱。我遇到过一次选举卡住最后发现是某个 Controller 节点的磁盘满了写不进日志自然无法参与投票。所以磁盘监控一定要做好元数据日志目录至少留 20% 的余量。4.2 Broker 无法注册到 ControllerBroker 启动后日志里一直报Connection to controller failed或者Registration failed。这种情况通常是 Broker 的controller.quorum.voters配错了或者 Controller 的监听地址 Broker 访问不到。检查方法在 Broker 节点上用nc -zv ctrl1 9093测试连通性。如果端口不通检查 Controller 的listeners配置和防火墙规则。如果端口通但注册失败检查 Broker 的node.id是否和已有节点冲突。还有一个隐蔽的原因Controller 的controller.listener.names和 Broker 的listeners协议不匹配。比如 Controller 配了CONTROLLER://Broker 却用PLAINTEXT://去连协议对不上就会失败。确保两边的监听器名称和协议一致。4.3 元数据操作变慢的排查如果创建 Topic、修改分区这类操作明显变慢可以从几个方向排查。先看 Controller 的日志有没有Snapshot generation相关的耗时记录。快照生成太频繁会拖慢元数据写入可以调大metadata.log.max.record.bytes.between.snapshots。再看 Controller 节点的磁盘 IO。元数据日志是顺序追加对磁盘顺序写性能要求高。如果用的是机械盘或者共享存储IO 延迟可能成为瓶颈。建议 Controller 节点用本地 SSD。还有一个容易忽略的点Broker 数量太多。每个 Broker 都要从 Controller 拉取元数据Broker 越多Controller 的元数据分发压力越大。如果集群有几百个 Broker可以考虑增加 Controller 节点数把元数据分发压力分散开。4.4 常见问题速查表现象可能原因排查方法解决方式Controller 选举卡住voters 配置不一致对比三个节点配置统一配置后重启Broker 注册失败网络不通或端口错误nc 测试端口检查防火墙和监听配置元数据操作慢快照频繁或磁盘 IO 高查看 Controller 日志和磁盘监控调大快照间隔换 SSD集群启动后不可用Controller 数量不足多数派检查启动顺序和节点状态确保多数 Controller 在线迁移后 Topic 丢失元数据同步不完整对比 ZooKeeper 和 Controller 元数据回滚迁移重新同步4.5 几个我踩过的坑和独家技巧第一个坑格式化存储目录时用错集群 ID。kafka-storage.sh format的-t参数如果和实际集群 ID 不一致节点启动后会拒绝加入集群。这个错误在日志里不明显只报Inconsistent cluster ID。解决办法是重新格式化但注意格式化会清空元数据生产环境操作前一定要备份。第二个坑Controller 和 Broker 混部时资源竞争。测试环境图省事把两种角色放一起结果消息吞吐一上来Controller 的元数据写入就被拖慢导致 Broker 心跳超时触发不必要的重平衡。生产环境一定要分离部署至少把元数据日志目录和消息数据目录放在不同磁盘上。第三个技巧用kafka-metadata-quorum.sh工具查看元数据状态。这个工具可以查看当前 Leader、Follower 列表、日志偏移量等信息排查问题时非常有用bin/kafka-metadata-quorum.sh --bootstrap-server localhost:9092 describe --status输出里会显示CurrentLeader、CurrentVoters、MaxFollowerLag等关键指标。如果MaxFollowerLag持续增大说明某个 Follower 同步跟不上需要检查它的网络或磁盘。第四个技巧监控元数据日志的磁盘使用量。KRaft 的元数据日志虽然不大但快照文件会累积。如果快照清理不及时磁盘可能被撑满。建议配置监控当metadata.log.dir使用率超过 70% 时告警。5. 性能调优与规模上限的实测观察5.1 分区数量对元数据的影响KRaft 相比 ZooKeeper 最大的优势之一就是分区规模上限提高。我在测试环境做过对比同样配置下ZooKeeper 模式在分区数到 15 万左右时创建 Topic 的延迟从几百毫秒涨到十几秒KRaft 模式在分区数到 50 万时创建 Topic 延迟还在 1 秒以内。但这个提升不是无条件的。分区数增加会带来元数据日志膨胀快照文件变大Controller 内存占用增加。我实测下来每个分区的元数据大约占 1KB 到 2KB 内存50 万分区大概需要 1GB 左右的 Controller 堆内存。所以 Controller 节点的 JVM 堆建议至少 4GB给元数据留足空间。5.2 Controller 节点数量的取舍3 个 Controller 能容忍 1 个故障5 个能容忍 2 个。但 Controller 越多Raft 的写入延迟越高因为 Leader 要等更多 Follower 确认。我实测过 3 个和 5 个 Controller 的写入延迟差异3 个时元数据写入 P99 在 15ms 左右5 个时涨到 25ms 左右。所以选择 Controller 数量要权衡可用性和延迟。大部分场景 3 个足够除非你的集群规模特别大或者对可用性要求极高才考虑 5 个。超过 5 个没有意义Raft 的多数派机制决定了 7 个节点的容错能力只比 5 个多 1 个但延迟增加明显。5.3 元数据日志的磁盘选型Controller 节点的磁盘性能直接影响元数据写入延迟。元数据日志是顺序追加对顺序写性能要求高对随机读要求低。所以选盘的时候优先看顺序写 IOPS 和延迟。我用过三种盘做对比机械盘顺序写延迟在 5ms 到 10msSATA SSD 在 1ms 到 2msNVMe SSD 在 0.1ms 到 0.5ms。元数据写入延迟直接受磁盘影响所以 Controller 节点强烈建议用 NVMe SSD。如果预算有限至少用 SATA SSD不要用机械盘。提示元数据日志目录和消息数据目录分开放在不同磁盘上可以避免消息写入的 IO 压力影响元数据写入。这个在 Broker 和 Controller 混部的场景下尤其重要。6. 写在最后的一些个人体会KRaft 这套东西刚出来的时候我也觉得不就是把 ZooKeeper 换掉吗但真正用起来才发现它改变的不只是部署方式而是整个集群的运维逻辑。以前排查问题要同时看 Kafka 和 ZooKeeper 两套日志现在只看 Kafka 自己的元数据日志就够了。以前 Controller 切换要等几十秒现在几秒就完成。这些变化在平时可能感觉不明显但在故障时刻每一秒的恢复时间都意味着真金白银。我个人的建议是如果你还在用 ZooKeeper 模式可以开始规划迁移了。不用急着上生产先在测试环境跑通流程把配置和排查方法摸熟。迁移过程中最需要注意的是元数据一致性每次滚动重启后都要确认 Controller 和 Broker 的元数据版本对得上。另外Controller 节点的磁盘和网络一定要给足资源这是整个集群的大脑大脑卡了全身都动不了。最后分享一个小技巧KRaft 的元数据日志可以用kafka-dump-log.sh工具直接查看内容排查元数据问题时非常有用。命令是bin/kafka-dump-log.sh --cluster-metadata-decoder --files /data/kraft-metadata/00000000000000000000.log输出里能看到每条元数据记录的详细内容包括操作类型、操作对象、时间戳等。这个工具在排查某个 Topic 为什么不见了这类问题时特别管用。