写点什么

基于开源组件打造自动驾驶 Kafka 集群

作者:俞凡
  • 2022 年 3 月 13 日
  • 本文字数:5049 字

    阅读完需:约 17 分钟

本文介绍了 Slack 如何基于 Chef、Terraform、CruiseControl、CMAK 等开源工具打造自动化的 Kafka 基础设施,实现 Kafka 集群的自动化运维管理。原文:Building Self-driving Kafka clusters using open source components[1]


本文讨论 Slack 如何使用 Kafka,并且介绍了在过去四年中是怎样由一个小而精干的团队构建并规模化运行自动驾驶 Kafka 集群。


Slack 利用 Kafka 构建发布/订阅系统,这一系统在所有重要的作业队列(Job Queue)[2]中都扮演重要角色,为 Slack 用户的几乎所有操作(例如在 channel 中展开链接,发送通知,通知机器人,更新搜索指数以及运行安全检查)提供异步作业执行框架。此外,Kafka 还充当了在 Slack 上传输关键任务数据的神经系统,为日志管道[3]、跟踪数据[4]、计费管道、企业分析[5]和安全分析数据提供赋能。


Slack 的 Kafka 之旅


早在 2018 年,几个团队就在各自的用例中采用了 Kafka,并各自运行独立的 Kafka 集群。结果,不同团队部署了不同版本的 Kafka,并且都做了重复的部署、运维以及管理 Kafka 集群的工作。


因此我们接手了将所有 Kafka 集群标准化到一个版本下的项目,并由一个团队进行管理。然而,由于我们团队规模比较小,所以希望能够尽可能让运维自动化。


今天,Slack 在 10 个 Kafka 集群中管理着大约 0.7 PB 的数据,运行在数百个节点上,每秒处理数百个 topic 的数百万条消息,最高总吞吐量为 6.5 Gbps。我们的 Kafka 基础设施成本不仅由硬件决定,也由网络决定,而消费者包括了从繁重的批处理作业到像 Job Queue[2]这样对延迟非常敏感的应用。


什么是自动驾驶 Kafka 集群?


Kafka 是一款优秀的软件,可以在 Slack 的数百个节点上运行。然而,如果你曾经尝试过部署或管理 Kafka,就会知道这不是一件容易的事。我们经常会因为 broker 速度慢、时不时出现的故障或容量管理问题而被要求提供支持。


Kafka 运维自动化的目标是消除日常管理 Kafka 的运维开销。


为此,我们为 Kafka 集群确定了一些常见的运维任务,包括:

  • Kafka 常规管理操作,如创建 topic,更改分区计数以及重新分配分区给 broker

  • 容量规划操作,比如向集群添加/删除 broker

  • 运维问题,如替换 broker 或部署新版本的软件

  • 诊断 Kafka 集群问题的 on-call 工作

  • 解释 Kafka 消费者的消费速度是否足够的客户支持工作


因此,当我们迁移到新版本的 Kafka 集群时,决定将运维方面的操作自动化,或者为用户提供自服务。


Kafka 2 项目


我们统一了努力的方向,基于 2.0.1 版本实现了一个更加自动化的 Kafka。我们的 Kafka 设置由以下组件组成:


构建、发布、配置和部署:Chef 和 Terraform


我们使用 Chef 来管理基本操作系统,在主机上部署和配置 Kafka 软件。我们的每个 Kafka 集群都运行在不同的角色下,有自己的自定义配置,但共享相同的基础配置。我们用 Terraform 模块为 AWS 中的 Chef 角色创建 ASG,自动管理节点的开通和关闭。


以前部署 Kafka 主要是通过部署 Debian Kafka 包来管理。然而,我们发现部署预构建的包是很痛苦的,因为配置并不总是可靠。此外,由于我们无论如何都要重写默认配置,所以 Chef 配置非常复杂。为了解决这些问题,我们在内部 fork 了 Kafka 仓库,并建立了 CI/CD 流水线来构建 Kafka 并将静态二进制文件发布到 S3。然后,Chef 将从 S3 中提取二进制文件并部署,这一过程可重复执行。


Apache Zookeeper 3.4 集群一般来说是手动配置的,我们没有一种自动的方式来确保每个 Zookeeper 节点都有唯一的 ID,并且没法在不重启整个集群的情况下重新分配 ID。手动配置 Zookeeper 节点不仅冗长乏味(我们经常在普通的节点故障发生时被要求提供支持),而且很容易出错,我们有可能会意外的在同一个 AWS 可用域启动多个 Zookeeper 节点,从而增加影响半径。为了减少乏味和错误,我们通过升级到 Zookeeper 3.6 来自动化这个过程,它在替换 broker 时不需要重启集群。当配置 Zookeeper 节点时,通过 Consul KV 自动分配唯一的 ID。有了这两个变化,就可以使用 Terraform 通过 ASG 开通 Zookeeper 集群。


Kafka 集群稳定性优化


