ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Kafka集群扩容实战:新增Broker与数据迁移完整指南

Kafka集群扩容实战:新增Broker与数据迁移完整指南 做Kafka集群扩容这件事我最早的实践是在一个日增几百GB数据、四台Broker的在线业务集群上。Kafka这东西单机或者三五节点的小集群跑起来都很省心可一旦业务量上来磁盘占用、瞬时吞吐、分区数、消费Lag这些指标会同时给你压力。区别于网上很多只是“把新机器装起来启动完就算扩容成功”的教程真正让我觉得有价值的部分是启动新Broker节点之后那一整套数据迁移的操作链路。简单说如果不管数据迁移新节点在一段时间内几乎等于空转老节点上的所有分区照样挤在原来的物理盘上该卡的还是卡。本篇文章就把“新增Broker节点”和“数据迁移”这两件事串起来讲清楚适合已经熟悉Kafka基础概念、但没完整操刀过集群扩容的工程师也适合正在做扩容方案设计的架构师拿来当自查清单用。1. 判断扩容时机你的Kafka集群什么时候真的需要加Broker很多人来问我扩容的事第一句话都是“我们集群不够用了怎么加节点”。但这个“不够用”特别模糊不好直接动手。Kafka的扩容成本和收益其实很高如果判断错了加了节点也解决不了问题。所以在动手之前我非常建议先建立一套简单的扩容判断逻辑。1.1 最典型的扩容信号最常见的扩容信号有这么几类磁盘使用率持续走高压缩和清理跑不赢写入速度单个数据目录已经接近85%以上。某个核心Topic的分区数量很大但每个分区的请求吞吐仍然到了网卡或CPU瓶颈。消费者组的Lag持续堆积但客户端处理能力和业务逻辑优化后也没有明显好转怀疑Broker侧分区取数响应变慢。新增主题已经很难选出“负载不重”的Broker来落脚分区分配肉眼可见地不均匀。这里面最容易被忽视的是磁盘。Kafka的吞吐核心其实在于顺序写和顺序读磁盘够不够快、够不够空往往比CPU优先级更高。如果发现节点负载不高、但磁盘占用到了红线扩容的本质是在增加存储容量而不是增加计算能力这时候要补节点也要注意新节点的存储配置不能太弱。还有一种情况是broker的副本同步长期处于“追不上”的状态。ISR里频繁出现副本掉出又加回重点要看的是这个——它有时不是因为网络抖动而是因为单块磁盘上承载的分区太多或者某个磁盘的分区目录文件数太大导致副本拉取数据写盘的IO时间超过了Kafka默认的副本超时时间replica.lag.time.max.ms默认30秒。这种坏味道光靠优化参数很难根治把磁盘压力分担到新节点上才是正解。1.2 先别急着加机器先检查这几个指标扩容之前我会先花半小时登录现有Broker按下面的顺序排查网络sar -n DEV 1 5或iftop看网卡占用。Kafka对带宽极其敏感如果网卡已经跑满加节点能缓解单机压力但跨机架副本同步的流量也会随之增加要注意新节点的网络容量。磁盘IO用iostat -x 1看%util和平均IO等待时间。如果磁盘队列很长说明瓶颈在盘上。文件描述符cat /proc/sys/fs/file-max和ulimit -n我见过几次“隐性问题”连接数一多系统直接报“Too many open files”此时不是扩容而是调整内核参数。JVM堆用jstat -gcutil pid 1000看老年代和Full GC频率。Kafka本身对堆要求不高但GC时间过长会导致请求抖动容易被误判成集群容量不足。分区请求吞吐看客户端侧生产者/消费者的指标或者用Kafka自带的kafka-run-class.sh kafka.tools.JmxTool看Broker端bytes-in-per-sec、bytes-out-per-sec。如果单Broker的入站流量接近几GB并且CPU的用户态占比高说明这台机器的线程和网络栈已经到顶了。只有这些基础检查都做完了确认不是单节点乱象而是集群整体容量问题再去买机器、启动节点这个方向才靠谱。1.3 从客户端视角确认瓶颈还有一个容易被忽略的信号源客户端监控。有时候Broker本身看着还行CPU中等、磁盘不高但业务方反馈“Kafka消息延迟高”。我一般会让业务方抓一下生产者的batch.size、linger.ms、acks设置以及消费者max.poll.records、单线程处理耗时。如果生产端ackall且linger.ms很小客户端请求会被每个分区Leader所在节点的响应时间拖住消费者如果单条消息处理耗时长Lag上涨与Broker扩容关系不大那是消费端计算能力的问题。把这个方向理清楚才能避免“加机器白花钱”。2. 扩容前的准备工作版本、磁盘、带宽与配置基线判断完确实需要扩容下一个阶段就是把“新Broker节点”加入现有集群前的一系列准备工作。这一步最烦但也最容易因为经验不足而翻车。2.1 版本兼容性检查Kafka集群内部各Broker之间要求协议版本兼容。如果你现在的集群是Kafka 2.8新节点直接装3.6启动时会发现各种UnsupportedVersionException或者元数据同步异常。跨大版本新增节点不是绝对不行但你得接受“旧节点以旧协议和老数据格式运行新节点兼容新版本”这种中间状态并且要仔细阅读官方升级文档中关于broker版本滚动的说明。我的建议很保守新增节点使用和现有集群相同的版本或者最多打同一个小版本内的补丁版本。如果你确实想借这次扩容顺便升级那就走完整的“滚动升级”流程而不要混在扩容里同时做不然出了问题既分不清是迁移问题还是升级问题。实际操作时我会先看Broker启动日志里的版本信息或者执行kafka-broker-api-versions.sh --bootstrap-server 任一Broker --version来确认。2.2 磁盘容量与目录布局规划Kafka的数据目录log.dirs是我在新节点上最关注的东西。这里有几个容易踩的小点不要把 log.dirs 里的多个目录放在同一块物理磁盘上比如把 /data1 和 /data2 都挂在同一块大云盘下看起来容量翻倍实际上IO还是共享的。云盘选型要注意单盘吞吐上限和IOPS。Kafka写日志基本是顺序追加对顺序写性能要求高普通HDD7200转也能勉强跑但如果是高吞吐核心集群至少给SSD或NVMe。目录空间规划时建议预留20%以上余量。因为分区副本迁移期间新老节点都会同时存在多份数据磁盘占用会有一个“双写”阶段如果整机才预留10%迁移后半段大概率会爆盘。我在一次扩容前简单计算过容量现有副本数据总量约1.8TB三个Broker平均每台600GB扩容目标是六台Broker那么每台新节点数据目录至少要预留 1.8TB / 6 × 1.2迁移期间冗余 ≈ 360GB再加日志和索引文件的额外膨胀我直接给了单节点2TB NVMe后期完全没为磁盘发过愁。2.3 机架感知与带宽预留如果新节点和旧节点在同一个机房或者同一个机架通常不用配置broker.rack。但如果你跨机架部署则必须设置broker.rack这样Kafka在分配副本时会尽量让同一分区的多个副本落在不同机架上避免单机架宕机导致所有副本同时丢失。带宽方面Kafka副本同步的流量遵循“leader网络出口、follower网络入口”的逻辑。新节点迁移数据时它要从其他Broker拉数据也会接收客户端流量。这个“双份流量”如果没预留迁移期间很容易打满新节点的网卡导致ISR频繁收缩。所以我会在迁移策略里直接给副本拉取流量限速宁可迁移慢一点也不要惊动线上。2.4 记录现有Broker配置基线扩容后要对照检查所以扩容前先把现有正常工作的Broker配置下下来每个Broker的server.properties重点关注log.retention.hours、log.segment.bytes、log.retention.check.interval.ms。log.dirs的挂载和目录情况。自定义的JVM参数尤其堆大小、GC日志参数。主题级别的配置retention.ms、min.insync.replicas、max.message.bytes这部分通过kafka-configs.sh --describe --entity-type topics导出。我一般会把当时线上正在运行的server.properties直接复制到新节点上只改broker.id、listeners、log.dirs这三个关键项。这样能最大程度避免新旧节点行为不一致。3. 新增Broker节点的完整操作从server.properties到节点验证准备工作完成后正式在新机器上部署Kafka。很多教程会直接告诉你“解压、改配置、启动”就完事但三步里面每一步都有值得仔细检查的细节。3.1 server.properties里要改的五个关键项以Kafka 2.8/3.x为例我一般只动这些broker.id5 listenersPLAINTEXT://0.0.0.0:9092 advertised.listenersPLAINTEXT://10.10.10.5:9092 log.dirs/data0/kafka-logs,/data1/kafka-logs offsets.topic.replication.factor3 transaction.state.log.replication.factor3 transaction.state.log.min.isr2broker.id新节点的ID不能和现有Broker重复否则启动时集群元数据会错乱正常情况下会直接报错退出。检查现有ID的方法是zookeeper-shell.sh zk/brokers/ids或者在KRaft模式下用kafka-metadata.sh。advertised.listeners这里最容易出错。0.0.0.0是监听地址但要告诉客户端连哪个IP就必须写实际对客户端暴露的IP。很多容器化部署或者云主机有多块网卡的场景这里的地址写错会导致客户端连接不上。log.dirs如果有多块数据盘这里可以逗号分隔但要注意每个目录都最好独立物理盘目录本身必须存在且有权限。Kafka不会自动创建不存在的目录启动时会报错。Partition自动均衡相关新版本的auto.leader.rebalance.enable默认是true但它的触发频率很低不要指望靠它完成迁移后的均衡。后面我会讲手动reassign。如果集群开启了SASL/SSL记得同步安全和认证配置。我在一次规模不大的集群扩容中漏了SASL机制配置新节点启动后客户端连不上日志里全是Failed authentication排查半天才发现配置文件里安全协议还是PLAINTEXT。3.2 启动新节点与日志检查启动命令很简单无非是bin/kafka-server-start.sh -daemon config/server.properties但启动之后不要立刻看“Running”就以为OK。我更习惯做这几步jps确认进程存在。看logs/server.log里有没有INFO Completed load of log这类加载日志确认数据目录正常初始化。看是否有ERROR、FATAL尤其是Log directory ... not found、SocketException。用ss -ltnp | grep 9092确认监听端口正常。验证新节点是否已经加入集群bin/kafka-broker-api-versions.sh --bootstrap-server 新节点IP:9092能正常返回版本列表说明新节点已经被集群元数据接受了。也可以用kafka-metadata.sh --describe看Broker列表或老版本zookeeper-shell.sh查看/brokers/ids。新节点刚启动时会先尝试从ZooKeeper或KRaft元数据里加载全量主题元数据。因为现有主题很多这个加载可能需要几分钟甚至更久。如果你的运维监控发现“Broker在线但主题列表为空”先别急着折腾给它一点时间完成元数据同步再观察日志。3.3 新节点上线后的状态观察新Broker上线初期我的观察重点是新节点的CPU和网络是不是基本闲置如果迁移还没开始老主题的数据不会主动到新节点上。磁盘上是否有新创建的主题目录只有新建主题的分区分配可能随机落到新节点上。集群里有没有因为新节点加入触发Metadata response更新客户端侧是否短暂重连。这里有个重要结论新建主题的分区分配是随机的它可能落到老节点也可能落到新节点这取决于分区分配策略和已有Broker列表。但是存量主题的数据不会因为新节点加入而自动搬迁。所以下一章才是这个项目标题里真正的重头戏数据迁移。4. 数据迁移核心副本重新分配的原理与操作数据迁移在Kafka里的官方名字是“分区副本重新分配”Partition Reassignment。它本质上是把某个分区的副本列表从一个Broker集合改到另一个Broker集合Kafka会为新列表里的新副本创建副本目录并追上数据。4.1 副本迁移的底层机制每个Topic分区在Broker上的落盘形式是一个目录log.dirs/topic-partition。比如fa-news-0这个目录就是Topic名为fa-news、分区0的所有日志段。副本重新分配发生时Kafka会做这些事在新副本目标Broker上创建对应的分区目录。新的follower副本向leader发送Fetch请求把日志从旧leader那里拉到自己本地。拉取足够快后新副本进入ISR同步副本集合。旧副本会被标记为“删除中”一段时候后会在旧Broker上被清理掉。所以迁移期间同一块数据最多存在三份取决于副本因子这就是为什么我之前说磁盘要预留余量。另外要注意迁移过程中分区的Leader一般保持不变。即使Leader所在的Broker不迁移也会因为副本离开ISR而短暂不可用。极端情况下如果副本全部迁走Leader会切换到新副本上客户端会对分区Leader变化产生一次重连和重新拉取元数据。这段期间消费者会有一点点感知但通常业务端是无感的。4.2 生成迁移计划部署好新节点后第一步是生成一份迁移计划。Kafka提供了一个现成的工具kafka-reassign-partitions.sh。最稳妥的流程是用它先--generate它会基于当前分区分布生成目标副本分配。bin/kafka-reassign-partitions.sh \ --bootstrap-server 旧节点1:9092,旧节点2:9092,旧节点3:9092 \ --topics-to-move-json-file topics-to-move.json \ --broker-list 0,1,2,3,4,5 \ --generate其中topics-to-move.json是一个简单的JSON指定要迁移的主题{ topics: [ {topic: fa-news}, {topic: fa-log} ], version: 1 }--broker-list是目标Broker集合例如全部六台节点的broker.id。--generate会输出两段JSON一段是“当前分配”一段是“建议分配”。建议分配的内容就是一个标准的reassignment JSON大概长这样{ version: 1, partitions: [ {topic: fa-news, partition: 0, replicas: [0, 2, 4]}, {topic: fa-news, partition: 1, replicas: [1, 3, 5]}, ... ] }建议分配遵循的原则是把某个分区的多个副本尽量放在不同Broker上同时尽量均匀地分布到所有Broker。它生成的是全局均衡方案但注意它只对指定主题做均衡迁移过程中不会考虑主题间的磁盘差异。如果fa-news分区特别大而fa-log很小生成分配的负载在物理盘上可能并不平衡。4.3 执行迁移和限流参数拿到reassignment JSON后保存成文件reassignment.json执行bin/kafka-reassign-partitions.sh \ --bootstrap-server 旧节点1:9092 \ --reassignment-json-file reassignment.json \ --execute执行前我强烈建议加上--throttle参数。这个参数的原理是限制分区副本在每个Broker上的复制流量单位是bytes/second。如果不加新节点会开启全速拉取模式可能导致旧节点网络出口打满在线业务出现请求超时。新节点网卡入口打满ISR瞬间收缩。磁盘IO抖动影响同一台机器上其他分区的读写。比较保守的做法是先用--throttle 104857600100MB/s观察迁移速度和线上指标再决定是否调高。如果线网带宽充足并且业务有低谷窗口给到200MB-300MB/s也是常见的。执行--execute后Kafka会输出迁移任务并给出可执行--verify的提示。如果你设置了限流Kafka会自动配置Broker端动态参数leader.replication.throttled.ratefollower.replication.throttled.rate任务结束之后Kafka会自动把这些参数恢复。但如果你手动执行失败可能残留这些动态参数后续得自己清理。这也是为什么我建议不要手动去配置这些参数而是通过kafka-reassign-partitions.sh来统一管理。4.4 监控迁移状态与ISR保护迁移执行后不要直接走人我一般盯着三件事bin/kafka-reassign-partitions.sh \ --bootstrap-server 旧节点1:9092 \ --reassignment-json-file reassignment.json \ --verify--verify会输出每个分区是否迁移完成并且会提示Reassignment of partition ... completed successfully。这是判断迁移是否结束的官方手段。同时建议看一下每个分区的ISR状态kafka-topics.sh --bootstrap-server 旧节点1:9092 --describe \ --topic fa-news --under-replicated-partitions如果--under-replicated-partitions一直有输出说明有副本在追赶中ISR没有完全同步。这属于迁移进行中的正常现象但要注意如果持续很久没有完成估计是限速设置得太低或者新节点的磁盘IO跟不上。迁移期间Kafka会优先保证已有的ISR副本不丢失。只要min.insync.replicas配置合理客户端写入acksall时不会丢数据但副本迁出过程中如果某台机器硬件异常在线业务的数据可用性可能短暂下降。所以扩容窗口尽量选择业务低峰并确保至少有2个副本是同步状态。生产集群我一般要求min.insync.replicas2。5. 迁移后的负载均衡细化让新Broker真正发挥作用把reassignment JSON执行完看起来任务完成了但Kafka集群的扩容工作并没有结束。如果你只迁了一部分大Topic集群的Broker负载仍然可能严重不均。5.1 按主题迁移和按分区迁移的选择我在实战中一般分两类场景如果只有少数几个核心Topic占据大头那就只对“大头”主题生成迁移计划。如果集群主题很多、单主题很小但整体数据量大我会直接选择“迁移所有主题”这时更推荐用--topics-to-move-json-file配合--broker-list让Kafka自己算全局均衡。不过--topics-to-move-json-file的全局均衡只考虑了“分区副本在Broker上的数量”并没有考虑分区大小和装载不均匀问题。我在一次扩容里就遇到过主题A分区非常庞大生成计划把主题A的两个副本分到了同一块磁盘上结果迁移完后那块磁盘空间依然告警。解决思路是在生成计划后人工审查分区与磁盘的对应关系手动调整reassignment.json里的replicas排列或者干脆用分区级别的JSON文件自己指定哪些分区放在哪几台Broker上。比如我只想把fa-news-0迁到[0, 3, 5]{ version: 1, partitions: [ {topic: fa-news, partition: 0, replicas: [0, 3, 5]} ] }这种手动模式更精细但要求你清楚了解当前每个分区的分布。我们通常先用kafka-topics.sh --describe导出当前分布然后结合磁盘使用率来定制。5.2 迁移效果的验证指标迁移完成后我会用下面几个指标做最终验收kafka-topics.sh --describe查看每个分区的Leader列是否覆盖到新节点防止新节点全是Follower、没有Leader那样写入流量仍然打不到新节点上。查看每个Broker的log.dirs目录的大小。Kafka没有现成的CLI直接展示目录占用我会用du -sh /data0/kafka-logs/*汇总对比。观察新节点的bytes-in-per-sec和bytes-out-per-sec确认有实际读写流量而不是只做数据备份。再次执行kafka-reassign-partitions.sh --verify确认没有正在进行的reassignment任务。迁移完成后如果新Broker参与了Leader和Follower的角色但客户端连接仍然偏爱旧节点可能还需要调整客户端的bootstrap.servers和元数据更新逻辑。Kafka客户端会定期刷新元数据一般不需要手动重连但如果客户端配置了很长的metadata.max.age.ms新分区映射可能感知较慢可以把该参数适当调低。5.3 调优客户端参数与后续观察扩容完成后并非一劳永逸我倾向于做一次“扩容后一周观察”重点看新节点是否产生了不均衡热点比如某个Broker的分区Leader特别多。Kafka分区Leader自动均衡有没有触发。默认情况下它基于leader.imbalance.check.interval.seconds默认300秒触发条件是Leader分布不均超过一定比例。但它的迁移粒度有限如果发现Leader还是偏斜严重我会手动触发一次Leader重选举bin/kafka-leader-election.sh \ --bootstrap-server 旧节点1:9092 \ --topic fa-news --partition 0 --election-type preferred或者干脆对所有分区执行一次preferred leader恢复让分配列表里的第一个Broker优先成为Leader。这样写入流量能更好地分散到新节点上。确认没有因为Kafka版本问题导致的可视化监控数据异常。很多人喜欢装Kafka UI工具来观察集群常用的像Kafka UI、Offset Explorer、Burrow等。扩容后如果这些工具展示的节点数或分区分布不对多半是工具自身缓存刷新一下元数据就好。注意不要因为UI数据不准误判扩容失败。6. 扩容过程中的常见坑与我的实操建议最后这部分我想把几次踩坑的真实经历拉出来讲。有些问题说出来很“低级”可线上高压状态下人就是容易栽在低级错误上。6.1 三个真实踩坑案例第一个坑broker.id冲突。当时老集群有两个Broker的ID是3和4新节点配置时我想当然给成了3一启动新节点直接把自己注册成了已有Broker整个集群出现元数据错乱很多客户端报LeaderNotAvailableException。处理办法是立刻停掉新节点清掉它写进ZK的临时节点再把ID改回5重新启动。耗时一小时实际上是十分钟就能做完的事因为排查“为什么集群全乱了”花了大半天。第二个坑迁移限速参数设置不当新节点拉取太久。那次我把--throttle设成了 1MB/s担心影响线上结果一场迁移跑了两天没跑完。ISR里的新副本迟迟追不上期间又把在线流量挤到了一台Leader上反而变得更卡。后来把限速提到 100MB/s一个晚上就迁移完了。所以限速要设但不能设得太“怂”最好根据Broker网卡带宽测试一个安全值比如总带宽的30%-50%。第三个坑忘记处理旧Broker下线。迁移完成后个别旧节点的磁盘上仍然残留着分区目录但副本已经不在Broker列表里。如果不清理磁盘占用会一直虚高。官方地清理方式是重新分配某个分区的副本到旧节点之外后旧节点会自动删除这些分区如果没删需要手工确认没有分区引用了这些目录再删除目录释放空间。这个操作最好确认该分区不在kafka-topics.sh --describe的副本列表里否则误删会出大问题。6.2 我推荐的扩容流程模板结合这些实际操作我整理了一份精简的流程模板留给团队和后来的人用记录当前集群基线主题列表、分区副本分布、每个Broker磁盘占用、ISR状态。审批并购买新机器安装和现有集群一致版本的Kafka。配置server.properties重点是 broker.id、listeners、advertised.listeners、log.dirs、安全协议。启动新节点用kafka-broker-api-versions.sh和日志确认节点加入集群。用kafka-reassign-partitions.sh --generate生成目标分配方案人工检查是否有明显不均衡。保存reassignment JSON执行--execute并设置合理的--throttle。轮询--verify并同时观察UnderReplicatedPartitions和Broker网络/磁盘指标。迁移完成后手动触发preferred leader选举实现Leader均衡。用du等工具对比各Broker数据目录大小确认磁盘均衡。把旧节点的空目录清理干净并归档本次的迁移JSON方便日后做同样的操作。这套流程我前后在几个集群上用过稳定性很高。唯一要调整的是“迁移窗口”如果你业务有明确低峰期把--execute放在低峰开始之前让迁移在路上跑着低峰过去之后基本就完成了。6.3 最后扩容这件事本质上是在跟“数据位置”打交道。新增Broker只是一个开始数据迁移才是让成本产生价值的关键动作。如果你能在扩容前认真盘点指标严格控制迁移限流并在迁移后重新平衡Leader与副本分布整个集群的吞吐和稳定性通常会有明显改善。我在实际项目中见过不少团队扩容完后发现新节点几乎没有存量数据那台机器纯粹成了“备胎”最后还得重新组织一次迁移把数据搬过去白白增加一次风险。希望这篇实战小结能帮你少走这一步弯路。
返回列表