Fork me on GitHub

OPPO 实时计算平台基于云原生的作业弹性伸缩设计与实践

一、背景

Flink目前是业界主流的流批一体的计算引擎。OPPO基于Flink构建的实时计算平台总共运行5000+作业,服务于公司二十几条业务线。我们从2021年开始着手计算上云的工作,目前已经有1/5的作业稳定运行在k8s上。

实时计算任务有以下特点:

  • 资源初始固定。任务需要在提交之前明确资源用量且作业运行过程中不会自动调整
  • 任务常驻。由于数据源多为无界流式数据。一旦有新的流数据进入任务,它会立刻发起并进行一次计算任务,因此整个过程是持续进行的;
  • 负载呈周期性变化。实时作业流量和负载会随着时间的变化出现明显的波峰波谷

由于以上特点带来了以下问题:

  1. 调优困难。通常,用户需要花费大量的时间进行作业调优。例如,新上线一个作业,需要考虑如何配置该作业的资源、并发数、TaskManager个数及大小等
  2. 资源用量无法匹配负载的变化。由于实时作业的负载往往随着流量的变化而变化,初始设置的资源量容易过多或太少,从而造成资源浪费或者资源不足而导致作业延时。而解决这个问题用户往往需要手动重启作业再次设置资源用量,而这种操作繁琐的同时也是滞后的

下图展示的是某作业流量和资源使用情况:

图片

图片

由图中看出作业呈明显的波峰波谷的情况。

二、技术方案

2.1 整体架构

为了解决上述问题,我们设计实现了一种基于云原生的Flink自动扩缩容的方案。整体方案以kubernete为基座,自研的"自动诊断 + 弹性伸缩"为核心。其中自动诊断模块由 SmartResource 负责,弹性伸缩的能力承接由 Flink 计算框架负责。

  • SmartResource负责根据作业上报的指标、用户自定义的监控规则、全链路诊断规则来判断当前作业的运行的健康度,作业是否需要伸缩均源于此。
  • Flink 计算框架使用自研自适应调度器RescaleMonitor,自动感知资源的变化,动态改变算子的并行度,并重新调度作业。

架构图如下:

图片

整体流程如下:

  1. 用户配置自定义的伸缩策略到 SmartResource
  2. SmartResource 将相关的策略转化为告警规则并配置到云监控上
  3. 云监控的数据来自 Flink 集群上报的作业相关的各种指标
  4. 云监控触发告警时回调指定的 SmartResource 接口,SR 根据告警信息,在任务链路打上优化建议Tag;此外,需要调整资源的,结合用户配置的伸缩规则自动计算需要伸缩的副本数,并告知后端服务
  5. 后端服务收到伸缩请求后,再次将此请求在诊断联调上应用一遍来确认是否是个有效的请求,如果是会在数据库中存储一个状态为 pending 的伸缩请求
  6. 当作业的 Checkpoint 完成后,会通知 ostream backend 然后开始执行Flink计算资源的伸缩

2.2 方案详述:

Flink 集群往往由一个 JobManager 配合多个 TaskManager 构成。在纵向上也就是在单个 TaskManager的参数配置上,主要关注 CPU、内存两个方面。在横向上主要关注 TM 的数量。同时 TM 数量也代表了整个集群可用 slot 个数,我们在伸缩的时候也是从纵横向两个方面考虑。

1. 纵向伸缩:

纵向的伸缩主要依赖于 Pod 在声明资源的时候设置 request 和 limit。当我们在创建 TaskManager的时候,在用户配置的基础资源量上额外设置最小资源量(降低下限),最大资源量会略大于用户配置的基础资源量(提高上限)。作业的负载波动的时候,单个 Pod(TM)占用的资源也会在 request 和 limit 之间波动。这样在纵向上,减少资源的固定占用。也能很好的解决堆外内存占用突高引起的容器OOM的问题。

示例配置如下:

图片

图片

2. 横向伸缩

横向的伸缩主要表现在 TaskManager 数量的增减上,TaskManager的增减同样反应整个集群可用的资源数上(slot),但是这里两个问题需要解决:

  • 当 TaskManager增减的时候 JobManager 以及已经申请的 TaskManager不能丢失,也就是怎么保持已申请的资源?
  • 当 TaskManager增减的时候 JobManager 要快速感知新增的 slot,也就是如何感知资源变化并快速调度Job?

2.2.1 云原生独立部署模式

