近年来,无论是海外还是国内,虽然受疫情影响,公有云的市场规模增速有所放缓,但是云的市场总规模仍然是持续增长的。公有云作为一个各个国家重点布局的战略方向和其本身万亿级市场的定位[1],我们学习用好云是非常有必要的。
AutoMQ Kafka 充分认识到“云优先”的重要性,围绕公有云具备规模化效益和技术红利的云基础设施重新设计了 Kafka。在保证 100% 兼容 Apache Kafka 的基础上带来了极致的云成本优势和弹性能力,云上综合有 10 倍以上的成本节约[2]。今天就和大家分享下 AutoMQ Kafka 云上成本节约的利器之一,Spot 实例。
01
Spot 实例应用的挑战
Spot 实例本质上是一种实例购买类型。Spot 实例是云计算实例规模化成本红利的产物,通过机器的分时复用来提升利用率,从而推出更加廉价的实例购买类型。这本身也是云厂商相比私有 IDC 自建机房固定资源预留带来的规模化优势。Spot实例本身的硬件能力和正价的按需实例别无二致,但是其价格可以低至按需实例的价格的1折。用好Spot实例将使得软件系统在云上获得极大的成本节约。
使用Spot实例本质就是薅云厂商的羊毛。Spot实例诱人的价格令人心动,但是其存在的一个最大的问题就是——不确定性。云厂商不会对Spot实例的可用性提供SLA,根据云厂商的规则,在必要的时候云厂商会直接发起Spot实例的回收流程,终止Spot实例。对于AutoMQ来说,如何以一种确定性的方式来使用Spot实例,为用户提供有SLA、可靠的Kafka服务,是我们面临的主要挑战。AutoMQ Kafka 通过大量应用 Spot 实例来降低总体计算成本[3]。在经过诸多实践后,我们得出一些在 Spot 实例上提供可靠 Kafka 服务的方法。
02
在不可靠的 Spot 实例上提供可靠的服务
Broker 无状态化
由于 Spot 本身随时会中断的特性, 云厂商的 Spot 实例最佳实践基本[4]都会强调 Spot 实例适用于无状态的应用。因此一个软件系统“无状态”完成得越彻底, 则 Spot 实例则会被利用得更彻底。
有状态引用最大的问题在于其状态数据的迁移、恢复。以 Apache Kafka 为例,即使在 3.6.0 版本以后支持了分级存储(非 GA)的特性[5],其 broker 仍然是有状态的设计,对于每个 broker 上的分区数据要求最后一个 logsegment 必须在一级存储上。当这个 logsegment 非常大时,占用的一级存储空间将会非常大,当其关联的 broker 下线时,这些状态数据迁移是非常耗时的。如果不采用分级存储,这种迁移花费数小时甚至数天[6]都是很常见的。
AutoMQ Kafka 虽然在架构上除了依赖对象存储以外还依赖 EBS 块存储,但是其本质上是采用了一个无状态的架构,一级存储是松耦合的,充当一个缓冲区的角色。下图可以揭示 Apache Kafka 的多级存储和 AutoMQ 存储架构的区别。AutoMQ Kafka 使用的 EBS 写入缓冲区默认值为固定的 3GB,在扩缩容场景可以完成秒级甚至毫秒级下线(取决于具体采用的机型)。
大量应用 Spot 实例,会存在集群中计算实例的频繁上下线,如果采用 Apache Kafka,不仅需要人为介入处理 Spot 实例的替换,同时这种频繁的上下线、分区数据移动将会造成系统明显抖动,对数据的生产、消费产明显的影响。而 AutoMQ Kafka 由于其无状态的设计,很好的规避了这种问题,即使使用大量的 Spot 实例,也可以将这种实例替换带来的系统抖动降低到最小,以业务无感的方式完成 Spot 实例的替换。
极速的弹性与 Serverless
AutoMQ Kafka 是天然支持 serverless 的。系统本身的弹性速度和质量决定过了其所能提供的 Serverless 服务质量。Spot 实例的大量应用,由于不可预期的回收行为,会导致整个系统使用的计算实例经常性地被置换。在这个过程中,AutoMQ Kafka 所在计算实例接受实例终止信号到新的 Spot 实例被替换后启动 AutoMQ Kafka 并且重新接受流量整个冷启动过程的耗时长短决定着 AutoMQ Kafka 弹性的效率。
以 Apache Kafka 为例,如果使用 Spot 实例并且产生了实例的置换,其整个冷启动的过程如下。从图上我们可以非常清晰的看到,当数据规模较大时(TB 级)或者存在分区热点时,Apache Kafka 整个冷启动时间中执行手动完成分区迁移、数据拷贝、流量重新均衡的过程耗时十分长,可达小时甚至是天级别[6],而采用 AutoMQ Kafka 由于其采用可靠性和可用性分离的设计,单副本即高可靠,整个分区移动过程无任何数据拷贝[7]。下图可以清晰看到,如果采用 Apche Kafka 在数据规模较大的场景下是完全没法应用 Spot 实例并且提供 serverless 能力的,因为在冷启动的整个时间轴上,Apache Kafka 在分区移动和流量重平衡两个过程的耗时占据着总耗时绝对的比重。不将这两块耗时降低到与其他冷启动阶段相同数量级下,spot 实例的应用和 serverless 也无从谈起。
与之相反的是,AutoMQ Kafka 凭借其秒级分区迁移[9]和持续流量重平衡[8]等杀手锏特性,不仅将高危的、重运维的分区移动和重平衡的耗时降低到秒级,同时整个过程还是自动化的,相比 Apache Kafka 而言,有了跨时代的进步。当软件系统本身有较短的冷启动时间以后,围绕冷启动的其他阶段进行优化才有意义。在 AutoMQ 内核不再成为冷启动瓶颈的情况下,AutoMQ 也将不断探索利用容器技术、GraalVM AOT 编译等手段提升整个端到端冷启动的效率,给大家带来更快、更好的弹性能力。
充分利用云 Spot 实例的终止信号
Spot 实例回收的一般流程遵循如下流程,先发送终止信号,然后等待若干秒后再强制终止机器。不同云厂商的 Spot 实例的终止流程基本是如下流程的变种,核心路径基本相同。AutoMQ Kafka 的架构上使用了一块非常小(默认 3GB)的云盘 SSD (AWS 上即 EBS,下文皆以 EBS 表示云盘 SSD)来充当缓冲区的角色,以保证 AutoMQ Kafka 追尾读的低延迟。得益于 AutoMQ Kafka 无状态的 Broker 设计,EBS 上只会残留约几百 MB 左右的少量缓存数据,只要保证 Spot 实例在接收到终止信号的等待期间将这部分数据刷到对象存储上,即可完成优雅停机。
AutoMQ 充分利用了这个实例终止信号,通过感知这个实例终止信号,然后在实例接收到终止信号的这段等待时间内提前执行刷出 EBS 缓存数据的操作来完成优雅停机。不同云厂商开放给用户去感知这个终止信号的方式会有差异,但是基本都会预留至少 10 秒以上的等待时间来让应用执行优雅下线,而这预留的时间对于 AutoMQ 来说是完全足够的。
Spot 实例友好的容灾机制
前面小节提到了 AutoMQ Kafka 利用 Spot 实例终止信号后的一小段等待时间来完成优雅停机,这时候一定会有聪明的小伙伴提出质疑:我们应该考虑面向失败的设计,最坏情况下例如网络异常、系统负载异常卡顿导致 AutoMQ  来不及将数据在终止信号后的这段等待时间及时刷出怎么办呢?其实,这种情况 AutoMQ 也已经考虑到了,因此专门设计了 Spot 实例友好的容灾机制[10]
下图是整个容灾机制的简单示意图,总体上概括起来就是:
  1. AutoMQ 通过探测及时发现由于 Spot 实例回收而遗留的游离数据卷,通过云盘管理的 API 将其挂载到一台合适的新的计算实例上
  2. 将游离数据卷残留的少量数据刷出到对象存储
  3. 删除已经为空的数据卷
