案例分享 | Yelp 如何在 Kubernetes 上运行 Kafka(第 2 部分 - 迁移)

2022-08-12 10:29:52 浏览数 (1)

本文译自 Kafka on PaaSTA: Running Kafka on Kubernetes at Yelp (Part 2 - Migration)[1]作者:Lennart Rudolph

上一篇文章,我们详细介绍了开发基于 PaaSTA 的新部署模型的架构和动机。现在想分享我们将现有 Kafka 集群从 EC2 无缝迁移到基于 Kubernetes 的内部计算平台的策略。为了帮助促进迁移,我们构建了与集群架构的各种组件接口的工具,以确保该过程是自动化的,并且不会影响用户读取或写入 Kafka 记录的能力。

将 EC2 上的 Kafka 迁移到 PaaSTA 上的 Kafka

背景

在实施过程中,集群中支持 EC2 的 Kafka 代理与一个自动扩展组 ASG[2]相关联。每个 ASG 都有一个弹性负载均衡器(ELB),它促进了与集群的所有连接并充当入口点。每个集群还附带一些辅助服务和作业,但其中大部分已经部署在 PaaSTA 上。然而,一些重要的管理系统直接在 Kafka 服务器上作为 cron 作业运行。这次重新设计特别重要的一点是集群重新平衡算法和主题自动分区算法。重新平衡算法尝试在集群的代理之间均匀分配分区和领导,而自动分区算法会根据吞吐量指标自动设置主题分区数量。由于我们已经计划在架构中加入巡航控制,现在是迁移到新的再平衡算法的好时机。

因此,在这次迁移中,我们重点替换的三个关键组件是集群入口、集群平衡算法和主题自动分区算法。我们不需要寻找 ELB 的替代品,因为 PaaSTA 通过 Yelp 的服务网格提供了原生的负载平衡能力,这使得在组成集群的 Kubernetes 容器上发布 Kafka 变得简单。在当前的 EC2 场景中,我们还在 Kafka 主机上运行了自定义重新平衡算法,但这最终被 Cruise Control 取代(有关此服务的更多详细信息,请阅读第 1 部分),它提供了类似的功能。最后,我们基于 Puppet 的运行主题自动分区脚本的 cron 作业被替换为类似的 Tron 在 PaaSTA 上运行的作业。下表提供了跨部署方法的不同组件的概述:

零件

EC2

PaaSTA

集群入口点

电子负载均衡器

Yelp 的服务网格

集群平衡

kafka-utils 中的再平衡算法

巡航控制

主题自动分区

cron 作业(基于 Puppet)

Tron 工作

每种部署方法使用的组件表

由于我们不会同时迁移所有集群,因此我们希望避免对 Kafka 集群发现配置文件进行重大更改。为了了解更多情况,在 Yelp,我们使用一组kafka_discovery文件(由 Puppet 生成),其中包含每个集群的引导服务器、ZooKeeper[3] chroot 和其他元数据的信息。我们的许多内部系统(如Schematizer[4]Monk[5]) 依赖于这些文件中的信息。这种迁移策略只需要更新 broker_list 以指向服务网格的入口,从而保持与我们现有工具的兼容性。我们确实将这次迁移作为一个机会,通过删除 Puppet 作为真实来源来改进传播方法,而是选择使用 srv-configs(服务使用的配置的规范位置)。下面是一个发现文件的例子:

代码语言:javascript复制
>> cat /kafka_discovery/example-cluster.yaml
---
clusters:
  uswest1-devc:
 broker_list:
 - kafka-example-cluster-elb-uswest1devc.<omitted>.<omitted>.com:9092
 - kafka-example-cluster-elb-uswest1devc.<omitted>.<omitted>.com:9092
 zookeeper: xx.xx.xx.xxx:2181,xx.xx.xx.xxx:2181,xx.xx.xx.xxx:2181/kafka-example-cluster-uswest1-devc
local_config:
  cluster: uswest1-devc
  ...

迁移策略概述