针对横向伸缩第一个问题我们设计了基于 k8s 的独立的作业部署式:kubernetes-standalone-application。此模式会独立部署副本为1的JobManager,副本为n(根据用户指定的并发自动计算)的TaskManager,两者都是采用kubernetes deploy模式部署。由于TM的生命周期不在由JM控制,所以我们可以在外部控制TM的数量,这位我们后续的弹性伸缩打下了基础。

此模式是我们使用云原生的方式封装了Flink自带的standalone模式,在原生的standalone下,假如要部署JobManager、TaskManager的时候需要构建两个非常复杂的的kubernetes yaml文件(例如使用deploy),在部署、平台化方面存在诸多不便之处。我们在原有的命令行基础上支持了standalone的部署模式的支持,兼容现有客户端的各种参数命令。

新的部署模式图如下:

图片

特点如下:

  • 云原生一键部署;
  • 自动推断资源参数;
  • 自研KSA Resourcemanager管理已申请资源;
  • 与现有管理平台无缝集成
  • 通过k8s的watch机制自动追踪资源状态;

部署命令如下:

图片

2.2.2 资源伸缩协调器

针对横向伸缩第二个问题我们设计了 RescaleMonitor ,它是一个伸缩监视器,主要用于快速感知资源的变化并决定是否需要重新调度作业。它首先计算作业所需的资源,计算完资源后,会一直等到可用资源稳定下来,一旦资源稳定,会开始确定作业的实际并行度, 一旦确定了并行度并且执行与可用槽匹配,调度程序就会部署执行。

  • 快速部署 。只要有外部资源增加的时候,RescaleMonitor会判断是否满足调度作业的最小资源,如果满足就会立刻部署 jobgraph,不满足会一直等待。
  • 快速失败 。当有外部资源减少的时候,依赖于Kubernetes的watch机制可以快速感知资源的变化,无需等待资源超时。此处我们以pod名字作为Resource ID,可以在内部快速定位资源建立连接,进而执行各种操作。
  • 安全恢复 。当有外部资源 增减的时候不会立即执行 ,Pending下来等待checkpoint完成的时候会检查是否存在资源增减的Request,如果由才会立即执行重新部署JobGrap并从当前完成的checkpoint恢复,这样在保证作业不丢的情况下,尽量减少重复消费数据的可能。
  • 可固定并发 。支持固定某些算子的最大并发,这在作业类似是消费 kafka 的时候特别有用。
  • UI联动 。当资源变化的时候会实时通知web前端实时显示最新的资源用量。

调度流程示意图:

图片

三、方案实践及效果

3.1 弹性伸缩

我们在平台提供了用户开启弹性伸缩的前端页面,也给出了常用的默认设置,如下图所示,在CPU利用率大于70%并持续5分钟的时候,开启扩容,在CPU利用率小于30%并持续5分钟的时候,开启缩容。

图片

弹性伸缩效果图如下,通过途中可以看出,作业在cpu利用率低时,自动降低的并发。

图片

我们记录了每次伸缩的事件,包括时间、触发原因,伸缩前后的资源等,方便平台跟用户跟踪资源的变化和排查问题。

图片

3.2 自动诊断

以某作业为例,此次作业的存在明显的波谷,且资源利用率很低。

图片

图片

通过我们的智能诊断服务可以自动优化整个作业的资源配比,在不重启作业的情况下完成TM的动态调整。

图片

优化后的效果如下

图片

经过我们计算经过自动诊断加弹性伸缩可以为业务带来至少20%的成本降低。

其实成本降低只是收益一方面,让流量更加契合资源,让诊断更加智能,让作业稳定高效的运行,才能更好的服务于下游业务。也是我们实时计算平台服务的宗旨。

四、后续规划

后续我们在继续优化弹性伸缩效果的基础上,继续朝着下面几个方向努力。

  • batch任务的自适应调度
  • 基于机器学习的自动化诊断
  • 流批一体的错峰调度

附录

  1. https://nightlies.apache.org/flink/flink-docs-master/zh/docs/deployment/elastic_scaling/
  2. https://cwiki.apache.org/confluence/display/FLINK/FLIP-159%3A+Reactive+Mode
  3. https://cwiki.apache.org/confluence/display/FLINK/FLIP-160%3A+Adaptive+Scheduler
  4. https://kubernetes.io/zh-cn/docs/tasks/run-application/horizontal-pod-autoscale/

作者简介

John,OPPO实时计算平台高级研发工程师,Apache Flink Contributor,长期专注于大数据计算领域。


本文地址:https://www.6aiq.com/article/1663338974448
本文版权归作者和AIQ共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出