虽然上述配置有助于减轻自动化配置主机的痛苦,但我们仍然不得不负责集群运维,比如将分区迁移到新的 broker,并为负载重新平衡 broker。此外,集群运维操作影响到了客户,导致他们需要上线支持或者达不到 SLO。


经过分析,我们发现 Kafka 集群中的热点导致了不稳定性,不同的问题会引发热点。


我们注意到,集群中有几百个 Kafka topic,每个 topic 基于不同的负载有不同的分区计数。在常规运维过程中,一些 broker 会比其他 broker 处理更多数据。在集群运维操作(比如添加/删除 broker)过程中,这些热点又会加剧,从而导致数据消费延时。


为了解决热点问题,我们希望均匀利用集群中的所有 broker。我们将所有分区计数更改为 broker 计数的倍数,从而消除写热点。我们通过在所有节点上选择均匀的消费者数量来平滑读取热点。只要所有分区都均匀分布在集群中,就可以在整个集群中获得可靠的利用率,并平滑读写速率。此外,当扩展 broker 或消费者时,将更新 topic 的分区计数,这样分区计数仍将是代理计数的倍数,以确保均匀的利用率。


Kafka 集群热点发现的另一个原因是分区再平衡事件期间的复制带宽消耗。我们发现,复制带宽不足的生产者或消费者消耗了大部分资源,尤其是在高峰时段。因此,我们限制了集群可以使用的复制带宽。然而,限制复制带宽会导致集群运维操作变慢,为此我们还修改了操作,每次只移动少量分区,这让我们能够持续做出许多小的改变。


尽管做出了这些努力,Kafka 集群仍然会由于部分故障失去平衡。为了将这些操作自动化,我们使用了出色的 Cruise Control[6]自动化套件(由 LinkedIn 构建)来自动化集群平衡操作,并确保集群中所有节点的平均利用率。总体来说,这些调优为集群稳定运行提供了帮助。


混沌工程


考虑到将流量从旧集群切换到新集群的影响很大,我们使用暗流量对新集群进行了混沌实验。


测试涉及到集群负载下各种资源。此外,我们能够在受控的条件下终止 broker,这有助于我们更好的理解代理失效的模式及其对生产者和消费者的影响。


在这些测试中,我们发现集群恢复操作大多受到主机每秒发送的数据包数量的限制。为了支持更快的恢复,我们在这些主机上启用了 jumbo frame。现在我们的 Kafka 实例拥有 Slack 基础设施团队中最高的每秒数据包利用率。


此外,这也帮助我们识别到使用 Go Sarama 库的用户在一些边缘情况下的 bug。在某些用例下,我们已经将这些客户端迁移到 Confluent Go,这也帮助我们标准化了跨语言的客户端配置。在我们无法升级使用者的情况下,我们添加了适当的工作区和告警来监视这些用例。


在这些测试中,我们也意识到 Zookeeper 的问题会迅速演变成更大的 Kafka 问题。所以,我们为每个 Kafka 集群配置了单独的 Zookeeper 集群来减少 Zookeeper 故障的影响半径,虽然这么做会稍微增加一些成本。


混沌实验还帮助我们理解在实际故障期间可能出现的操作问题,并帮助我们对集群做出更好的调优。


自服务 Kafka 集群


在很多情况下,消费者团队会向我们询问或者提出增加/减少集群容量的问题。其中一类问题是关于常规操作的,比如容量规划,另一类是了解流水线的健康状况。


此外,使用 CLI 工具来理解 Kafka 发生了什么是很乏味的。所以,我们部署了 kafka manager[7],让每个人都能看到 kafka 集群的元数据,比如 broker 列表和 topic 列表。Kafka Manager 还帮助我们简化了日常操作,比如创建新的主题和增加主题的分区数量。


为了提供对 Kafka 消费者健康状况的操作可见性,我们部署了 Kafka offset exporter[8]的一个分支,将消费者的偏移信息导出为 Prometheus 度量指标。我们在这些数据的基础上构建了仪表盘,向消费者实时提供每个 topic、每个消费者的聚合消费指标。


为了减少知识孤岛,我们将各种执行手册标准化为单一的执行手册,有助于将所有 Kafka 知识聚集到一个地方。此外,我们将多个 Kafka 仪表板整合到一个全局仪表板中,用于所有 Kafka 集群。


总之,这些自助式工具帮助客户更好的理解数据,同时减少了团队的运维开销。这些工具还通过减少 SSH 到 Kafka broker 的需要改善了安全状况。


升级 Kafka 集群


为了升级 Kafka 集群,我们决定不进行本地集群升级。原因是我们没有信心确保在升级窗口期间做到不停机,特别是在同时升级多个版本时。此外,我们没有办法验证新集群是否存在问题,特别是在更改底层硬件类型时。


为了解决这些问题,我们制定了一个新的集群切换的升级策略。切换过程如下:

  • 启动新集群

  • 在新集群上使用暗流量运行所有验证测试

  • 停止向旧集群生产数据

  • 开始向新集群生产数据

  • 在保留窗口过期后关闭旧集群


