logo

Kafka 重平衡挑战及 AutoMQ 的解决方案

Published on

引言

如果你曾经在公司管理过 Kafka 部署,你很可能做过跨集群的分区重组工作。由于 Kafka 采用紧密耦合的计算和存储设置,当集群成员发生变化(比如添加或删除 broker)或用户想要在 broker 之间进行负载均衡时,必须在 broker 之间重新分配分区副本,这会导致数据迁移。

Kafka 提供了一个用于处理重分配过程的脚本,但它需要用户干预,且在规划方面需要更强的稳健性。像 Cruise Control 这样的工具已经介入,基于集群状态提供自动副本平衡,并提供更复杂的重分配计划。

然而,数据迁移的问题仍然存在。本文将探讨 AutoMQ 如何解决 Kafka 的重平衡挑战。

AutoMQ 是一个云原生解决方案,提供 100% 的 Kafka 兼容性,同时将数据完全存储在对象存储上。这种方法提供了一个高成本效益的 Kafka 替代方案,而不会牺牲低延迟和高吞吐量性能。更重要的是,你永远不需要在 broker 之间传输数据。

Kafka 分区基础

让我首先回顾一下 Kafka 的核心概念:

  • Kafka 的数据单位是消息message
  • 消息在 Kafka 中被组织成主题topic
  • 你可以把消息想象成数据库系统中的行,把主题想象成表
  • 一个主题被分成多个分区partition
Kafka 分区示意图

每个主题的分区对应一个逻辑日志。在物理上,日志是由一组大小大致相同的段文件(例如 1GB)实现的。每当一条消息被写入分区时,broker 就会将该消息追加到最后一个段文件中。

Kafka 日志结构

为了确保数据的持久性和可用性,分区被复制到可配置数量的 broker(副本因子)。

Kafka 副本分布

这有助于在 broker 发生故障时自动故障转移副本,确保消息在出现故障时仍然可用。每个 Kafka 分区通常有一个主副本leader)和零个或多个从副本follower)。所有写入操作必须送到分区的主副本,而读取操作可以由主副本或分区的从副本提供服务。

Kafka 会以轮询方式在集群中分配分区副本,以避免将高流量主题的所有分区放在少数节点上。

副本重分配在 Kafka 中

考虑到副本是分布在整个集群中的,当现有的 broker 宕机或添加新的 broker 时会发生什么?Kafka 副本需要重新分配。假设我们有三个 broker 和两个分区,每个分区有两个副本:

Kafka 副本重分配示例

如果一个 broker 发生故障,Kafka 会自动将该 broker 作为 leader 的所有分区的领导权重新分配给持有副本的其他 broker。Kafka 最终可能会在其他可用的 broker 上为这些分区创建新的副本,以维持复制因子。

Kafka broker 故障处理

当添加新的 broker 时,副本会被重新分配以确保 broker 之间的工作负载均衡。

Kafka 新增 broker 处理

除了集群成员变更外,broker 之间的工作负载平衡也需要分区副本重分配。在 broker 之间平衡数据有助于防止某些分区可能接收到比其他分区更多流量的热点问题。此外,确保数据在 broker 之间均匀分布可以实现最佳的资源利用。

Kafka 的开源版本支持一个工具来实现分区重分配,叫做 kafka-reassign-partitions (bin/kafka-reassign-partitions.sh)。该工具可以在 3 种模式下运行:

  • -generate: 用于创建分区重分配计划;给定主题列表和 broker 列表,该工具会生成一个候选重分配计划,将主题的分区移动到新的 broker。
  • -execute: 在此模式下,工具根据用户提供的计划执行分区重分配。这可以是自定义的手动创建计划,也可以是使用 --generate 选项提供的计划。
  • -verify: 该工具验证上次 --execute 期间列出的所有分区的重分配状态。

然而,用户需要手动完成重分配过程,这容易出错且效率低下。有没有办法自动处理这种重分配呢?幸运的是,已经开发出了第三方工具来实现这个目的。

LinkedIn 的 Cruise Control

