快手:国内Top级大规模集群调度优化实践

房孝敬 2021-03-17 10:15:05
作者介绍

房孝敬,快手 | 大数据架构师,2017年加入快手,负责快手大数据资源调度方向,包括超大规模分布式资源调度,分布式计算,AI架构等。此前曾在阿里,腾讯,负责搜索&大数据系统研发工作,hadoop/spark/k8s社区的contributor。

导读:随着公司业务的快速发展,离线计算集群规模和提交的作业量持续增长,如何支撑超大规模集群,如何满足不同场景的调度需求成为必须要解决的问题。基于以上问题,快手大数据团队基于YARN做了大量的定制和优化,支撑了不同场景下的资源调度需求

 

今天的介绍会围绕下面四点展开:

 

  • 调度相关背景及快手数据规模与场景

  • 快手调度器Kwai scheduler介绍

  • 多调度场景优化介绍

  • 其他工作&未来规划

 

一、快手数据规模场景

 

 
1. 快手数据规模

 

目前快手离线计算单集群数万台机器,每日处理数百P数据量,百万级别作业,对大数据存储,计算,调度有非常大的挑战。首先介绍下快手大数据架构体系技术栈。

 

 
2. 快手大数据体系架构介绍

 

 

快手大数据架构底层采用hdfs/hbase构建数据存储层,用于支撑海量数据的存储;上层是YARN资源调度层,实现百万级别的作业和任务调度;再上层是各种计算引擎构成的执行层,如Flink、MR、SPARK,PRESTO,TensorFlow等计算框架用于执行业务的计算任务,最上层属于应用层如FLink作业托管平台,机器学习平台,以及SQL提交平台,面向用户提供服务。本次分享的YARN属于资源调度层,用于把计算引擎的Task快速调度到合适的机器上。

 

 

3. YARN资源调度系统介绍

 

  • YARN背景介绍:

 

 

 

YARN是Apache Hadoop旗下的顶级项目,Hadoop 2.0发布时引入,主要用于解决hadoop1.0面临的集群调度性能和扩展性问题。通过把集群资源管理和作业资源管理拆分成ResourceManager和ApplicationMaster两个组件,实现调度架构从单级架构向二级架构的转变,提升了集群性能。YARN专注于集群资源管理和调度,包含ResourceManager和NodeManager两个核心组件;ResourceManager负责集群资源管理和分配;NodeManager在每台机器上部署,负责管理所在机器上资源。

 

  • YARN调度器演进过程:

 

 

 

原生YARN在调度过程中,先选择一个节点,并对队列进行排序,递归从root队列找到最优的叶子队列,再对叶子队列中运行的app进行排序,选出app在这个节点上调度资源。随着集群规模增长和队列数目的增加,调度耗时越来越长,调度吞吐成为制约集群规模的主要瓶颈。为提升调度吞吐,调度器的发展经历了三个阶段:

 

  • 第一阶段通过心跳触发调度过程,实现比较简单,但心跳处理逻辑和调度逻辑在同一个线程,调度和心跳处理逻辑会相互影响。

 

  • 第二阶段将调度逻辑剥离到单独的线程以降低调度和心跳逻辑耦合性,从而提升了调度性能;但调度逻辑和心跳处理共享一把大锁,并且调度过程中对队列排序占据大量时间,整体性能提升有限。

 

  • 第三阶段引入全局调度器的概念,可以并发对队列资源进行调度,最终通过统一的commit过程保证调度结果一致性。多线程并发调度可以提升调度性能,但没有解决调度过程中排序耗时过多问题,并且引入的多线程调度,会损害调度结果的公平性。

 

快手基于fair scheduler 单线程调度版本,不断优化单线程调度的性能,但由于单线程调度的局限性,在集群节点接近万台规模时,集群性能出现瓶颈;上线自研的kwai scheduler调度器后,在集群调度性能上有极大的提升,目前单集群规模已达数万台,同时在调度策略方面,支持可插拔的调度架构,方便扩展新的调度策略。

 

