欢迎光临
我们一直在努力

【Kafka进阶4】KRaft 完全指南:Apache Kafka 摆脱 ZooKeeper 的架构

KRaft(Kafka Raft Metadata Mode)是 Apache Kafka 自 2.8.0 引入、3.x 正式推荐生产使用的元数据管理新模式。它彻底移除了对 ZooKeeper 的依赖,将集群协调、元数据存储和共识算法全部内置,让 Kafka 成为一个真正自包含的分布式系统。

文章目录

    • 🧐 KRaft
    • ⚙️ KRaft 核心架构与工作原理
      • 1️⃣ Raft 共识协议
      • 2️⃣ 元数据内部存储
      • 3️⃣ 节点角色分离
    • 🧩 KRaft vs ZooKeeper:
    • 📊 KRaft 的核心优势
    • 🔧 关键配置与启用方式
      • 📝 核心配置项(`server.properties` 或 `controller.properties`)
      • 🚀 启用步骤(以 3 个控制器、3 个 Broker 为例)
    • 👑 控制器(Controller)的部署模式
      • 静态仲裁 vs 动态仲裁(KIP-853)
      • 动态控制器管理命令
        • ➕ 添加新控制器
        • ➖ 移除控制器
    • 🔍 运维与调试工具
      • 1️⃣ `kafka-metadata-quorum` —— 查看仲裁状态
      • 2️⃣ `kafka-dump-log` —— 解码元数据日志
      • 3️⃣ `kafka-metadata-shell` —— 交互式查看元数据树
    • ⚠️ 部署注意事项与限制
      • ✅ 生产环境推荐
      • 🚫 当前已知限制(截至 Kafka 3.9)
    • 🔄 从 ZooKeeper 迁移到 KRaft 的建议步骤
    • 🎯 总结

🧐 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-PEQseVb7Fb5q4Frn{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-PEQseVb7Fb5q4Frn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PEQseVb7Fb5q4Frn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PEQseVb7Fb5q4Frn .error-icon{fill:#552222;}#mermaid-svg-PEQseVb7Fb5q4Frn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PEQseVb7Fb5q4Frn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PEQseVb7Fb5q4Frn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PEQseVb7Fb5q4Frn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PEQseVb7Fb5q4Frn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PEQseVb7Fb5q4Frn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PEQseVb7Fb5q4Frn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PEQseVb7Fb5q4Frn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PEQseVb7Fb5q4Frn .marker.cross{stroke:#333333;}#mermaid-svg-PEQseVb7Fb5q4Frn svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PEQseVb7Fb5q4Frn p{margin:0;}#mermaid-svg-PEQseVb7Fb5q4Frn .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-PEQseVb7Fb5q4Frn .cluster-label text{fill:#333;}#mermaid-svg-PEQseVb7Fb5q4Frn .cluster-label span{color:#333;}#mermaid-svg-PEQseVb7Fb5q4Frn .cluster-label span p{background-color:transparent;}#mermaid-svg-PEQseVb7Fb5q4Frn .label text,#mermaid-svg-PEQseVb7Fb5q4Frn span{fill:#333;color:#333;}#mermaid-svg-PEQseVb7Fb5q4Frn .node rect,#mermaid-svg-PEQseVb7Fb5q4Frn .node circle,#mermaid-svg-PEQseVb7Fb5q4Frn .node ellipse,#mermaid-svg-PEQseVb7Fb5q4Frn .node polygon,#mermaid-svg-PEQseVb7Fb5q4Frn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PEQseVb7Fb5q4Frn .rough-node .label text,#mermaid-svg-PEQseVb7Fb5q4Frn .node .label text,#mermaid-svg-PEQseVb7Fb5q4Frn .image-shape .label,#mermaid-svg-PEQseVb7Fb5q4Frn .icon-shape .label{text-anchor:middle;}#mermaid-svg-PEQseVb7Fb5q4Frn .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-PEQseVb7Fb5q4Frn .rough-node .label,#mermaid-svg-PEQseVb7Fb5q4Frn .node .label,#mermaid-svg-PEQseVb7Fb5q4Frn .image-shape .label,#mermaid-svg-PEQseVb7Fb5q4Frn .icon-shape .label{text-align:center;}#mermaid-svg-PEQseVb7Fb5q4Frn .node.clickable{cursor:pointer;}#mermaid-svg-PEQseVb7Fb5q4Frn .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-PEQseVb7Fb5q4Frn .arrowheadPath{fill:#333333;}#mermaid-svg-PEQseVb7Fb5q4Frn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PEQseVb7Fb5q4Frn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PEQseVb7Fb5q4Frn .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PEQseVb7Fb5q4Frn .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-PEQseVb7Fb5q4Frn .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PEQseVb7Fb5q4Frn .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-PEQseVb7Fb5q4Frn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PEQseVb7Fb5q4Frn .cluster text{fill:#333;}#mermaid-svg-PEQseVb7Fb5q4Frn .cluster span{color:#333;}#mermaid-svg-PEQseVb7Fb5q4Frn 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-PEQseVb7Fb5q4Frn .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-PEQseVb7Fb5q4Frn rect.text{fill:none;stroke-width:0;}#mermaid-svg-PEQseVb7Fb5q4Frn .icon-shape,#mermaid-svg-PEQseVb7Fb5q4Frn .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PEQseVb7Fb5q4Frn .icon-shape p,#mermaid-svg-PEQseVb7Fb5q4Frn .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-PEQseVb7Fb5q4Frn .icon-shape .label rect,#mermaid-svg-PEQseVb7Fb5q4Frn .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PEQseVb7Fb5q4Frn .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-PEQseVb7Fb5q4Frn .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-PEQseVb7Fb5q4Frn :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

    $ bin/kafka-storage.sh random-uuid
    > 4LwY4x1ZRrW0uK8Qm9pN6A

  • 初始化每个控制器节点(假设 3 台机器,节点 ID 分别为 1,2,3) 在每台控制器上执行:

    $ 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 均需执行)

    $ bin/kafka-storage.sh format \\
    –cluster-id 4LwY4x1ZRrW0uK8Qm9pN6A \\
    –config config/kraft/broker.properties \\
    –no-initial-controllers

  • 启动所有节点

    $ 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,则为动态。

    动态控制器管理命令

    ➕ 添加新控制器

    # 先 provision 新控制器并启动,待其追上数据后执行:
    $ bin/kafka-metadata-quorum.sh \\
    –bootstrap-server localhost:9092 \\
    add-controller

    ➖ 移除控制器

    建议先关闭待移除控制器,再执行:

    $ 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 —— 查看仲裁状态

    $ 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 —— 解码元数据日志

    # 查看日志段
    $ 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 —— 交互式查看元数据树

    $ 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 模式;对于老项目,需评估停机窗口和业务风险,逐步规划迁移。

    赞(0)
    未经允许不得转载:171主机测评 » 【Kafka进阶4】KRaft 完全指南:Apache Kafka 摆脱 ZooKeeper 的架构
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址