confluent博客解读part1 confluent cloud serveless经验心得

背景

confluent博客解读是一个系列,主要是对confluent以下博客内容的一个阅读总结。

今天我们介绍的是第2篇,主要是关于confluent cloud是如何做serveless kafka的

前言

云上提供serveless能力是软件工程师的最爱,像S3。实现serveless kafka,用户使用起来感觉是没有ZK,没有broker,这里有很多有挑战的问题需要解决,包括:

  • 在保证可用性的情况下,自动弹性扩缩kafka集群
  • 在一个有状态的系统上保证数据存储的均衡性

这篇文章介绍confluent cloud的架构,并且说明confluent是如何通过k8s、confluent opeartor来达成kafka serveless的。

confluent的serveless经验会分成关键的四块来探讨:

  • confluent cloud control plane
  • confluent k8s operator
  • self-balancing cluster
  • infinite storage

confluent cloud总体架构

总体上采用了微服务化、event-driven的架构。这样可以保证:

  • 敏捷开发:confluent的各个开发小组可以敏捷响应用户需求,开发新特性。有各自的发布节奏。
  • 松耦合:服务间通过消息事件总线解耦,有更好的容错性
  • 控制面数据面解耦:一个中心化的全局的控制面和很多运行在不同region上的数据面
  • 运行在K8S上

image.png
该架构的好处是:

  • 解耦故障点:数据面和控制面各自都可以独立工作,互不影响
  • 可审计和可观测:因为都是事件流,可以做事件流回放,排查诊断一些消费慢的场景很有帮助
  • 安全与治理:中心的事件流方便做安全控制,消息溯源
  • 低延迟、消息追赶:数据面彻底解耦控制面后,控制面的一些long run操作可以异步执行,数据面重启恢复就更加快

笔者观点:

  • 采用一个高可用的内置kafka集群来解耦,如果能把系统复杂性和额外带来的副作用控制好,用内置kafka完全解耦控制面和数据面还是非常有价值的,而且可以非常方便的做event driven的架构
  • 虽然微服务化、event driven带来了不少非常不错的好处,例如敏捷开发、更好的event观测和追踪,不过也不要忽视其额外引入的架构复杂性,过早设计过于复杂的系统可能带来额外的负担

confluent cloud控制面

confluent控制面职责:

  • 资源的供应、退役、弹性
  • 访问控制
  • 处理API请求,更新目标状态,例如一些生命周期相关的操作
  • 同步API keys
  • 执行替换策略,主要是处理一些弹性事件
  • etc…

设计CKU

设计confluent unit for kafka(CKU): 避免客户自己去映射云资源和容量的关系。CKU通过设定各个维度资源的值,来表征整体kafka集群的容量。用户修改CKU会反应到一个自定义K8S资源 physical stateful cluster(PSC)。调整CKU的工作流程大致如下:
image.png

笔者解读:先落库持久化,然后PSC感知请求。文中没有提到细节,不过这里应该是做成了一个解耦的形态了,转成PSC的行为应该是watch了数据库的状态。

kafka cluster resize

resize流程如下
image.png

Infinite Storage(tiered storage)

完成分层存储的能力需要控制面提供一个工作流编排引擎。这个编排引擎接受一个spec,然后基于这个spec生成一个执行计划,确保spec关联的所有依赖按照依赖的逻辑顺序正确执行。

image.png

笔者解读:

  • 控制面的编排引擎会负责资源准备
  • bucket provisioner提供的BYOK对数据安全性是个不错的措施

控制面和数据面的交互

利用outbox patterm分发event

outbox pattern本质就是通过CDC来订阅控制面数据库的变更,然后来通知其他watcher,在confluent cloud上主要是各种service

通过mothership kafka的PSC topic分发PSC change event

k8s上PSC资源的更新是通过监听mothership kafka的PSC topic来达成的。watch PSC topic的数据,并且通过k8s api更新到K8S数据面。

控制面也通过mothership kafka监听数据面的操作

由于引入mothership kafka解耦控制面和数据面,数据面的一些信息,例如变更完成事件,也可以让控制面监听mothership kafka的topic来接受事件。

