KRaft(Kafka Raft Metadata Mode)是 Apache Kafka 自 2.8.0 引入、3.x 正式推荐生产使用的元数据管理新模式。它彻底移除了对 ZooKeeper 的依赖,将集群协调、元数据存储和共识算法全部内置,让 Kafka 成为一个真正自包含的分布式系统。
🧐 KRaft
在传统 Kafka 架构中,ZooKeeper 承担了控制器选举、元数据存储、Broker 状态管理等职责。虽然稳定,但带来了诸多困扰:
- 运维复杂:需要额外维护 ZooKeeper 集群,两套系统独立部署、监控、升级。
- 性能瓶颈:ZooKeeper 基于 ZAB 协议,在高分区数或频繁元数据操作下容易成为瓶颈。
- 分区数上限:受 ZooKeeper 节点存储和会话超时限制,单集群通常难以突破 20 万分区。
- 故障恢复慢:控制器选举依赖外部 ZooKeeper,出现问题时恢复时间较长(分钟级)。
KRaft 的诞生正是为了解决这些痛点,让 Kafka 的元数据管理变得 更轻量、更快速、更可靠。
⚙️ KRaft 核心架构与工作原理
KRaft 的设计围绕 Raft 共识算法 展开,将所有元数据当作 内部 Topic 来管理。其核心机制如下:
1️⃣ Raft 共识协议
- 控制器节点(Controller)通过投票选举出一个 Leader,负责处理所有元数据变更请求。
- 只有获得 多数派(Quorum) 确认后,变更才会提交,确保 强一致性。
- 多数派通常由奇数个节点组成(如 3 或 5 个),可容忍 1 或 2 个节点故障。
2️⃣ 元数据内部存储
- 所有集群元数据(Broker 信息、Topic/Partition 元数据、配置等)均存储在内部 Topic
__cluster_metadata中。 - 该 Topic 也采用日志追加方式,充分利用 Kafka 自身的顺序写入和复制能力。
3️⃣ 节点角色分离
KRaft 模式下,每个 Kafka 节点可担任以下角色(通过 process.roles 配置):
| 角色 | 职责 | 建议 |
|---|---|---|
| Controller | 元数据管理、Leader 选举、重平衡协调 | 生产环境独立部署 3 或 5 台 |
| Broker | 消息存储与读写 | 按业务负载水平扩展 |
| Combined(混合) | 同时承担 Controller 和 Broker | 仅适用于开发/测试环境,生产不推荐 |
🧩 KRaft vs ZooKeeper:
#mermaid-svg-pHHIAdglxCp7fLMa{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-pHHIAdglxCp7fLMa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-pHHIAdglxCp7fLMa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-pHHIAdglxCp7fLMa .error-icon{fill:#552222;}#mermaid-svg-pHHIAdglxCp7fLMa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-pHHIAdglxCp7fLMa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-pHHIAdglxCp7fLMa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-pHHIAdglxCp7fLMa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-pHHIAdglxCp7fLMa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-pHHIAdglxCp7fLMa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-pHHIAdglxCp7fLMa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-pHHIAdglxCp7fLMa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-pHHIAdglxCp7fLMa .marker.cross{stroke:#333333;}#mermaid-svg-pHHIAdglxCp7fLMa svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-pHHIAdglxCp7fLMa p{margin:0;}#mermaid-svg-pHHIAdglxCp7fLMa .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-pHHIAdglxCp7fLMa .cluster-label text{fill:#333;}#mermaid-svg-pHHIAdglxCp7fLMa .cluster-label span{color:#333;}#mermaid-svg-pHHIAdglxCp7fLMa .cluster-label span p{background-color:transparent;}#mermaid-svg-pHHIAdglxCp7fLMa .label text,#mermaid-svg-pHHIAdglxCp7fLMa span{fill:#333;color:#333;}#mermaid-svg-pHHIAdglxCp7fLMa .node rect,#mermaid-svg-pHHIAdglxCp7fLMa .node circle,#mermaid-svg-pHHIAdglxCp7fLMa .node ellipse,#mermaid-svg-pHHIAdglxCp7fLMa .node polygon,#mermaid-svg-pHHIAdglxCp7fLMa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-pHHIAdglxCp7fLMa .rough-node .label text,#mermaid-svg-pHHIAdglxCp7fLMa .node .label text,#mermaid-svg-pHHIAdglxCp7fLMa .image-shape .label,#mermaid-svg-pHHIAdglxCp7fLMa .icon-shape .label{text-anchor:middle;}#mermaid-svg-pHHIAdglxCp7fLMa .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-pHHIAdglxCp7fLMa .rough-node .label,#mermaid-svg-pHHIAdglxCp7fLMa .node .label,#mermaid-svg-pHHIAdglxCp7fLMa .image-shape .label,#mermaid-svg-pHHIAdglxCp7fLMa .icon-shape .label{text-align:center;}#mermaid-svg-pHHIAdglxCp7fLMa .node.clickable{cursor:pointer;}#mermaid-svg-pHHIAdglxCp7fLMa .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-pHHIAdglxCp7fLMa .arrowheadPath{fill:#333333;}#mermaid-svg-pHHIAdglxCp7fLMa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-pHHIAdglxCp7fLMa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-pHHIAdglxCp7fLMa .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pHHIAdglxCp7fLMa .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-pHHIAdglxCp7fLMa .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pHHIAdglxCp7fLMa .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-pHHIAdglxCp7fLMa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-pHHIAdglxCp7fLMa .cluster text{fill:#333;}#mermaid-svg-pHHIAdglxCp7fLMa .cluster span{color:#333;}#mermaid-svg-pHHIAdglxCp7fLMa div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-pHHIAdglxCp7fLMa .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-pHHIAdglxCp7fLMa rect.text{fill:none;stroke-width:0;}#mermaid-svg-pHHIAdglxCp7fLMa .icon-shape,#mermaid-svg-pHHIAdglxCp7fLMa .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pHHIAdglxCp7fLMa .icon-shape p,#mermaid-svg-pHHIAdglxCp7fLMa .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-pHHIAdglxCp7fLMa .icon-shape .label rect,#mermaid-svg-pHHIAdglxCp7fLMa .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pHHIAdglxCp7fLMa .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-pHHIAdglxCp7fLMa .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-pHHIAdglxCp7fLMa :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} KRaft架构
Raft共识
Raft共识
元数据同步
元数据同步
元数据同步
内部通信
Controller Leader
Controller Follower
Controller Follower
Broker
Broker
Broker
传统架构
选举/元数据
选举/元数据
选举/元数据
依赖外部
ZooKeeper集群
Kafka Broker
Kafka Broker
Kafka Broker
核心变化:
- ❌ 移除了外部 ZooKeeper 集群。
- ✅ 控制器节点自身组成 Raft 多数派,实现 自管理。
- ✅ Broker 只需与控制器交互,不再依赖第三方协调器。
📊 KRaft 的核心优势
| 对比维度 | 传统 ZooKeeper 模式 | KRaft 模式 |
|---|---|---|
| 架构复杂度 | 需维护两套独立集群 | 仅需维护 Kafka 集群 |
| 配置项数量 | 约 40+ 项 | 精简 30%(约 28 项) |
| 元数据操作延迟 | 较高(依赖 ZK 网络往返) | 降低 50% 以上 |
| 集群启动时间 | 分钟级(等待 ZK 会话) | 秒级 |
| 分区数上限 | ~20 万 | 百万级(实测可达 200 万) |
| 控制器故障恢复 | 数秒至分钟(依赖 ZK 选举) | < 1 秒(Raft 原生选举) |
| 安全模型 | 需分别配置 ZK 和 Kafka 认证 | 统一 的认证授权机制 |
| 运维负担 | 双倍监控、备份、升级 | 单集群统一管理 |
🔧 关键配置与启用方式
📝 核心配置项(server.properties 或 controller.properties)
| 配置项 | 说明 | 示例值 |
|---|---|---|
process.roles |
节点角色:broker / controller / broker,controller(混合) / 留空(ZooKeeper 模式) |
controller |
node.id |
节点唯一数字 ID | 1 |
controller.quorum.voters(静态)或 controller.quorum.bootstrap.servers(动态) |
指定控制器节点列表,用于发现和选举 | 1@host1:9093,2@host2:9093,3@host3:9093 |
listeners |
监听地址,需区分 CONTROLLER 端口和 PLAINTEXT 端口 | CONTROLLER://:9093, PLAINTEXT://:9092 |
controller.listener.names |
指定用作控制器通信的监听器名称 | CONTROLLER |
metadata.log.dir |
元数据日志存储目录 | /var/kafka/metadata |
🚀 启用步骤(以 3 个控制器、3 个 Broker 为例)
-
生成集群 ID
bash$ bin/kafka-storage.sh random-uuid > 4LwY4x1ZRrW0uK8Qm9pN6A -
初始化每个控制器节点 (假设 3 台机器,节点 ID 分别为 1,2,3)
在每台控制器上执行:
bash$ bin/kafka-storage.sh format \ --cluster-id 4LwY4x1ZRrW0uK8Qm9pN6A \ --config config/kraft/controller.properties \ --initial-controllers "1@controller1:9093:${UUID1},2@controller2:9093:${UUID2},3@controller3:9093:${UUID3}"注意:
UUIDx可通过kafka-storage.sh random-uuid分别为每个控制器生成一个目录 ID。如果使用 动态仲裁 (推荐),只需指定
--initial-controllers,无需设置controller.quorum.voters,系统会自动生成VotersRecord。 -
格式化 Broker 节点(每个 Broker 均需执行)
bash$ bin/kafka-storage.sh format \ --cluster-id 4LwY4x1ZRrW0uK8Qm9pN6A \ --config config/kraft/broker.properties \ --no-initial-controllers -
启动所有节点
bash$ bin/kafka-server-start.sh config/kraft/controller.properties # 控制器 $ bin/kafka-server-start.sh config/kraft/broker.properties # Broker
💡 小贴士:首次启动时,确保先启动所有控制器(至少达到多数派),再启动 Broker。
👑 控制器(Controller)的部署模式
静态仲裁 vs 动态仲裁(KIP-853)
| 特性 | 静态仲裁 | 动态仲裁(推荐) |
|---|---|---|
| 配置方式 | controller.quorum.voters 硬编码所有控制器 |
controller.quorum.bootstrap.servers 仅需部分种子节点 |
| 控制器变更 | 需修改所有 Broker 和 Controller 配置并重启 | 通过 kafka-metadata-quorum add/remove-controller 动态调整 |
| 适用版本 | Kafka 3.0 ~ 3.8 | Kafka 3.9+(需 kraft.version=1) |
| 灵活性 | 低 | 高(支持在线扩缩容) |
如何判断当前集群类型?
执行
kafka-features.sh --bootstrap-controller <controller:port> describe,若kraft.version为0或不存在,则为静态;若为1,则为动态。
动态控制器管理命令
➕ 添加新控制器
bash
# 先 provision 新控制器并启动,待其追上数据后执行:
$ bin/kafka-metadata-quorum.sh \
--bootstrap-server localhost:9092 \
add-controller
➖ 移除控制器
建议先关闭待移除控制器,再执行:
bash
$ bin/kafka-metadata-quorum.sh \
--bootstrap-server localhost:9092 \
remove-controller --controller-id <id> --controller-directory-id <directory-id>
⚠️ 注意:在 KIP-996(预投票)实现前,移除前务必先停止目标控制器,避免出现脑裂风险。
🔍 运维与调试工具
1️⃣ kafka-metadata-quorum ------ 查看仲裁状态
bash
$ bin/kafka-metadata-quorum.sh --bootstrap-server localhost:9092 describe --status
输出示例:
ClusterId: fMCL8kv1SWm87L_Md-I2hg
LeaderId: 3002
LeaderEpoch: 2
HighWatermark: 10
MaxFollowerLag: 0
CurrentVoters: [{"id": 3000, "directoryId": "...", "endpoints": ["CONTROLLER://localhost:9093"]}, ...]
CurrentObservers: [{"id": 0, "directoryId": "..."}, ...]
2️⃣ kafka-dump-log ------ 解码元数据日志
bash
# 查看日志段
$ bin/kafka-dump-log.sh --cluster-metadata-decoder --files metadata_log_dir/__cluster_metadata-0/00000000000000000000.log
# 查看快照
$ bin/kafka-dump-log.sh --cluster-metadata-decoder --files metadata_log_dir/__cluster_metadata-0/00000000000000000100-0000000001.checkpoint
3️⃣ kafka-metadata-shell ------ 交互式查看元数据树
bash
$ bin/kafka-metadata-shell.sh --snapshot metadata_log_dir/__cluster_metadata-0/00000000000000000000.log
>> ls /topics
foo
>> cat /topics/foo/0/data
...
>> exit
⚠️ 部署注意事项与限制
✅ 生产环境推荐
- 控制器数量 :至少 3 个,奇数个(3/5/7...),以容忍 N 个故障需
2N+1个节点。 - 角色分离 :Broker 和 Controller 不要混合部署,避免资源争抢和升级耦合。
- 内存与磁盘 :每个控制器预留 至少 5GB 内存 和 5GB 磁盘 用于元数据(实际视分区数调整)。
- 网络:控制器间通信延迟需 < 20ms,避免选举超时。
🚫 当前已知限制(截至 Kafka 3.9)
- JBOD(多存储目录):仍处于早期访问阶段(KIP-858),生产环境慎用。
- 动态配置修改 :部分动态配置(如
log.retention.ms)在独立控制器上修改后可能无法同步,需等待未来版本修复。 - 静态转动态 :目前 不支持 将静态仲裁集群在线转换为动态仲裁,需重新格式化(需停机)。
🔄 从 ZooKeeper 迁移到 KRaft 的建议步骤
📌 由于 KRaft 与 ZK 模式 不兼容 ,迁移通常需要 停机切换。以下为通用迁移流程,建议先在测试环境演练。
- 版本升级 :将当前 Kafka 集群升级到 3.9.x(最新稳定版),并确保客户端兼容。
- 元数据导出 :使用
kafka-dump-log或其他工具导出 ZK 中的元数据(Topic、分区、配置、ACL 等)。 - 搭建新 KRaft 集群 :按照前文步骤,使用 相同的集群 ID(可选)和配置,部署新的 Controller + Broker 节点。
- 数据复制:将旧集群的数据(所有 Topic 分区数据)物理复制到新集群的存储目录(可通过 MirrorMaker 或直接 rsync)。
- 切换流量:停止旧集群,将生产客户端指向新集群的 Broker 地址。
- 验证与回退:验证业务功能,如有问题可快速切回旧集群(保留旧集群一段时间)。
💡 更平滑的替代方案 :使用 Kafka 的 滚动升级 + 双运行 模式(KIP-500 后续版本支持),但截止目前,官方推荐仍为停机迁移。请密切关注社区新特性。
🎯 总结
KRaft 是 Kafka 历史上最具颠覆性的架构改进之一,它将分布式协调的复杂性 内化,带来了:
- ✅ 更简单的运维(一套系统替代两套)
- ✅ 更高的性能(元数据操作延迟减半,启动秒级)
- ✅ 更强的扩展性(支持百万级分区)
- ✅ 更快的故障恢复(<1 秒)
- ✅ 更统一的安全模型
对于新建项目,强烈建议 直接采用 KRaft 模式;对于老项目,需评估停机窗口和业务风险,逐步规划迁移。