在高层次上,迁移的目标是从使用 EC2 兼容组件无缝切换到使用 PaaSTA 兼容组件,而不会导致现有生产者和消费者客户端出现停机。因此,在将任何数据从基于 EC2 的代理迁移到基于 PaaSTA 的代理之前,我们需要确保所有新组件都已到位。我们还希望最大限度地减少迁移所需的工程时间,因此我们实施了一些工具来帮助自动化流程。最后,我们需要确保这个过程经过全面测试并且是安全回滚的。

迁移过程的第一步是为我们的每个 Kafka 集群设置一个基于 PaaSTA 的负载均衡器,它也可以用于宣传基于 EC2 的代理。这暴露了连接 Kafka 集群的两种不同方法:现有的 ELB 和新的服务网格代理,它将在迁移期间和之后用于基于 PaaSTA 的代理。这需要更新上述 kafka_discovery 文件,以包括备用的连接方法,我们还设计了一种新方法来使用 cron 作业传播这些文件,而不是依赖 Puppet。正如在前一篇文章中提到的,减少对 Puppet 的依赖有助于我们将部署一个新 Kafka 集群的时间减半,因为我们可以更快地更改和分发这些配置文件。完成这些工作后,我们还清除了相关缓存,以确保没有客户使用过时的集群发现信息。下面是一组图,说明了这一过程:

集群连接迁移

接下来,我们为集群部署了一个专用的 Cruise Control 实例,并禁用了自愈功能 。我们不希望多个再平衡算法同时运行,由于自愈算法能够重新平衡集群,我们阻止了 Cruise Control 自动移动主题分区。在此之后,我们为集群创建了一个 PaaSTA 实例,但我们明确禁用了 Kafka Kubernetes operator 对 Cruise Control 的使用。对于具有 N 个代理的 EC2 集群,我们随后添加了额外的 N 个基于 PaaSTA 的代理,从而在迁移期间有效地将集群规模扩大了 1 倍。

在新的 PaaSTA 代理上线并健康运行后,集群中的 EC2 代理和 PaaSTA 代理数量相等。我们还通过创建__CruiseControlMetrics[6]主题并在每次迁移前设置适当的配置来启用指标报告。为了保持对分区移动时间的控制,我们禁用了当前的自动重新平衡算法。在这一点上,我们已准备好开始将数据从 EC2 代理中移出,并利用 Cruise Control 的 API 来移除他们。请注意,这个 API 仅将分区从指定的代理移开,并不会真正停用主机。在整个迁移过程中,我们继续 EC2 生命周期行动发送心跳,因为与 EC2 代理关联的自动缩放组将持续到迁移过程结束。下图说明了整个迁移过程中每个组件的状态:

从条件再平衡脚本迁移到 Cruise Control

我们没有手动发出代理删除请求,而是构建了一个基本的迁移助手服务来检查集群状态,反复向 Cruise Control REST API 发出请求,并逐一删除 EC2 代理。在 Cruise Control 完成将所有分区数据从 EC2 代理移到 PaaSTA 代理之后,我们准备终止 EC2 代理。这是通过将 ASG 的大小从 N 缩小到 0 ,并在我们的配置文件中删除对旧 EC2 ELB 的引用来实现的。由于我们使用 Terraform 来管理 AWS 资源,因此回滚过程就像git revert重新创建资源。停用 EC2 代理后,我们删除了停用帮助程序服务的实例,并在集群的 Cruise Control 实例中启用了自我修复。现在这样做是安全的,因为集群完全由基于 PaaSTA 的代理组成。至此,集群迁移已完成,剩下的工作需要在认为安全后清理任何杂项 AWS 资源(自动缩放 SQS 队列、ASG、ELB 等)。

风险、回滚和金丝雀发布

虽然我们努力优化安全而不是迁移速度,但我们的方法自然还是存在一些风险和缺点。一个考虑因素是由于每个集群的规模翻倍而导致的临时成本增加。对此的替代方法是迭代地添加一个 PaaSTA 代理,从一个 EC2 代理进行数据迁移,停用一个 EC2 代理,然后重复。由于这种方法将数据迁移限制在每次一个代理的副本集,因此这种方法会延长迁移过程的总时间。最终,我们决定我们倾向于迁移速度,因此拥有两倍数量的代理的前期成本是我们愿意支付的成本。此外,从长远来看,在 PaaSTA 上使用集群所带来的好处将超过这些初始成本。另一个权衡是,集群规模加倍也会导致我们的一些高流量集群的集群规模非常大。这些集群在迁移过程中需要额外的关注,而这种工程时间成本也是我们为了缩短迁移时间而愿意进行的初始投资。