二、Kwai scheduler调度器介绍景

 

 
1. 基于集群状态做全局批量调度

 

 

Kwai scheduler整体架构如上图所示,ResouceManager中RPC层和事件处理层基本保持不变,主要改动点是将调度逻辑做一个整体的剥离替换原先的fair scheduler调度。每次调度过程中拉取集群状态做镜像,基于集群镜像并发批量调度,调度完成后,将调度结果推送回去。App可以通过原有的心跳接口获取调度container。

 

 
2. Kwai scheduler 调度流程

 

 

Kwai scheduler 基于集群镜像(节点的资源使用情况;队列的最小资源和最大资源量,以及当前资源使用量,APP资源使用量和资源需求量等)进行资源的预分配,计算出每个APP可以在这一轮调度中分配多少资源。APP根据预先分配到的资源量,并发去竞争节点上的空闲资源,如果竞争成功,完成APP的资源调度过程。

 

APP资源调度过程中,可以根据不同场景为 APP配置不同的调度策略,根据调度策略过滤节点并计算每个节点分数,选出分数最高节点尝试进行资源分配。调度过程中基本都是CPU密集操作,避免了锁的干扰(不同APP竞争节点资源时有轻量的自旋锁),有非常高的性能。并且不同的APP可以多线程并发调度,具备很好的扩展性。

 

 
3. Kwai scheduler 调度策略

 

 

Kwai scheduler 调度策略主要实现filter和score接口。filter接口用于过滤节点,score根据节点信息,为节点进行打分,然后选出最优节点进行调度。比如APP task打散策略,根据每个节点分配的APP资源量,对节点进行打分,节点上分配的APP资源量越多,节点分数越低,从而把APP的task在集群范围内打散到不同的节点。

 

 

 
4. Kwai scheduler调度线上效果

 

Kwai scheduler 上线后,支撑单集群数万台机器,1万+作业同时运行,每天调度吞吐量峰值5w/s+,资源分配率93%+,同时支持不同的调度场景。

 

三、多调度场景优化

 

 
1. 离线ETL场景

 

离线场景下如何保障核心作业的SLA是比较核心的问题。在快手,核心作业和普通作业在同一个队列中,通过完善作业分级保障能力和异常节点规避能力,保障核心作业的SLA。

 

离线ETL场景中经常会遇到以下情况以及相应的优化方案:

 

1)其他队列作业大量占据资源不释放

 

通过优化队列间资源抢占来解决这个问题。为防止抢占影响过大,默认情况下只有高优先级核心作业触发抢占,并且会限制每轮抢占的最大资源量。抢占过程中根据作业优先级,饥饿等待时间等条件动态计算每个队列可以抢占的资源量,从而把资源倾斜给优先级更高,饥饿等待时间更长的作业。

 

2)队列内低优先级作业占据大量资源不释放

 

在生产场景下如果低优先级作业占用大量资源不释放,导致优先级比较高的任务无法获取到足够资源,从而导致产出延迟。为解决这个问题提出基于虚拟队列来保障高优先级作业产出。所谓虚拟队列,是在物理队列下,按照一定逻辑规则(比如优先级)抽象出的逻辑队列。每个虚拟队列有一定的资源配额,并且会触发物理队列内部的抢占,从而解决上面的问题。

 

3)低优先级作业占据app solt不释放

 

为方便AppSlot资源的管理,抽象出minApp概念,如果App启动时,队列running App小于minApp,将会立刻启动App,不会受限于父队列的maxRunningApp,这样在队列层面保障有可预期的app slot。但同样存在一个问题,队列内部低优先级作业占据大量AppSlot不释放,导致高优先级作业启动延迟。为此提出了App Slot抢占功能。如下图所示,如果发现高优先级作业(P0)长时间pending不能启动,扫描队列内runningApp,选择低优先级作业进入睡眠模式(不再调度新task,极端情况下回收task)从而释放出slot资源,保障高优先级作业能及时启动。

 

 