虽然这种策略有协调截断消费者的缺点,但这是一种标准的运维过程,也适用于其他场景,如跨集群移动 topic 以及测试新的 EC2 实例类型。


分裂 Kafka 主集群


在投入时间让 Kafka 可以自我维持以及提升了可靠性之后,我们可以有时间去做其他重要的功能,比如 tracing[4]。然而,即使我们已经做了所有工作,当系统达到临界点时,仍然需要重新审视假设和容量。


我们在 2021 年初接近了这个点,90 个 broker 集群在网络吞吐量上达到了临界点,最高达到 40,000 pps。网络饱和导致下游管道延时,Kafka 在正常工作负载下都难以跟上用户,更不用说处理大流量峰值了。依赖日志管道来调试问题的开发者每天都会受到 Kafka 网络饱和的影响。


为了减少 Kafka 主集群的负载,我们利用工具和自动化将大型 topic 分割到更小、更高性能的集群中(从旧的 d2[9]实例升级到现代的启用了 nitro 的 d3en 实例[10])。比较两个集群之间类似的工作负载,新的集群能够在 20 个 broker 上实现类似的性能(每 1,000 pps),大约提高了 2.5 倍的效率。


在将三个最大的 topic 移出主集群后,问题立即获得了缓解。下面是一些当时的图表,用来说明这项工作的影响。



这些峰值代表了消费者从最大的 topic 之一消费数据产生的滞后,每当滞后超过 5 亿,就会因为日志刷新而破坏 SLA。



主题迁移完成后,消费者延迟大大改善。我们的日志延迟从最坏情况下的 1.5 小时减少到最坏情况下的 3-4 分钟。此外,我们的日志记录流水线的 on-call 数量从一个月 71 个告警减少到一个月 9 个告警。相当大的进步!


更小的专用 Kafka 集群也更容易管理,集群运维操作可以完成得更快,而且由于干扰产生的噪音问题更少。


结论


使用 Cruise Control、Kafka Manager、Chef 和 Terraform 等开源组件,可以大规模运行自愈的 Kafka 集群。此外,使用标准的 SRE 原则和适当的工具,如 Kafka Manager 和 Kafka offset exporter,可以构建可靠、自助式和自动驾驶的 Kafka。


我们从其他人的 Kafka 配置中受益匪浅,本着分享传递的精神,可以在 Gibhub 上找到我们的 Kafka 配置[11]


过去几年来,这一架构一直在 Slack 成功运行。展望未来,Kafka 将在 Slack 扮演更重要的角色,它是新的变更数据捕获(CDC,Change Data Capture)项目的一部分。新的 CDC 功能将支持 Slack 的权限服务(Permission Service)的缓存需求,该服务用于授权 Slack 中的操作,也将支持数据仓库的近实时更新。为此,我们在 Slack 成立了一个新的数据流团队来处理所有当前和未来的 Kafka 用例,并维护和支持 Slack 的所有 Kafka 集群。


References:

[1] Building Self-driving Kafka clusters using open source components: https://slack.engineering/building-self-driving-kafka-clusters-using-open-source-components/

[2] Scaling Slack's Job Queue: https://slack.engineering/scaling-slacks-job-queue

[3] Data Wrangling at Slack: https://slack.engineering/data-wrangling-at-slack/

[4] Tracing at Slack Thinking in Causal Graphs: https://slack.engineering/tracing-at-slack-thinking-in-causal-graphs/

[5] Understand the data in your Slack analytics dashboard: https://slack.com/help/articles/360057638533-Understand-the-data-in-your-Slack-analytics-dashboard

[6] Cruise Control: https://github.com/linkedin/cruise-control

[7] Cluster Manager for Apache Kafka: https://github.com/yahoo/CMAK

[8] Kafka offset exporter: https://github.com/echojc/kafka-offset-exporter

[9] Now afailable d2 instance the latest generation of Amazon EC2 dense storage instances: https://aws.amazon.com/about-aws/whats-new/2015/03/now-available-d2-instances-the-latest-generation-of-amazon-ec2-dense-storage-instances/

[10] D3: https://aws.amazon.com/ec2/instance-types/d3/

[11] https://gist.github.com/mansu/dfe521987b48c060eb17cf3c5f7c3068


你好,我是俞凡,在 Motorola 做过研发,现在在 Mavenir 做技术工作,对通信、网络、后端架构、云原生、DevOps、CICD、区块链、AI 等技术始终保持着浓厚的兴趣,平时喜欢阅读、思考,相信持续学习、终身成长,欢迎一起交流学习。

微信公众号:DeepNoMind

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

俞凡

关注

还未添加个人签名 2017.10.18 加入

俞凡,Mavenir Systems研发总监,关注高可用架构、高性能服务、5G、人工智能、区块链、DevOps、Agile等。公众号:DeepNoMind

评论

发布
暂无评论
基于开源组件打造自动驾驶Kafka集群_架构_俞凡_InfoQ写作平台