作者: 舟柒、楼台
1. 业务背景与挑战
1.1 实时计算集群现状
关于热点机器处理一直是阿里云 Flink 集群运维的一大痛点,不管在日常还是大促都已经是比较严重的问题,同时这也是分布式系统的老大难问题。而在今年整个阿里云成本控制的背景下,随着集群水位的逐步抬升,热点问题愈发严重。日均有上千次的热点机器出现,并且在晚上业务高峰期,整个热点持续时间会超过 60min,对于业务以及对于平台影响是比较大的。
这里的影响是体现在稳定,成本,效率三方面的,首先热点会导致作业的部分节点延时,高 SLA 要求任务的性能得不到保障,类似于下面这张图。其次,受热点机器这个短板的影响,整体集群水位没有办法进一步抬升,影响成本控制。最后在我们没有一个比较好地机制去处理热点的情况下,是需要人肉并且非常被动地去处理热点,费时费力,影响效率。
面对全面影响运维领域(稳定成本效率三个方面)的热点问题,我们开发的 Flink 集群的自愈系统,出发点就是要解决 Flink 集群的热点机器问题。
1.2 热点问题分析
接下来具体分析为什么会产生机器热点。实际上不管说集群是 20% 还是 60% 的物理水位,整体集群的物理资源对于承载业务来说都是够用的,K8S 的调度器默认会按照作业的 request 值去尽量平铺。但是目前阿里云的 Flink 集群是允许任务进行超用的,就会出现下面这个图的情况。
当任务配置不合理时,会导致部分的 Pod 超用,部分 Pod 不超用。使得机器之间虽然 request 差距不大,但是实际的usage 就会偏差比较大,毛刺的部分节点就会成为热点机器。上面这幅图我们可以看到一台机器上 Pod 的实际消耗在业务高峰期会超过整体的申请值,这个时候就会造成热点机器。
我们可以得出结论:热点的出现与水位绝对值多少没有必然关系,而与作业的超用程度正相关。因此 Flink 热点的治理都是围绕着解决作业超用问题展开的。
2. 成本优化 - 热点处理
2.1 热点机器处理思路
面对热点机器问题,可以分为「大促」与「日常」两类场景。
针对大促场景,任何在大促峰值期间做的运维操作,对于业务来说都是有损的,因此要尽量避免大促期间发生热点而去执行运维操作,但是我们通常可以通过压测去合理地预估作业在峰值的行为,通过一些提前的预测的方法在大促前规避作业超用,从而规避热点的出现。因此,我们设计了一套基于作业的画像系统,在热点发生的事前去干预热点的出现。
针对日常场景,在 7*24 小时保障要求下,热点发生的频次高且类型多。叠加用户操作,平台变更等非预期行为干扰,往往热点的出现是相对随机且没有办法提前预测的。我们开发了热点的事中自愈能力,追求热点隐患出现后的快速消除,当热点隐患发生后,能够在敏感任务受影响的时效内完成热点压制。而与之相配套的事后恢复与运维可观测性能力也同步建设。
整体的思路上,仍是围绕处理超用任务来做,事前画像是提前规避超用,事中自愈则是对超用的任务进行应急处理。针对热点发生的时间线,形成了事前画像,事中自愈,事后恢复,可观测性四部分的热点机器解决方案。接下来我会详细展开介绍这四个部分的能力。
2.2 事前画像
2.2.1 业务流程
首先什么是事前画像。前面提到的,事前画像是针对大促场景下,通过对超用任务的提前规避,来实现消除热点的方案。要提前规避任务超用,就需要作业在大促峰值的资源配置是精准的。但是目前参与大促的任务,往往作业拓扑是非常复杂的,且作业的数量非常巨大。单纯的依靠数据研发同学人肉地去调优每个任务的每个节点是不现实的。因此,事前画像就是用来解决超大量复杂作业资源精准配置的问题,让作业在峰值期间不超用运行。
我们设计了一套自动迭代系统,输入每个任务的目标 RPS,通过全链路的压测平台与 Flink 任务管控平台,让作业经过启动压测任务,灌入压测流量,生成作业画像结果,停止压测流量,停止压测任务,作业利用率及 RPS 实际值比对,更新画像结果到压测任务的模拟压测流程,自动化地执行 N 轮迭代,产出一份作业 usage 与 RPS 均符合预期的资源配置文件。整个迭代过程不需要数据研发同学参与,全自动完成。
2.2.2 实现方案
除了自动化的迭代流程外,整个环节中最为关键的就是画像是如何产生任务资源配置的,这里的能力是依托于阿里云作业管控平台中的 Autopilot 服务来完成的。
Autopilot会定时采样每个 TM 所有资源使用信息(包括 cpu usage, mem usage, delay)等,统计在压测峰值窗口内TM 使用的资源 p95 值,剔除毛刺或倾斜的 TM 带来的干扰,并通过 TM 到 ssg 到 task 的映射关系,生成符合大促峰值需求的配置。
2.2.3 实现效果
在今年的双 11 大促,事前画像的能力也得到了了充分的验证。这 5 个图中轮次 1 代表画像迭代前,轮次 2 代表画像迭代后。
今年大促,实时链路团队紧贴 DT 业务方深入合作,创新性地提出作业画像结合压测来智能化地学习、调整作业合理资源配置,不仅在稳定性上实现集群 65% 水位 0 热点,而且帮助业务降低 17% 成本(如上图所示),更是帮助 DT 完成上百个作业数千次自动化调优。同时还提出了集群按业务优先级错峰管理模式,通过 VVP 限流功能与作业动态布局,达成不同优先级作业在各自大促峰值期间满足延迟要求,提升了集群整体水位,二者在双 11 生产首次应用,节约了 100 台机器,在后续大促中也可持续优化应用。
2.3 事中自愈
2.3.1 技术选型
面向超用作业,如何进行事中自愈有多种方案,目前阿里云内部的其他分布式系统主要有以下四种方案 :
a.全局允许超用,也就是以前Flink集群使用的方案
b.全局强限制不允许超用;
c.通过静态的规则限制超用;
d.动态限制超用能力。
由于实时计算的业务是有其特殊性的(比如说需要有一定的 burst 的能力应对「回追/FO/大促」的场景;低优作业往往倾向于通过牺牲一定程度的性能来换取成本减少;复杂拓扑下不同算子或轻或重的倾斜问题导致资源需求不同等等这些要求)。我们认为超用对于实时任务是一个长期会存在且合理的需求,结合日常,故障,压测,大促几种场景比较,第四种方案,也就是通过 Inspector 来实现局部的动态限流是比较合适的方案。在热点出现时自动化地针对热点隐患机器上的 作业 进行限流及重调度,让热点消除。而在热点消除后,再根据实际情况重新恢复作业的超用能力。
2.3.2 业务流程
整体业务流程总的来说分为三个部分,感知,决策,执行。对于事中自愈来说,通过设定阈值与机器学习的方法识别机器异常进行感知,基于SRE 经验沉淀的决策树对作业进行决策,通过降级,限流,驱逐等作为执行方法。
2.3.3 难点与创新
2.3.3.1. 决策树(难点)
在决策这一部分中,以 SRE 的经验,我们总结了针对热点处理的决策树,用来判断需要选择什么作业来进行操作。这个决策树是基于「业务影响」与「热点快速消除」这两者的平衡来做。对于平台侧而言,肯定是想要热点消除的越快越好,也就是直接剔除掉使用最高的作业好了。但是另外一方面,平台需要承接业务,需要考虑业务方面受不受得了这种操作,也就意味着平台上的作业会有不同的需求,包括有的作业不接受 FO / 延迟,对他们的作业操作后,我们需要给他们有个解释等等,这对如何挑选作业增加了难度,需要根据不同的场景在这里进行不同的取舍。我们将业务的重要程度抽象为作业的优先级指标,整个决策树需要考虑的因子包括作业的优先级,超用程度,规格,黑白名单,以及血缘关系等。
对于具体的决策树而言,我们首先是考虑业务,追求尽量降低对高优先级业务的影响以及减少对业务的影响面,因此我们优先考虑非白名单的,低优的,超用多的,使用高的作业进行操作。但是由于优先级和超用数值并不是消除热点的最优路径,如果一些优先级高的作业使用非常多导致的热点,也就意味着 Inspector 需要限制很多低优先级的任务才能把热点降下来,而这意味着,从集群层面来说,对业务的影响面反而增大,并且热点消除时间相对变长,为了减少影响面以及考虑到可解释性,我们还增加了从快速消除热点为出发点的规则,也就是如果优先级高并且超用高于阈值,我们会优先处理这类作业。另外,对于对作业操作的数量来说,我们会设置一个回收的限度,仅回收足够资源让机器不热为止。
2.3.3.2 执行上的创新
上述说的是决策树方面偏重业务层的考虑,接下来介绍再针对作业驱逐的执行操作上的,且深入到软件侧的技术创新点:
对于「驱逐」操作而言,一般对 Pod 进行驱逐的过程,是将驱逐请求发往 API Server,删除 Pod 后,由调度器选择其他节点将 Pod 调度上去。之前黑盒调度指的是在调度器这一环节上,Pod 会受到调度器各种插件的影响,给每个节点进行打分,相对 Inspector 而言,最终 Pod 具体调度到哪一个节点的不确定性比较大,不太明确,这会阻碍 Inspector 后续决策的进一步优化。而我们针对这一点,提出白盒调度,将调度逻辑放入 Inspector 内部,由 Inspector 根据内部算法指定 Pod 所调度到的节点。整个过程会更明确和可控。具体白盒调度实现细节就是 Inspector 会创建 K8S 的 自定义的资源 ReserveResource,在里面指定好这个资源的所属 owner 就可以了,调度器这边已经更改好调度的逻辑,适配 Inspector,每次调度 Pod 的时候,会先看 ReserveResource 这个预留的资源的 owner 里面,是否有这个 Pod,如果有,就白盒调度到指定的 Node 上去。不走调度器本身复杂的调度逻辑。
执行的操作方案,除了「驱逐」,还有「限流」,限制整个作业的资源使用量。整个流程为 Inspector 向 JM 发出一个异步的更新请求,发送完请求 Inspector 这边流程就结束,JM 接收到这个请求之后,先持久化到 TM Pod 创建模板里,保证 新创建的 TM 以及 TM FailOver 了之后,所有 TM 资源限制配置是一样的,JM 保证了增量的 TM 一致后,再去修改线上存量的 TM 配置,向 API Server 发送 patch 请求修改 TM Pod spec 里的资源配置,TM Pod 所在节点的 kubelet 就会 watch 到这个变化,从而去修改 TM Pod 的 cgroup,借用 Linux 的能力动态限制 TM 的资源使用情况,从而形成「限流」这么一个效果。Inspector 之后会异步地去检查限流是否生效,与此同时在 K8S Event 中也可以观察到这个变化。
2.3.4 效果
从执行效果这一角度来看 Inspector 整个事中自愈,热点的消除的过程,比较 high-level 一点,可以说就是降低低优任务的使用,将资源让位给高优任务,从而降低它的延迟,这么个效果。对应图中来讲,从 CPU 维度而言,就是通过限制左边这个低优任务的 CPU 使用,缓解集群热点情况,让右边的高优任务延时降低。
上图为 Inspector 整体的集群效果,流量高峰期间,产生了 100+ 的热点,通过开启 Inspector,将热点在 3min 内完全压制。
从单作业角度来看,当热点机器消除后,很明显的,处于热点机器的 TM 延迟就被消除了,数据就会回追回来。
2.4 事后恢复
2.4.1 业务流程
讲完事中自愈,对于事后恢复流程来说,刚刚事中自愈中被限流的作业,如果在空闲机器,它有使用需求,那么就需要把限制放开来。通过这种让作业使用量的峰值错开,在高优任务使用峰值过去后,低优任务仍能够使用空闲的机器资源,恢复延时,从而达到兼顾业务稳定性以及提高集群水位的效果。
2.4.2 决策树
同样,面对如何挑选作业的问题,也会有一个制衡,整个过程就和洗手类似,如果水龙头直接放开到最大,就会水花四溅,对比这里如果直接放开作业的资源使用过猛,单台机器作业资源使用量突然快速增加,就会将机器迅速打到热点,从而引发 Inspector 事中自愈,又会将作业资源使用量限制回去,反反复复就会导致如图中所示的这种「震荡」的问题,影响集群和作业的稳定性。因此,我们需要通过合理控制作业恢复的频率与数量,让作业能够平缓地追数据,避免震荡。
2.5 可观测性
2.5.1 整体建设思路
作为平台的 SRE,在服务上线后,需要特别关注服务的「业务效果」与「用户影响面」,比如看 Inspector 有没有做出一些有害的操作,出现有害的操作的时候,需要提前想好它的预案,立马停止不当的操作并且回滚回去。因此需要建立完整的可观测性能力。总体来说,可观测性可以分为两个角度,一个是偏运维的 SRE 角度,一个是偏使用的用户角度。
对于 SRE 而言,当有热点机器的时候,我们需要看 Inspector 有没有做出操作,以及这个操作是否合理,因为正常情况下是没有热点机器的,再看看整个决策树是否可以优化。另外当天的结果复盘时,可以看看 Inspector 的影响面如何,从优先级角度而言,可以看到高优任务受影响比例,形成一个简单的报表,这些点我们都需要可视化,来帮助 Inspector 的不断成长从而提高集群的安全水位。
对于用户角度而言,当他们的作业出现「延迟」 或者「FO」的情况时,可以用「可视化的大盘」来查看这些情况是否是 Inspector 操作导致的影响,从而让用户自己考虑优化作业,包括调整资源,作业优先级调整等等,让用户答疑自闭环,减少工单量。
2.5.2 效果
那么具体实现效果图而言,可以在 Grafana 上配置大盘,可以看到这段时间 Inspector 操作过的作业数目,以及各个优先级作业汇总情况,以及 误操作的作业名单,Inspector 操作的详情,包括热点机器 sn,作业优先级,热点机器超用数值等等。这些都可以帮助 Inspector 不断的优化迭代。
给用户呈现的大盘里面,用户可以通过「作业名字」来进行查询,得到 Inspector 是否有操作过这个作业,以及相应的修改建议等等。
3. 整体规划和未来方向
3.1 计划蓝图
Flink Cluster Inspector 的定位是面向于集群异常的自愈系统,期望通过稳定,成本,效率三大领域的异常进行自愈,实现 Flink 集群的自动驾驶。当前的能力主要集中于成本领域,主要是针对各类热点机器进行自愈,包括 CPU,内存,磁盘三个维度。后续会往稳定性领域,也就是单节点的异常,系统服务的异常以及集群所承载的业务异常进行自愈,同时会往效率领域拓展,对于整体集群水位以及库存方面的异常通过弹性的扩缩容能力进行自愈。
目前整个系统都是围绕云原生构建的,在 Flink 系统云原生化的背景下,运维能力也全面云原生化,基于云原生的技术栈如 Operator,SideCar,申明式 API 等,构建了围绕 CR 开发,基于 GitOps 的 CI/CD,以及可观测高可用的运维。而承载上述业务能力的技术内核,则是紧贴于调度系统的异常自愈框架,我们抽象了针对异常自愈的感知,决策,执行三个关键环节,并通过一个统一的框架整合,方便各运维同学以插件式的方式快速实现各自的业务需求。同时也深度贴合调度层,与 K8S 的同学协同,开发面向于 Flink 场景的调度器增强能力。
3.2 顶层视角
基于以上 Inspector 的能力,我们将 Flink 集群打造成为一个将成本稳定性效率串联起来的联动系统,进行精细化的成本控制。我们定义了 24h 平均安全水位和瞬时安全水位,分别用来衡量集群的成本控制能力,和集群稳定性问题临界能力。通过设置当前所能达到的安全水位,当集群水位较低的时候,能够通过隔离业务,在业务影响面最低的情况下将机器下线。而如果集群水位过高,则快速申请预算,进行机器的扩容。通过 Inspector 中动态容量维持模块,将集群水位始终控制在安全水位附近。
在某些紧急情况下,集群的瞬时值可能会过高,触发集群整体稳定性问题。当水位已经超过了所能够承载的安全水位上限,则需要进入应急处理流程,通过 3-5-20 报警处理,批作业限制,大促紧急预案等消除异常。当水位仍在安全水位范围内,由自愈服务进行兜底,通过自动化的将异常的机器或者业务进行隔离或限制,将稳定性问题自动恢复。容量模块不断抬升水位,锤炼自愈服务的可靠性,而自愈能力的增强,又能够反过来提升安全水位,进而抬升集群水位,形成联动。