4)回溯作业影响生产作业

 

回溯作业的特点在于大量提交多个作业,如果不加控制可能会影响生产作业的产出。主要方案是限制回溯作业最大资源量和最大运行APP数目,将影响控制在一定的范围以内。但是限制最大资源量和运行数目导致大量回溯作业在yarn处于pending状态,对yarn有比较大的压力,通过与上游调度系统打通,反压上层工作流调度系统,阻止新提交的回溯作业,从而减轻了YARN负载。对于已经提交到yarn上的作业,会限制每个队列最大pending app个数,从而保障总体pending app数目可控。

 

5)高优先级作业大块资源请求不能及时满足

 

原有的Reserve机制中,调度器可以reserve一批节点,不再调度新task,等待节点上自然释放资源。如果被reserve节点资源长时间不释放,如何处理?针对这个场景开发了reserve抢占功能,用于抢占reserve节点上的低优先级的container,从而保障节点上有足够的空闲资源启动高优先级作业。

 

6)规避异常节点,避免核心作业长尾

 

通过采集节点物理指标,task失败率,task运行速度,以及shuffle失败率等,将此节点标记为异常节点,不再调度新Task。从而尽量减少异常节点的影响范围,规避其导致的Task长尾,失败问题。

 

 

2. Adhoc即时查询场景

 

AdHoc场景主要着力于提升每个用户的查询体验。

 

通过虚拟队列技术,从user维度来划分虚拟队列,实现基于user公平的资源的分配,配合基于user的资源抢占,从而避免大量资源被某一个用户占用,导致其他用户长时间得不到资源。

 

 

 

3. 机器学习训练场景

 

机器学习训练场景下,资源需求呈现all or nothing特点,在队列资源紧张时,如果基于yarn原生的公平调度方式,为每个app分配部分资源,容易产生资源分配死锁问题。为此我们采用APP轮转调度策略,采用类似FIFO策略,保障头部APP(头部会动态变化,轮转策略名称的由来)的资源需求,避免死锁问题。

 

 

4. Flink实时作业场景

 

FLink实时场景下,主要介绍故障发生时,如何尽量减少故障的影响范围,以及如何快速恢复故障作业:

 

通过cpu均衡调度,避免机器cpu热点。

 

通过AM失败节点规避机制,避免调度到AM失败机器。

 

NM挂起(不调度新Task,介于RUNNING和LOST状态)机制,防止NM异常退出导致Task失败。

 

基于Hawk秒级发现节点宕机,快速进行作业恢复。

 

 

虽然可以基于Hawk秒级发现节点宕机,但作业恢复过程可能需要几分钟(申请资源,下载jar包,job recover等)。我们通过资源冗余分配策略,优化掉其中资源申请和下载jar包过程,最终实现秒级作业恢复。

 

四、其他工作&未来规划

 

  • 支持超大规模集群:

 

主要目标支撑十万量级的集群规模,目前基于社区的federation方案进行改造。

 

  • Hadoop跨IDC集群建设:

 

受限于公司物理集群规划,离线集群会分布在不同的IDC,如何基于有限的跨IDC带宽,对数据和计算进行合理排布,是一个非常有挑战的问题。

 

  • 在离线资源混合部署:

 

基于在线机器的空闲资源运行离线任务,在资源调度和隔离方面有很多工作要做,目前已经取得一定收益。

 

  • 在离线资源统一管理:

 

目前YARN托管离线调度,k8s托管在线调度,如何让资源更弹性更统一?我们也在做一些尝试。

 

  • 流shuffle服务建设:

 

shuffle过程产生大量大量的随机IO,通过流shuffle服务接管MR和SPARK shuffle过程,将随机IO转变成顺序IO,提升集群算力并减少在离线混部过程中IO影响。

 

今天的分享就到这里,谢谢大家。

 

作者丨房孝敬
来源丨公众号:DataFunTalk(ID:datafuntalk)
dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn
活动预告