Cruise Control 是一个帮助大规模运行 Apache Kafka 集群的工具。由于 Kafka 的普及,许多公司的 Kafka 集群规模在不断扩大。在 LinkedIn,运营着超过 7000 个 Kafka broker,这使得 Kafka 的工作负载平衡变得极具挑战性。此外,监控和检测大型 Kafka 集群中的问题也至关重要。

Cruise Control 提供以下功能:

  • 资源利用率跟踪
  • 当前 Kafka 集群状态可观察性
  • 集群异常检测、告警和自我修复
  • 管理操作(如 broker 添加/删除或集群重平衡)
  • 多目标重分配计划生成

Cruise Control 依赖最近的副本负载信息来优化集群。它定期收集 broker 和分区级的资源使用情况,以捕获每个分区的流量模式。利用这些模式,它确定每个分区对 broker 的负载影响。该工具然后构建一个工作负载模型来模拟 Kafka 集群的性能。目标优化器基于用户定义的目标列表,探索各种方式为集群工作负载生成优化方案。

Cruise Control 架构

这种方法与 kafka-reassign-partitions 不同;虽然 Kafka 原生工具仅基于提供的输入进行重平衡,但 Cruise Control 使用工作负载模型,并为重平衡计划提供更强大的目标集。

尽管 Cruise Control 帮助解决了重平衡操作的开销问题,但在 broker 之间通过网络移动数据的需求仍然存在。当数据在 broker 之间传输时,集群必须等待一段时间才能达到平衡状态。这也使得 Cruise Control 或其他第三方工具的平衡过程在执行时可能不够准确;当工具执行决策时,它只针对集群的当前快照进行操作。由于 Kafka 中的数据需要复制,决策执行速度较慢。在执行决策时,与该决策相关的集群状态快照可能已经发生显著变化,使得决策的准确性降低。

这个问题在 Kafka 中持续存在,这是由于其设计目标是保持存储和计算紧密集成。

AutoMQ:无需移动数据

AutoMQ 中,这些问题变得简单了。

AutoMQ 利用 Apache Kafka 代码实现 100% 的 Kafka 协议兼容性,同时引入共享存储架构来替代 Kafka broker 的本地磁盘。其目标是使系统完全无状态。

虽然 Kafka broker 直接将消息写入操作系统页面缓存,但 AutoMQ broker 首先将消息写入堆外内存缓存,在写入对象存储之前对数据进行批处理。为了确保在 broker 无法将数据从内存移动到对象存储时的数据持久性,AutoMQ 引入了可插拔的预写日志(Write-Ahead Log, WAL)。Broker 必须确认消息已存储 WAL 中,然后才能写入 S3。在接收消息时,broker 将其写入内存缓存,并仅在将其持久化到 WAL 后才返回确认。如果发生 broker 故障,AutoMQ 利用 WAL 中的数据进行恢复。

AutoMQ 架构

通过这种方式,AutoMQ 实现了完全的计算存储分离。

AutoMQ 架构

AutoMQ 的设计暗示了两个重要事实:

  1. 由于对象存储服务保证了数据的持久性和可用性,因此无需在 broker 之间复制数据。因此,每个分区只会有一个副本 —— 主副本。
  2. Broker 是完全无状态的;broker 和分区之间的关系仅通过元数据管理,而不是在 broker 的本地磁盘上物理存储负责的分区数据。

因此,重平衡过程变得简单得多。不需要移动数据;AutoMQ 只需要调整 broker 和分区之间的元数据映射。这使得决策可以快速、准确和有效地执行。

说到元数据,AutoMQ 利用基于 Kafka 的 KRaft 模式的元数据管理架构。最初,Kafka 依赖单独的 ZooKeeper 服务器进行集群元数据管理。在 KRaft 模式下,Kafka 使用内部基于 Raft 的控制器仲裁组 —— 一组负责维护和确保元数据一致性的 broker。在 KRaft 中,每个 broker 都保留元数据的本地副本。同时,控制器仲裁组的领导者管理更新并将其复制到所有 broker,从而减少操作复杂性和潜在的故障点。

AutoMQ 将集群元数据(如分区和 broker 之间的映射)存储在控制器仲裁组领导者中。只有领导者可以修改这些元数据;如果 broker 想要更改它,必须与领导者通信。元数据被复制到每个 broker;元数据的任何更改都由控制器传播到每个 broker。