核心理念就是事件驱动,API KEY同步、资源的provisioning和deprovisioning都遵循围绕mothership kafka的事件驱动架构。

笔者解读:如果没有mothership kafka如何做相同的事情?可以引入其他提供watch能力的中间件,也可以自己做轮询。

Confluent Kubernetes Operator

confluent定义的operator主要就是围绕PSC来工作的,执行变更来meet PSC声明的最终状态。operator会和kafka做密切的交互来完成一系列操作。

operator扩容时的执行流程

例如在一个kafka cluster集群的扩容场景,operator会执行如下操作:

  • 确保网络配置正确,新的pod在内部网络和外部网络都可以正常工作
  • 为POD设置持久卷
  • 确保新增的pod的DNS是可以被正常解析的,这样才可以后续将partition data均衡过来,否则会导致后续数据无法正常访问
  • pod creation的时候会提供一个Init容器,提供pod az的布局图给kafka,方便kafka执行replace操作

psc声明

下图是PSC的部分spec内容,声明了kafka broker/pods在一个多AZ k8s集群上的replacement。
image.png

replacement的工作要点

  1. 检查前置条件:执行replacement时,operator会查询metric信息,确保kafka当前是healthy的,并且没有底层的一些分区平衡操作,才可以进行操作。
  2. 状态validation: operator更新完一个broker后会执行pre和post-roll validation,来确保当前broker重启是一个stable和healthy的状态。这些check确保kafka弹性的时候不丧失可用性。
  3. operator状态回显:SBC重平衡、K8S集群、持久卷的供应、POD的状态都需要回显给控制面(主要是编排引擎),下面是一个status状态案例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
status:
conditions:
- last_probe_time: 2021-07-04T13:23:52Z
last_transition_time: 2020-07-12T15:48:06Z
message: Cluster is not resizing, ignore=false
reason: ClusterNotResizing
status: "False"
type: confluent.io/psc/cluster-resizing
- last_probe_time: 2021-07-04T13:23:52Z
last_transition_time: 2021-07-04T00:56:41Z
message: Cluster is not rolling, ignore=false
reason: ClusterNotRolling
status: "False"
type: confluent.io/psc/rolling

笔者解读:这里状态回显的关键是需要梳理好有哪些状态,不同状态表征的含义是什么

Self-Balancing Cluster(SBC’s)

当operator扩展了一个kafka broker以后,会有如下事件发生:

  • kafka cluster controller感知新节点:kafka cluster controller会检测到当前broker所在机器没有任何数据,说明该节点是不均衡的
  • 触发重新均衡:workload还没有发往新的broker节点,等SBC完成充平衡
  • 新broker提供服务:重新均衡后,新的broker可以提供服务

confluent tips:

  • 利用对象存储来做快速的重平衡
  • 重平衡尽量挪动少量数据,快速达到平衡
  • 重平衡需要考虑多个维度的细节,包括replica counts、leader counts、disk和networ用量等。还需要考虑用户剩余磁盘、网络容量充足

Elasticity with dual-tier architecture

一些观点和事实

  • 无论重平衡算法多么好,如果移动大量数据的话是不可能有很好的弹性体验的:因为这会消耗大量的网络带宽、磁盘IOPS和CPU资源
  • confluent依赖分层的对象存储加速重平衡:这样broker节点上的数据(EBS数据卷)只是总数据量的一个子集。通过挪动数据子集可以加速重平衡。(如果没有对象存储,对于一个大型集群来说重平衡会要1~2天,有对象存储则只需要20~40分钟)

confluent存储架构

  • BROKER上存储可以独立工作:即使对象存储offline,broker仍然可以正常工作
  • 使用磁盘扩容来支持更好的弹性
  • **双层架构可以兼容低延迟、高吞吐两种workload: **如果低延迟,则主要读取broker local storage,否则则读取S3

image.png

未来

总体上就是提供无缝的serveless体验,会在以下领域继续深耕:

  • 控制面将请求、集群调整通过mothership kafka分发给数据面
  • 解耦的控制面和数据面
  • Confluent Kubernetes Operator:提供云原生的核心能力,包括SBC、双层存储。结合K8S本身的能力通过和kafka cluster协调完成平滑的弹性体验
  • 充分利用公有云提供的elastic resource