背景
线上kafka集群,3台机器,3个broker;其中某台机器因为硬件故障,需要停机维修;停机意味这跑在机器上的服务会停止。所以本次做kafka迁移的目标 是机器可以停止但依赖kafka的上游和下游业务可不能停止,因为所属行业的特殊性,服务的停止,对业务的影响和伤害还蛮大的。
分析
我们知道kafka是有高可用机制的。kafka的高可用机制,是靠分区多副本来保证的 :某个topic 的 leader分区挂了,kafka会从其它follower分区里,选择一个分区做为leader分区,继续对生产者和消费者提供读写服务。所以理论上,我们是可以停止某台机器上的kafka broker服务;kafka的broker 协调节点会自动切换其它follower分区为leader分区。
大概的过程是这样的,切换前:
切换后:
网上理论分析基本是这样了,但是以个人多年实战研发经验来看,每次的平衡迁移都不会是一次简单的事。从图上可以看到,机器24上以前没有这些leader分区,只有follower分区,那么意味这24上其实是没有这么多生产者和消费者连接的,如果把leader分区转移到24上,那么24的cpu,内存,网络,硬盘IO能支持吗?这个是实际生产上要考虑的一个风险点。
测试环境验证
测试环境搭建了一套和线上一样的环境3台机器3个broker;经过模拟,3分区,2副本的情况下,某个broker宕机情况对生产者和消费者的影响
kafka leader分区的切换,看起来对生产者和消费者都比较的友好,而且还不会丢消息。前提是有follower分区
现实很残酷
线上kafka集群的 topic分区是几个副本集了?是否都如我们设想的一样都有follwer分区了?最小同步副本集min.insync.replicas 的配置是多少了?
如果都是2个或者3个副本集就好了,说明我们的topic分区都是高可用的;但是如果只有一个副本集,就意味这只有leader分区,而没有follower分区;那么topic的分区是不满足高可用的。
通过统计zk上的brokerstopics节点;发现有100+个topic分区是3分片1副本。此时心理有一万匹XXX经过。
怎么办了?
通过查看官方文档资料:
kafka.apache.org/0102/docume…
大概有两种方法
方法1: leader分区直接迁移到另外两台机器,23机器修好后,再把这些topic 迁移回来;但是
这些topic还是没有副本集,不保证这些topic的高可用
方法2: 对这些主题增加副本集,先保证其分区高可用;然后23机器broker 优雅停机;通过优雅停机的方式,把leader分区, 切换到另外两台机器上。
经过大家的沟通,我们选择了方案2,理由是:
这些1副本的topic,大多数是核心业务的topic;当时建分区的时候,可能是由于未考虑到topic 分区的高可用,只选择了1副本,可以趁这本次的迁移,把这些topic的副本给建立起来,让kafka topic具备高可用;
另外一个理由是经过初步的评估,24和25的机器还有较多的,CPU,内存,网络,IO利用率等硬件资源 支持副本的扩充
两种抉择-----leader分区切换方案选择
通过调研我们知道,leader分区切换有两种时机
-
第一种:靠broker 优雅停机机制,把leader分区全量切换另外两台机器上
-
第二种:手工增量的方式对leader分区和follower分区进行切换,然后broker优雅停机
这两种方案最后的结果,都能让leader分区切换到follower分区上;两种切换方法,在本质上的不同是:时机1 是全量切换;时机2 是增量切换。
对于时机1来说 优点:
操作方便简单;需要的研发耗时和资源少;
缺点也很明显: 虽然有24和25的硬件资源初步评估,但全量切换后两台机器是否真的能抗住压力了?切换后要是有问题,还能切换回来吗?全量切换后的最终结果和预期可能会存在较大差异
对于时机2 来说,优点:
增量切换能够解决一刀切的问题,并且在增量切换时,发现 24和25机器资源快扛不住时,中途可以立刻停止,整个的切换过程更加可控,即使业务有问题,能及时止损;
缺点也很明显: 由于缺乏kafka等专业领域的技术人才,公司kafka的监控和运维体系一直没有搭建起来,都是通过手工命令行的方式 来进行监控和相关命令的执行,所以这种增量切换需要编写多个脚本和多次的执行;中间执行过程的风险也有;对整个过程的监控和研发时间的投入相对时机1来说,要增加很多。
经过和架构师,团队负责人的沟通,我们采用了时机2;但是副本集的增加和leader分区切换的脚本;可通过编写代码的方式生成,避免手工编写出错的概率;另外生成的脚本由2位同事负责review;先小批量执行,观察情况;没有问题,继续执行剩余的脚本。
实施步骤
手工副本集增加步骤
主要是通过kafka的kafka-reassign-partitions.sh 工具,进行副本集的增加。
1、查看当前topic副本集情况
kafka-topics.sh --describe --zookeeper XXX:2181/data/kafka --topic test-order
这里的/data/kafka是kafka 集群元数据 存入到zk的路径;
108,109,110是brokerId。
即.23机器的 brokerId是109
.24机器的 brokerId是108
.25机器的 brokerId是110
2、新建副本集扩充脚本。新建 reassign.json,把以下内容增加到文件
{"version":1,"partitions":[{"topic":"test-order","partition":0,"replicas":[108,109]},{"topic":"test-order","partition":1,"replicas":[110,109]},{"topic":"test-order","partition":2,"replicas":[108,110]}]}
简单说明下:结合第一步和脚本的内容可看出,分区0的扩充副本,增加了108 follower分区,分区1的扩充副本增加了109 follower分区,分区2的扩充副本,增加了 110 follower分区;这里的 109 110 108是brokder Id
3、执行,副本集扩充复制集方案
kafka-reassign-partitions.sh --zookeeper XXX:2181/data/kafka --execute --reassignment-json-file reassign.json --throttle 1048576
--throttle 是分区增加时的限流,单位为B/S;整理是1M/S的速度
输出
Current partition replica assignment
{"version":1,"partitions":[{"topic":"test-order","partition":1,"replicas":[110]},{"topic":"test-order","partition":0,"replicas":[109]},{"topic":"test-order","partition":2,"replicas":[108]}]}
Save this to use as the --reassignment-json-file option during rollback
Warning: You must run Verify periodically, until the reassignment completes, to ensure the throttle is removed. You can also alter the throttle by rerunning the Execute command passing a new value.
The throttle limit was set to 1048576 B/s
Successfully started reassignment of partitions.
4、 验证是否执行成功---注意会把限流设置也删除
kafka-reassign-partitions.sh --zookeeper XXX:2181/data/kafka --verify --reassignment-json-file reassign.json
输出
Status of partition reassignment:
Reassignment of partition [test-order,0] completed successfully
Reassignment of partition [test-order,1] completed successfully
Reassignment of partition [test-order,2] completed successfully
Throttle was removed.
5、再次查看当前topic副本集情况
kafka-topics.sh --zookeeper XXX:2181/data/kafka --topic test-order --describe
手工leader分区切换步骤
主要是利用了kafka的优先副本集选举;leader分区按照副本集的顺序进行选举;上面扩副本分区脚本时,故意把108 follower分区写在leader分区前面
1、创建 election.json 指定topic 需要调整的优化副本内容
{
"partitions": [{
"partition": 0,
"topic": "test-order"
}]
}
2、执行优化副本集选举脚本
kafka-preferred-replica-election.sh --zookeeper XXX:2181/data/kafka --path-to-json-file election.json
输出
Created preferred replica election path with {"version":1,"partitions":[{"topic":"test-order","partition":0}]}
Successfully started preferred replica election for partitions Set([test-order,0])
3、查看当前topic是否切换leader
结合副本集增加的第五步和手工leader分区切换第三步,可看到 分区0的leader 分区由109切换到了108上;所以.23上 109的leader分区变为了follower分区,而.24上的 108 follower分区变为了leader分区
4、查看产者和消费者,是否有丢消息
从生产者的日志看到,在经过自动重试后,消息能发到新的分区上;消费者也能自动进行leader分区切换,并且能继续消费消息
总结
本次kafka broker停服机器维修的本质,从应用技术的角度看,是对生产环境kafka集群 高可用的一次检阅。而这次检阅是被动的一次检阅,并不是由研发主动发起的。被动检阅,有点类似搞突击检测,但我们比突击检测好的是,还可以有足够的时间为了kafka集群具备高可用,做必要的自检工作,准备工作,验证工作;也把这么多年欠的kafka高可用的技术债给还了。
kafka集群本身是具备高可用机制的;但并不意味着你实际部署的kafka集群就具备业务高可用。生产环境kafka具备业务高可用,和使用kafka的姿势有关。这些姿势包括:
1、topic对应的分区是否有多副本
2、多副本最小同步数要求 即min.insync.replicas的配置,考虑好性能和可用性的tradeoff
你的kafka生产环境是如何做类似迁移和切换的了?欢迎留言评价。