AutoBalancer:AutoMQ 的自平衡功能

目标

目标是指导 Kafka 集群优化和平衡的一组目标或约束。这些目标定义了特定要求,如 broker 之间的负载分布、资源利用限制、分区复制和延迟目标。

AutoBalancer 目标分类

与提供预定义目标并允许用户编写自己的目标的 Cruise Control 不同,AutoMQ 的自平衡功能 AutoBalancer 通过提供一组经过充分测试的目标来简化事 AutoMQ 中的每个目标都定义了阈值和可接受范围。例如,如果目标涉及平衡 broker 利用率,CPU 利用率阈值为 50%,范围为 ±20%,则可接受范围从 30% 到 70%。只要流量保持在这个范围内,就认为达到了目标。AutoBalancer 将目标分为两类:

  1. 检测类型的目标,如检查资源容量违规(CPU 或网络 I/O)。
AutoBalancer 目标分类
  1. 优化类型的目标,如集群流量重平衡。AutoMQ 进一步将优化目标分为生产者、消费者和 QPS(每秒查询数)平衡目标。不同类型的平衡目标解决不同的指标。例如,生产者/消费者平衡目标旨在确保生产者/消费者流量平衡,或 QPS 目标旨在平衡 broker 之间的 QPS。
AutoBalancer 目标分类

为了确保优化目标执行后效果的稳定性,AutoMQ 会为检测目标和优化目标分别谨慎选择阈值和范围。例如,通过缩小优化目标的范围可以确保优化目标处理后获得更精确的结果。

某些目标可能比其他目标具有更高的优先级。AutoMQ 按优先级将目标分类为硬目标和软目标:

  • 硬目标:这些目标必须在任何情况下都得到满足,例如限制 broker 的分区数量或限制 broker 流量的上限。
  • 软目标:如果与硬目标发生冲突,软目标可以被忽略。流量平衡目标就是一个例子。

对于目标管理,AutoMQ 用数学模型表示每个目标。每个模型根据特定的数学件指示 broker 是否满足目标。在某些情况下,可能有多个可能的操作来实现目标(例如,将分区从 broker A 移动到 B 或从 broker A 移动到 C —— 两者都可以帮助平衡集群流量)。AutoMQ 还使用数学系统来确定在特定情况下最优的决策。每个决策基于与目标相关的参数进行评分,得分最高的决策将被执行。

组件

AutoBalancer 的实现主要包含以下三个组件:

AutoBalancer 目标分类
  1. 指标收集器:Apache Kafka 基于 YammerMetrics 和 KafkaMetrics 提供了一个指标收集系统。些指标可以通过 MetricsRegistryMetricsReporter 接口进行监控。基于这些接口,AutoMQ 实现了报告器来定期收集预定义的指标,如网络流量吞吐量。AutoMQ 使用内部主题在 broker 和控制器之间传输指标;收集指标后,报告器将它们编译成多个消息并发送到内部主题。

  2. 状态管理器:在控制器上,AutoMQ 维护着一个 ClusterModel,代表集群的当前状态和分区负载。通过监控 KRaft 元数据来管理集群的变化,如 broker 的添加、删除或分区重分配和删除,以更新 ClusterModel。同时,控制器持续从内部主题消费,预处理提取的指标,并更新 ClusterModel,确保它准确反映集群的当前状态。

  3. 决策调度器:该组件旨在帮助集群实现期望的结果,如限制每个 broker 的分区数量或限制单个 broker 的流量。在 AutoMQ 中,只有活动控制器参与决策和调度。在开始决策过程之前,AutoMQ 会 ClusterModel 进行快照,使用这个快照状态进行后续调度。一旦快照完成,ClusterModel 可以继续更新。AutoMQ 的决策过程使用类似于 Cruise Control 的启发式调度算法。

典型流程

AutoBalancer 流程