通过这种容灾机制,即使在最坏情况下,AutoMQ Kafka 仍然可以完成自动化的容灾,整个过程业务无感。
按需实例与 Spot 实例混部
AutoMQ Kafka 虽然大量应用了 Spot 实例来降低成本,但是仍然在两个纬度上保留了少量按需实例的使用,从而确保 AutoMQ 可以给用户提供可靠的 Kafka 服务。
KRaft 节点使用 on-demand 实例:
AutoMQ 的核心能力依赖的重要元数据依靠 KRaft,为了保证元数据的可靠性,参与 Raft 选举和保障元数据一致性的节点仍然使用的是 on-demand 实例,确保他们保持稳定。
Broker 集群支持 on-demand 和 Spot 实例混布:
以 AWS Spot 实例的实际使用情况来看,一个 30 台机器的 AutoMQ Kafka 集群,一天内会有若干次实例置换,这种零碎时刻的实例置换,在 AutoMQ 这种无状态和极致弹性的设计下对业务基本是无感的。Spot 实例的置换仅仅会导致部分分区数据的读写有秒级的 RT 抖动,这可以满足绝大部分 Kafka 的应用场景。即使如此,AutoMQ 也充分考虑到一部分对成本不敏感,但是对 RT 抖动要求非常苛刻的用户的诉求,允许用户调节 Broker 集群中 on-demand 实例的比例,权衡成本与延迟抖动频率。
回退按需实例
Spot 实例除了存在会中断的问题,还存在容易库存不足的问题。对于云厂商而言,按需实例是有 SLA 的并且要最高优先级保障库存余量充足。如果一个地域某个可用区下的计算实例库存不足,则会优先用于满足按需实例的供给。在这种规则下,一些冷门地域或者可用区的 Spot 实例库存容量容易产生不足,当需要发生实例替换时,会存在无法购买到竞价实例的情况。
AutoMQ Kafka 为了应对可能出现的 Spot 实例库存不足的情况,提供了回退按需实例(后文简称该特性为 fallback)的能力。Fallback 本质就是探测并识别 Spot 实例库存不足的情况,然后在这种情况下重新购买按需实例来补充容量。并且 fallback 支持当 Spot 实例可以重新购买时,自动将集群中的按需实例重新替换成按需实例。该功能的总体实现主要是利用了弹性伸缩组本身容量管理的特性来达到的,因篇幅原因,后续会专门出一篇文章来介绍 fallback 能力的实现。
03
稳定性与成本之间的权衡
Spot 实例本身不可预期的中断、库存问题使得很多系统设计与开发者对其应用望而却步,持有过度的偏见。其实这种疑虑本质上源于不了解。正如世间没有绝对的安全一样,也不存在绝对的稳定性。稳定性的定义因应用场景而异,因为不同场景对于“稳定”的标准各不相同。在软件系统设计中,关键在于做出恰当的权衡。
以 AutoMQ 提供的 Kafka 为例,如果你可以容忍因 Spot 实例替换带来的某些时刻部分分区上秒级的 RT 抖动,那么你可以放心的使用较大比例的 Spot 实例从而获取巨大的成本节约;但是如果你是一个对 RT 抖动极度敏感的用户,那你也仍然可以全部采用按需实例,仅仅享受 AutoMQ 带来的极致弹性能力。简单而言,适合自己的才是最好的,也欢迎大家真正来体验 AutoMQ ,看看我们到底几斤几两。AutoMQ Kafka 核心代码均已在 GitHub 开源,欢迎来社区一起交流。

参考资料

[1] 中国通信院 云计算白皮书 2023
[2] AutoMQ Kafka 云原生重塑 Kafka 架构
[3] AutoMQ Kafka 成本分析报告
[4] EC2 Spot 的最佳实践
[5] Kafka Tiered Storage Early Access Release Notes
[6]Making Apache Kafka Serverless: Lessons From Confluent Cloud
[7] AutoMQ 单副本高可用
[8] AutoMQ 持续重平衡
[9] AutoMQ 秒级分区迁移
[10] AutoMQ Kafka issue 447
继续阅读
阅读原文