写点什么

Kafka 负载均衡在 vivo 的落地实践

  • 2022 年 6 月 06 日
  • 本文字数:7446 字

    阅读完需:约 24 分钟

vivo 互联网服务器团队-You Shuo


副本迁移是 Kafka 最高频的操作,对于一个拥有几十万个副本的集群,通过人工去完成副本迁移是一件很困难的事情。Cruise Control 作为 Kafka 的运维工具,它包含了 Kafka 服务上下线、集群内负载均衡、副本扩缩容、副本缺失修复以及节点降级等功能。显然,Cruise Control 的出现,使得我们能够更容易的运维大规模 Kafka 集群。


备注:本文基于 Kafka 2.1.1 开展。


一、  Kafka 负载均衡

1.1 生产者负载均衡

Kafka 客户端可以使用分区器依据消息的 key 计算分区,如果在发送消息时未指定 key,则默认分区器会基于 round robin 算法为每条消息分配分区;

否则会基于 murmur2 哈希算法计算 key 的哈希值,并与分区数取模的到最后的分区编号。

很显然,这并不是我们要讨论的 Kafka 负载均衡,因为生产者负载均衡看起来并不是那么的复杂。

1.2 消费者负载均衡

考虑到消费者上下线、topic 分区数变更等情况,KafkaConsumer 还需要负责与服务端交互执行分区再分配操作,以保证消费者能够更加均衡的消费 topic 分区,从而提升消费的性能;

Kafka 目前主流的分区分配策略有 2 种(默认是 range,可以通过 partition.assignment.strategy 参数指定):

  • range: 在保证均衡的前提下,将连续的分区分配给消费者,对应的实现是 RangeAssignor;

  • round-robin:在保证均衡的前提下,轮询分配,对应的实现是 RoundRobinAssignor;

  • 0.11.0.0 版本引入了一种新的分区分配策略 StickyAssignor,其优势在于能够保证分区均衡的前提下尽量保持原有的分区分配结果,从而避免许多冗余的分区分配操作,减少分区再分配的执行时间。

无论是生产者还是消费者,Kafka 客户端内部已经帮我们做了负载均衡了,那我们还有讨论负载均衡的必要吗?答案是肯定的,因为 Kafka 负载不均的主要问题存在于服务端而不是客户端。

二、 Kafka 服务端为什么要做负载均衡

我们先来看一下 Kafka 集群的流量分布(图 1)以及新上线机器后集群的流量分布(图 2):


图 1

图 2

从图 1 可以看出资源组内各 broker 的流量分布并不是很均衡,而且由于部分 topic 分区集中分布在某几个 broker 上,当 topic 流量突增的时候,会出现只有部分 broker 流量突增。

这种情况下,我们就需要扩容 topic 分区或手动执行迁移动操作。

图 2 是我们 Kafka 集群的一个资源组扩容后的流量分布情况,流量无法自动的分摊到新扩容的节点上。此时,就需要我们手动的触发数据迁移,从而才能把流量引到新扩容的节点上。

2.1  Kafka 存储结构

为什么会出现上述的问题呢?这个就需要从 Kafka 的存储机制说起。

下图是 Kafka topic 的存储结构,其具体层级结构描述如下:

  1. 每个 broker 节点可以通过 logDirs 配置项指定多个 log 目录,我们线上机器共有 12 块盘,每块盘都对应一个 log 目录。

  2. 每个 log 目录下会有若干个[topic]-[x]字样的目录,该目录用于存储指定 topic 指定分区的数据,对应的如果该 topic 是 3 副本,那在集群的其他 broker 节点上会有两个和该目录同名的目录。

  3. 客户端写入 kafka 的数据最终会按照时间顺序成对的生成.index、.timeindex、.snapshot 以及.log 文件,这些文件保存在对应的 topic 分区目录下。

  4. 为了实现高可用目的,我们线上的 topic 一般都是 2 副本/3 副本,topic 分区的每个副本都分布在不同的 broker 节点上,有时为了降低机架故障带来的风险,topic 分区的不同副本也会被要求分配在不同机架的 broker 节点上。



了解完 Kafka 存储机制之后,我们可以清晰的了解到,客户端写入 Kafka 的数据会按照 topic 分区被路由到 broker 的不同 log 目录下,只要我们不人工干预,那每次路由的结果都不会改变。因为每次路由结果都不会改变,那么问题来了