自平衡调度器流程每个间隔(例如每 60 秒)启动一次,检查集群是否满足所有目标:

  1. 如果满足,调度器返回休眠状态
  2. 如果不满足,调度器将获取违反目标的 broker 列表
  3. 对于每个违规的 broker,调度器将制定分区重分配计划,试图使该 broker 达到目标
  4. 调度器然后检查分区重分配对该 broker 是否可行。如果可行,计划将在此集群中执行。如果不可行,这个 broker 无法满足目标,调度器将继续检查列表中的其他 broker

场景

让我们回顾 AutoBalancer 在不同场景下的行为:

在云环境中,"机架"可以指代一个可用区。

  1. 主题创建:AutoBalancer 支持主题创建时的架感知。它支持在机架间随机分配数据,但会考虑每个机架的"权重"。权重较重的机架平均会接收更多数据。在机架内部,数据在 broker 之间的分配将遵循它们各自的权重。如果一个 broker 具有更高的权重,它将在该机架内接收更大份额的数据。

  2. 添加 broker:AutoBalancer 支持新 broker 的逐步预热。系统不会一次性将所有流量发送到新 broker,而是随着时间推移慢慢引导流量,避免压垮它。除非涉及新机架,否则 AutoBalancer 还会尽量减少扩展过程中的跨机架流量,以防止网络拥塞。

  3. 移除 broker:AutoBalancer 支持自动将要移除的 broker 负责的分区迁移到另一个 broker。它会尽量将分区迁移到与被移除 broker 相同机架上的 broker。

  4. 不平衡吞吐量:系统根据 broker 处理特定请求率的能力分配流量。每个物理 broker 都有一个"权重"。这个权重衡量其处理负载的容量或能力。例如,性能更强的 broker 可能被分配更高的权重。AutoMQ 会考虑网络、IO 或 CPU 核心等因素来确定每个 broker 的权重。系统持续监控每个节点的负载和处理能力,以调整调度,防止任何单个 broker 过载。

  5. 单节点故障:AutoBalancer 支持识别慢速 broker,这可能预示潜在问题。系统随后可以通过将任务转移到更健康的节点来减少这些慢速 broker 的负载,允许慢速节点在不影响系统性能的情况下恢复。

AutoBalancer 与 Cruise Control 的对比

在结束本文之前,让我们回顾一下 AutoBalancer 和 Cruise Control 的一些区别:

  1. AutoMQ 原生支持 AutoBalancer 功能,无需复杂的操作和部署。相比之下,Cruise Control 需要独立部署和管理,与 Kafka 集群并行运行。

  2. Apache Kafka 在移动分区以平衡流量时需要复制大量数据,导致执行成本高昂。因此,Cruise Control 的平衡目标设计得严格,仅在流量波动较小的场景下有效。对于具有显著负载变化的场景,Cruise Control 难以保持有效性。AutoMQ 凭借其计算存储分离的设计,更适合处理复杂的负载场景。

  3. 得益于其设计,AutoMQ 允许 AutoBalancer 比 Cruise Control 更快地执行副本重分配。此外,由于 AutoBalancer 是 AutoMQ 的一个组成部分,它可以直接消费 KRaft 日志,使其能够更快地响应集群变化。

总结

感谢你阅读到这里。

在本文中,我们重新回顾了一些 Kafka 术语,例如分区副本如何在 broker 之间分布,以及为什么在集群成员变化时需要副本重分配。然后我们探讨了 Kafka 对重分配过程的原生解决方案。

接着,我们研究了像 Cruise Control 这样的第三方工具如何帮助用户更方便、更稳健地简化这个过程。我们发现 AutoMQ 可以完全解决重分配过程中的数据移动挑战,因为数据存储在 broker 之外,只需要调整元数据。最后,我们深入探讨了 AutoBalancer,即 AutoMQ 的自平衡功能。

我们看到,虽然 Cruise Control 协助用户处理 Kafka 的重分配过程,但核心问题仍然存在:数据仍然需要通过网络在 broker 之间传输。AutoMQ 的创新架构允许数据完全存储在对象存储中,这使得许多 Kafka 操作对用户来说变得更加简单,特别是在分区重分配期间;当分区分配给不同的 broker 时,只需要调整元数据。这也使得其内部自平衡更加高效和稳健。