如果迁移过程中出现灾难性问题,我们还需要设计回滚程序。在任何阶段按顺序扭转迁移过程的顺序,就足以回滚更改(这次使用 Cruise Control 的add_brokerAPI 而不是remove_broker删除任何未决的重新分配计划后的 API)。与此相关的主要风险是,迁移和回滚过程都在很大程度上依赖于 Cruise Control 处于健康状态。为了降低这种风险,我们评估了这些实例在测试集群上的资源需求,然后为非测试 Cruise Control 实例超额配置了硬件资源。我们还确保对这些实例的健康状况进行充分的监控和警报。最后,我们提供了备份实例,如果主实例变得不健康,它将作为替代。

虽然这个计划在理论上似乎是合理的,但我们需要在真实集群测试它,并彻底记录任何异常情况。为此,我们首先使用 Kafka MirrorMaker 克隆现有集群,然后在非生产环境中执行完整的金丝雀发布迁移,然后在生产环境中重复金丝雀发布迁移。一旦我们建立了足够的信心和文档,我们就在开发和暂存环境中对所有的 Kafka 集群进行了真正的迁移,然后再执行任何生产迁移。

挑战和学习

如前所述,该计划的主要风险是 Cruise Control 必须是健康的,才能进行迁移或回滚。在一些非产品迁移中,我们遇到了一些不稳定的情况,其中 Cruise Control 实例由于 Kafka 集群中的离线分区而变得不健康,暂时出现了代理不稳定的情况。由于 Cruise Control 的算法和内部集群模型依赖于能够读取(和写入)一组指标主题,则必须维护 Cruise Control 和每个 Kafka 集群之间的通信。因此,离线分区会阻止 Cruise Control 正常运行,所以在这些情况下,优先级是首先对 Kafka 中的问题进行分类和修复。此外,Cruise Control 公开配置值以调整其内部度量算法的各个方面,我们发现减少回溯窗口和所需数据点的数量有时会有所帮助。这样做有助于 Cruise Control 在 Kafka 代理遇到离线分区的情况下更快地重新生成其内部模型。

由于我们正在迁移单个集群,从开发环境中的集群开始,我们能够深入了解 Kafka 集群在 PaaSTA/Kubernetes 上运行时与在 EC2 上运行时相比的性能特征。就像我们在 EC2 裸机上运行的实例选择标准一样,我们能够根据资源需求建立具有不同实例类型的 Kafka 池(例如,标准池和大型池,每个池都包含不同的实例类型)。

我们最初为迁移过程考虑的另一种方法是建立一个新的基于 PaaSTA 的集群,其中包含 N 个代理,然后使用 Kafka MirrorMaker 将现有 EC2 集群的数据“克隆”到这个新集群上。我们还考虑调整策略,增加一个 PaaSTA 代理,删除一个 EC2 代理,然后重复 N 次。但是,这将需要为迁移目的更新 operator 的协调逻辑,并且我们需要手动确保每个代理对位于同一可用区。它还会引入一个冗长的数据复制步骤,我们认为这对于大型集群来说是不可接受的。在我们的开发环境中对程序进行了一些进一步的测试后,我们最终确定了这里描述的程序。

引用链接

[1]

原文链接: https://engineeringblog.yelp.com/2022/03/kafka-on-paasta-part-two.html

[2]

ASG: https://docs.aws.amazon.com/autoscaling/ec2/userguide/auto-scaling-groups.html

[3]

ZooKeeper: https://zookeeper.apache.org/

[4]

Schematizer: https://github.com/Yelp/schematizer

[5]

Monk: https://engineeringblog.yelp.com/2020/01/streams-and-monk-how-yelp-approaches-kafka-in-2020.html

[6]

__CruiseControlMetrics: https://github.com/linkedin/cruise-control/blob/fb13240bc5759b30720339c27fdc3a04b8544c23/config/cruisecontrol.properties#L49-L50

0 人点赞