Kafka KRaft 迁移指南:从 ZooKeeper 到 Raft 元数据
Kafka 4.0 移除 ZooKeeper 后,存量 ZK 模式集群如何规划 controller quorum、执行迁移并安全回滚
如果集群还在用 ZooKeeper 模式跑 Kafka,4.0 是最后的倒计时:Kafka 4.0 已彻底移除 ZooKeeper 支持,存量 ZK 集群无法直接升级,必须先迁移到 KRaft。KRaft 是 Kafka 用 Raft 协议自管元数据的新模式,替代外部 ZooKeeper 承担元数据存储与选主职责。本文给出从 3.x ZK 集群迁移到 KRaft 的可执行路径:前置条件、controller quorum 规划、dual-write 迁移阶段、验证与回滚策略。
先理解为什么要迁。ZooKeeper 模式的问题有两类。一类是元数据一致性:Kafka 的元数据(分区分配、配置、ACL、ISR 等)存储在 ZK 中,controller 需要把 ZK 里的元数据同步到自己的内存,再广播给 broker,两套系统之间的一致性靠外部协调,故障场景下容易出现 controller 与 ZK 状态不一致、分区长时间不可用。另一类是运维复杂度:集群要维护 ZK 与 Kafka 两套系统,ZK 的会话超时、磁盘故障、滚动升级都是额外负担,且 ZK 模式下 controller 故障恢复依赖 ZK 会话机制,恢复时间较长。KRaft 用 controller quorum(基于 Raft 的元数据日志)取代 ZK,元数据变更先写入 quorum 日志再广播,一致性由 Raft 保证,少一套系统,恢复与运维都更简单。KRaft 经历了 KIP-500 的设计与 KIP-866 的迁移支持,从 3.3.0 起 ZK 到 KRaft 的在线迁移成为可能(官方迁移文档)。
迁移前置条件,第一是版本路径。KRaft-only 是 4.0 的硬性要求,但迁移本身要在 3.x 上完成:建议先把集群升到较新的 3.x 版本,在 3.x 上完成 ZK 到 KRaft 的迁移并稳定运行一段时间,再升 4.0。跳过 3.x 直接升级到 4.0 是行不通的。第二是 controller quorum 设计。quorum 必须配置奇数个节点(1、3 或 5),3 个节点容忍 1 台故障,生产环境至少 3 个;controller 可以独立部署(dedicated,适合中大规模集群,broker 与 controller 资源隔离),也可以与 broker 合并部署(combined,小集群与测试环境常用);controller 需要独立稳定的磁盘与网络,元数据日志对延迟敏感,不要与其他高 IO 服务混跑。第三是存储目录规划。controller 与 broker 各自需要自己的 log.dirs,迁移前要确认目录容量与挂载方式,format 操作会清空目标目录,选错目录意味着数据丢失。
迁移执行分为三个阶段。阶段一,准备 quorum:用 kafka-storage random-uuid 生成集群 ID,为 controller 编写配置文件(process.roles=controller、controller.quorum.voters 等),用 kafka-storage format 格式化 controller 存储目录,然后启动 controller 节点。阶段二,broker 进入 dual-write:为每个 broker 增加 controller.quorum.voters 配置并指向 quorum,保持 process.roles=broker,逐个滚动重启 broker;重启后 broker 进入迁移模式,元数据同时写入 ZK 与 KRaft quorum,这个阶段就是 dual-write 阶段——它保证迁移过程中元数据不丢失,也提供了回滚窗口。阶段三,切换与移除 ZK:确认全部 broker 已进入迁移模式且元数据同步正常后,把 controller 切换到权威模式,让 controller quorum 成为元数据唯一权威,再逐个滚动重启 broker 去掉 zookeeper.connect 配置,最后下线并移除 ZK 节点。整个过程中用 kafka-metadata-quorum 命令观察 quorum 状态、用 kafka-metadata-shell 检查元数据内容,是验证迁移进度的主要手段。
验证与回滚要盯住两个时间点。在阶段二(dual-write 期间、controller 尚未切换为权威)之前,回滚是安全的:把 broker 配置中的 controller.quorum.voters 移除并滚动重启,集群回到纯 ZK 模式,元数据不丢失。一旦 controller 切换完成、KRaft 成为唯一权威,回滚窗口关闭,此时 ZK 里的元数据已经落后,不能再依赖 ZK 回滚,只能继续向前完成迁移;如果此时发现问题,处理手段是修复 KRaft 侧而不是退回 ZK。因此迁移计划要把"切换动作"安排在低峰期,并在切换前完成数据与元数据的完整备份。
常见失败模式有四种。一是 controller 失联:controller.quorum.voters 里写的节点地址与实际不一致、端口未放通、节点间时钟偏差过大,都会让 broker 无法与 quorum 建立连接,表现为元数据同步停滞,先排查网络与配置清单。二是元数据落后:dual-write 阶段 ZK 与 KRaft 写入不同步,常见于迁移过程中仍有大量元数据变更(大量新建 topic、频繁改配置),建议迁移窗口内冻结元数据变更操作。三是 format 误操作:对已有数据的目录执行 format 会直接清空,迁移前必须核对目录与集群 ID。四是回滚窗口误判:在 controller 切换后尝试回滚 ZK,会导致元数据不一致,集群状态不可预测,这属于操作纪律问题,需要在切换前做书面确认。
controller quorum 的规模权衡可以再展开。3 节点是生产基线,容忍 1 台故障,适合绝大多数集群;5 节点容忍 2 台故障,适合跨可用区部署或对可用性要求极高的场景,代价是每次元数据写入要复制到更多节点,写延迟略升;单节点 quorum 只适合开发测试环境,没有任何容错。选择 dedicated 还是 combined,核心看元数据写入频率:topic 数量多、分区变更频繁的集群,controller 的 CPU 与磁盘 IO 压力不小,独立部署能避免与 broker 的数据读写互相干扰;小集群则用 combined 省机器。另一个规划要点是节点间网络:quorum 选举与日志复制对网络抖动敏感,controller 节点之间、controller 与 broker 之间应保证低延迟互通,避免跨地域组 quorum。
迁移时间线可以按一个中型集群(30 个 broker、1 万个分区)估算:准备与格式化 controller 约半天;broker 逐个滚动重启进入 dual-write,每个 broker 重启约 10 分钟,总计约一个晚上;dual-write 观察期建议留 1 到 2 天,期间用 kafka-metadata-quorum describe 观察 quorum 状态、用 kafka-metadata-shell 抽查元数据与 ZK 是否一致,同时让业务流量正常跑,确认没有异常;随后在低峰期完成 controller 切换与 ZK 移除,再观察 1 天。整个迁移对业务的影响主要是滚动重启期间的短暂不可用(每台 broker 重启几十秒),producer 与 consumer 会自动重连,但建议在重启前确认客户端重连与重试参数,避免瞬时超时被误报为故障。把"dual-write 观察期"当成正式项目阶段来排期,是迁移成功率最高的做法。
迁移完成后的运维变化也值得提前预期。ZK 移除后,原先围绕 ZK 的监控项(会话数、znode 数量、ZK 磁盘与延迟)不再需要,取而代之的是 controller quorum 的日志复制延迟、选举时间与元数据同步状态,建议在迁移前就把这些指标接入监控;原先依赖 ZK 的运维脚本与告警也要一并清理,否则会产生大量无效告警。另一个细节是迁移后首次重启集群时,要确认 broker 配置里已无 zookeeper.connect 残留,controller 配置中的 voter 列表与实际节点一致,否则集群可能无法完成选主或出现只读分区。把这些收尾动作写进检查单,迁移才算真正结束。
下一步建议:先在测试环境用三个节点完整走一遍"format、dual-write、切换、移除 ZK"流程并验证回滚,再为生产集群制定低峰期迁移窗口,迁移当天按"先冻结元数据变更、再逐个滚动重启 broker"的节奏执行,全程用 kafka-metadata-quorum 留档。