随着 topic 数量不断增多,每个 topic 的分区数量又不一致,最终就会出现 topic 分区在 Kafka 集群内分配不均的情况。

比如:topic1 是 10 个分区、topic2 是 15 个分区、topic3 是 3 个分区,我们集群有 6 台机器。那 6 台 broker 上总会有 4 台 broker 有两个 topic1 的分区,有 3 台 broke 上有 3 个 topic3 分区等等。

这样的问题就会导致分区多的 broker 上的出入流量可能要比其他 broker 上要高,如果要考虑同一 topic 不同分区流量不一致、不同 topic 流量又不一致,再加上我们线上有 7000 个 topic、13 万个分区、27 万个副本等等这些。

这么复杂的情况下,集群内总会有 broker 负载特别高,有的 broker 负载特别低,当 broker 负载高到一定的时候,此时就需要我们的运维同学介入进来了,我们需要帮这些 broker 减减压,从而间接的提升集群总体的负载能力。

集群整体负载都很高,业务流量会持续增长的时候,我们会往集群内扩机器。有些同学想扩机器是好事呀,这会有什么问题呢?问题和上面是一样的,因为发往 topic 分区的数据,其路由结果不会改变,如果没有人工干预的话,那新扩进来机器的流量就始终是 0,集群内原来的 broker 负载依然得不到减轻。

三、如何对 Kafka 做负载均衡

3.1 人工生成迁移计划和迁移

如下图所示,我们模拟一个简单的场景,其中的 T0-P0-R0 表示 topic-分区-副本,假设 topic 各分区流量相同,假设每个分区 R0 副本是 leader。

我们可以看到,有两个 topic T0 和 T1,T0 是 5 分区 2 副本(出入流量为 10 和 5),T1 是 3 分区 2 副本(出入流量为 5 和 1),如果严格考虑机架的话,那 topic 副本的分布可能如下:



假设我们现在新扩入一台 broker3(Rack2),如下图所示:由于之前考虑了 topic 在机架上的分布,所以从整体上看,broker2 的负载要高一些。



我们现在想把 broker2 上的一些分区迁移到新扩进来的 broker3 上,综合考虑机架、流量、副本个数等因素,我们将 T0-P2-R0、T0-P3-R1、T0-P4-R0、T1-P0-R1 四个分区迁移到 broker3 上。



看起来还不是很均衡,我们再将 T1-P2 分区切换一下 leader:



经历一番折腾后,整个集群就均衡许多了,关于上面迁移副本和 leader 切换的命令参考如下:


Kafka 副本迁移脚本


# 副本迁移脚本:kafka-reassign-partitions.sh# 1. 配置迁移文件$ vi topic-reassignment.json{"version":1,"partitions":[{"topic":"T0","partition":2,"replicas":[broker3,broker1]},{"topic":"T0","partition":3,"replicas":[broker0,broker3]},{"topic":"T0","partition":4,"replicas":[broker3,broker1]},{"topic":"T1","partition":0,"replicas":[broker2,broker3]},{"topic":"T1","partition":2,"replicas":[broker2,broker0]}]}# 2. 执行迁移命令bin/kafka-reassign-partitions.sh --throttle 73400320 --zookeeper zkurl --execute --reassignment-json-file topic-reassignment.json# 3. 查看迁移状态/清除限速配置bin/kafka-reassign-partitions.sh --zookeeper zkurl --verify --reassignment-json-file topic-reassignment.json
复制代码

3.2 使用负载均衡工具-cruise control

经过对 Kafka 存储结构、人工干预 topic 分区分布等的了解后,我们可以看到 Kafka 运维起来是非常繁琐的,那有没有一些工具可以帮助我们解决这些问题呢?

答案是肯定的。

cruise control 是 LinkedIn 针对 Kafka 集群运维困难问题而开发的一个项目,cruise control 能够对 Kafka 集群各种资源进行动态负载均衡,这些资源包括:CPU、磁盘使用率、入流量、出流量、副本分布等,同时 cruise control 也具有首选 leader 切换和 topic 配置变更等功能。

3.2.1 cruise cotnrol 架构

我们先简单介绍下 cruise control 的架构。

如下图所示,其主要由 Monitor、Analyzer、Executor 和 Anomaly Detector 四部分组成:

(来源:cruise control 官网


(1)Monitor

Monitor 分为客户端 Metrics Reporter 和服务端 Metrics Sampler:

  • Metrics Reporter 实现了 Kafka 的指标上报接口 MetricsReporter,以特定的格式将原生的 Kafka 指标上报到 topic __CruiseControlMetrics 中。

  • Metrics Sampler 从__CruiseControlMetrics 中获取原生指标后按照 broker 和分区级指标分别进行聚合,聚合后的指标包含了 broker、分区负载的均值、最大值等统计值,这些中间结果将被发送 topic __KafkaCruiseControlModelTrainingSamples 和__KafkaCruiseControlPartitionMetricSamples 中;


(2)Analyzer

Analyzer 作为 cruise control 的核心部分,它根据用户提供的优化目标和基于 Monitor 生成的集群负载模型生成迁移计划。

在 cruise control 中,“用户提供的优化目标”包括硬性目标和软性目标两大类,硬性目标是 Analyzer 在做预迁移的时候必须满足的一类目标(例如:副本在迁移后必须满足机架分散性原则),软性目标则是尽可能要达到的目标,如果某一副本在迁移后只能同时满足硬性目标和软性目标中的一类,则以硬性目标为主,如果存在硬性目标无法满足的情况则本次分析失败。

Analyzer 可能需要改进的地方:

  1. 由于 Monitor 生成的是整个集群的负载模型,我们的 Kafka 平台将 Kafka 集群划分为多个资源组,不同资源组的资源利用率存在很大差别,所以原生的集群负载模型不再适用于我们的应用场景。

  2. 大多数业务没有指定 key 进行生产,所以各分区的负载偏差不大。如果 topic 分区副本均匀分布在资源组内,则资源组也随之变得均衡。

  3. 原生的 cruise control 会从集群维度来展开均衡工作,指定资源组后可以从资源组维度展开均衡工作,但无法满足跨资源组迁移的场景。


(3)Executor

Executor 作为一个执行者,它执行 Analyzer 分析得到的迁移计划。它会将迁移计划以接口的形式分批提交到 Kafka 集群上,后续 Kafka 会按照提交上来的迁移脚本执行副本迁移。

Executor 可能需要改进的地方:

cruise control 在执行副本迁移类的功能时,不能触发集群首选 leader 切换:有时在集群均衡过程中出现了宕机重启,以问题机器作为首选 leader 的分区,其 leader 不能自动切换回来,造成集群内其他节点压力陡增,此时往往会产生连锁反应。


(4)Anomaly Detector

Anomaly Detector 是一个定时任务,它会定期检测 Kafka 集群是否不均衡或者是否有副本缺失这些异常情况,当 Kafka 集群出现这些情况后,Anomaly Detector 会自动触发一次集群内的负载均衡。

在后面的主要功能描述中,我会主要介绍 Monitor Analyzer 的处理逻辑。

3.2.2 均衡 broker 出入流量 / 机器上下线均衡

对于 Kafka 集群内各 broker 之间流量负载不均的原因、示意图以及解决方案,我们在上面已经介绍过了,那么 cruise control 是如何解决这个问题的

其实 cruise control 均衡集群的思路和我们手动去均衡集群的思路大体一致,只不过它需要 Kafka 集群详细的指标数据,以这些指标为基础,去计算各 broker 之间的负载差距,并根据我们关注的资源去做分析,从而得出最终的迁移计划。

以 topic 分区 leader 副本这类资源为例:

服务端在接收到均衡请求后,Monitor 会先根据缓存的集群指标数据构建一个能够描述整个集群负载分布的模型。

下图简单描述了整个集群负载信息的生成过程,smaple fetcher 线程会将获取到的原生指标加载成可读性更好的 Metric Sample,并对其进行进一步的加工,得到带有 brokerid、partition 分区等信息的统计指标,这些指标保存在对应 broker、replica 的 load 属性中,所以 broker 和 repilca 会包含流量负载、存储大小、当前副本是否是 leader 等信息。



Analyzer 会遍历我们指定的 broker(默认是集群所有的 broker),由于每台 broker 及其下面的 topic 分区副本都有详细的指标信息,分析算法直接根据这些指标和指定资源对 broker 进行排序。

本例子的资源就是 topic 分区 leader 副本数量,接着 Analyzer 会根据我们提前设置的最大/最小阈值、离散因子等来判断当前 broker 上某 topic 的 leader 副本数量是否需要增加或缩减,如果是增加,则变更 clustermodel 将负载比较高的 broker 上对应的 topic leader 副本迁移到当前 broker 上,反之亦然,在后面的改造点中,我们会对 Analyzer 的工作过程做简单的描述。

遍历过所有 broker,并且针对我们指定的所有资源都进行分析之后,就得出了最终版的 clustermodel,再与我们最初生成的 clustermodel 对比,就生成了 topic 迁移计划。

cruise control 会根据我们指定的迁移策略分批次的将 topic 迁移计划提交给 kafka 集群执行。

迁移计划示意图如下:

3.2.3 首选 leader 切换

切换非首选 leader 副本,迁移计划示意图如下:


3.2.4 topic 配置变更

改变 topic 副本个数,迁移计划示意图如下:


3.3  改造 cruise control

3.3.1 指定资源组进行均衡

当集群规模非常庞大的时候,我们想要均衡整个集群就变得非常困难,往往均衡一次就需要半个月甚至更长时间,这在无形之中也加大了我们运维同学的压力。

针对这个场景,我们对 cruise control 也进行了改造,我们从逻辑上将 Kafka 集群划分成多个资源组,使得业务拥有自己的资源组,当某个业务出现流量波动的时候,不会影响到其他的业务。

通过指定资源组,我们每次只需要对集群的一小部分或多个部分进行均衡即可,大大缩短了均衡的时间,使得均衡的过程更加可控。

改造后的 cruise control 可以做到如下几点:

  1. 通过均衡参数,我们可以只均衡某个或多个资源组的 broker。

  2. 更改 topic 配置,比如增加 topic 副本时,新扩的副本需要和 topic 原先的副本在同一个资源组内。

  3. 在资源组内分析 broker 上的资源是迁入还是迁出。对于每一类资源目标,cruise control 是计算资源组范围内的统计指标,然后结合阈值和离散因子来分析 broker 是迁出资源还是迁入资源。

如下图所示,我们将集群、资源组、以及资源组下的 topic 这些元数据保存在数据库中,Analyzer 得以在指定的资源组范围内,对每个 broker 按照资源分布目标做均衡分析。

例如:当对 broker-0 做均衡分析的时候,Analyzer 会遍历 goals 列表,每个 goals 负责一类资源负载目标(cpu、入流量等),当均衡分析到达 goal-0 的时候,goal-0 会判断 broker-0 的负载是否超出上限阈值,如果超出,则需要将 broker-0 的一些 topic 副本迁移到负载较低的 broker 上;反之,则需要将其他 broker 上的副本迁移到 broker-0 上。

其中,下面的 recheck goals 是排在后面的 goal 在做均衡分析的时候,在更新 cluster model 之前会判断本次迁移会不会与之前的 goal 冲突,如果冲突,那就不更新 cluster model,当前的 goal 会继续尝试往其他 broker 上迁移,直到找到适合的迁移目标,然后更新 cluster model。


3.3.2 topic/topic 分区往指定 broker 上迁移

考虑这些场景:

  1. 一个项目下会有几个资源组,由于业务变更,业务想要把 A 资源组下的 topic 迁移到 B 资源组。

  2. 业务想要把公共资源组的 topic 迁移到 C 资源组。

  3. 均衡完成之后,发现总有几个 topic/分区分布不是很均匀。


面对这些场景,我们上面指定资源组进行均衡的功能就满足不了我们的需求了。所以,我们针对上述场景改造后的 cruise control 可以做到如下几点:

  1. 只均衡指定的 topic 或 topic 分区;

  2. 均衡的 topic 或 topic 分区只往指定的 broker 上迁移。

3.3.3 新增目标分析——topic 分区 leader 副本分散性

业务方大多都是没有指定 key 进行发送数据的,所以同一 topic 每个分区的流量、存储都是接近的,即每一个 topic 的各个分区的 leader 副本尽可能均匀的分布在集群的 broker 上时,那集群的负载就会很均匀。

有同学会问了,topic 分区数并不总是能够整除 broker 数量,那最后各 broker 的负载不还是不一致嘛?

答案是肯定的,只通过分区的 leader 副本还不能做到最终的均衡。

针对上述场景改造后的 cruise control 可以做到如下几点:

  1. 新增一类资源分析:topic 分区 leader 副本分散性。

  2. 首先保证每个 topic 的 leader 副本和 follower 副本尽可能的均匀分布在资源组的 broker 上。

  3. 在 2 的基础上,副本会尽可能的往负载较低的 broker 上分布。

如下图所示,针对每一个 topic 的副本,Analyzer 会依次计算当前 broker 的 topic leader 数是否超过阈值上限,如果超过,则 Analyzer 会按照 topic 的 leader 副本数量、topic 的 follower 副本数量、broker 的出流量负载等来选出 AR 中的 follower 副本作为新的 leader 进行切换,如果 AR 副本中也没有符合要求的 broker,则会选择 AR 列表以外的 broker。

3.3.4 最终均衡效果

下图是某个资源组均衡后的流量分布,各节点间流量偏差非常小,这种情况下,既可以增强集群扛住流量异常突增的能力又可以提升集群整体资源利用率和服务稳定性,降低成本。



3.4 安装/部署 cruise control

3.4.1 客户端部署:指标采集

【步骤 1】:创建 Kafka 账号,用于后面生产和消费指标数据

【步骤 2】:创建 3 个 Kafka 内部 topic:a 是用来存储 Kafka 服务原生 jmx 指标;b 和 c 分别是用来存储 cruise control 处理过后的分区和模型指标;

【步骤 3】:给步骤 1 创建的账号授予读/写以及集群的操作权限,用于读/写步骤 2 创建的 topic;

【步骤 4】:修改 kafka 的 server.properties,增加如下配置:

在 Kafka 服务上配置采集程序

# 修改kafka的server.propertiesmetric.reporters=com.linkedin.kafka.cruisecontrol.metricsreporter.CruiseControlMetricsReportercruise.control.metrics.reporter.bootstrap.servers=域名:9092 cruise.control.metrics.reporter.security.protocol=SASL_PLAINTEXTcruise.control.metrics.reporter.sasl.mechanism=SCRAM-SHA-256cruise.control.metrics.reporter.sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username=\"ys\" password=\"ys\";
复制代码

【步骤 5】:添加 cruise-control-metrics-reporter 的 jar 包到 Kafka 的 lib 目录下:mv cruise-control-metrics-reporter-2.0.104-SNAPSHOT.jar kafka_dir/lib/;

【步骤 6】:重启 Kafka 服务。

3.4.2 服务端部署:指标聚合/均衡分析

(1)到https://github.com/linkedin/cruise-control 下载zip文件并解压

(2)将自己本地 cruise control 子模块下生成的 jar 包替换 cruise control 的:mv cruise-control-2.0.xxx-SNAPSHOT.jar cruise-control/build/libs;


(3)修改 cruise control 配置文件,主要关注如下配置:

# 修改cruise control配置文件security.protocol=SASL_PLAINTEXTsasl.mechanism=SCRAM-SHA-256sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username=\"ys\" password=\"ys\";bootstrap.servers=域名:9092zookeeper.connect=zkURL
复制代码

(4)修改数据库连接配置:

# 集群idcluster_id=xxx  db_url=jdbc:mysql://hostxxxx:3306/databasexxxdb_user=xxxdb_pwd=xxx
复制代码

四、总结

通过以上的介绍,我们可以看出 Kafka 存在比较明显的两个缺陷:

  1. Kafka 每个 partition replica 与机器的磁盘绑定,partition replica 由一系列的 Segment 组成,所以往往单分区存储会占用比较大的磁盘空间,对于磁盘会有很大压力。

  2. 在集群扩容 broker 时必须做 Rebalance,需要 broker 有良好的执行流程,保证没有任何故障的情况下使得各 broker 负载均衡。

cruise control 就是针对 Kafka 集群运维困难问题而诞生的,它能够很好的解决 kafka 运维困难的问题。


参考文章:

  1.  linkedIn/cruise-control

  2.  Introduction to Kafka Cruise Control

  3.  Cloudera Cruise Control REST API Reference

  4.  http://dockone.io/article/2434664

  5. . https://www.zhenchao.org/2019/06/22/kafka/kafka-log-manage/

发布于: 刚刚阅读数: 3
用户头像

官方公众号:vivo互联网技术,ID:vivoVMIC 2020.07.10 加入

分享 vivo 互联网技术干货与沙龙活动,推荐最新行业动态与热门会议。

评论

发布
暂无评论
Kafka 负载均衡在 vivo 的落地实践_大数据_vivo互联网技术_InfoQ写作社区