YARN做为Hadoop的资源管理系统,负责Hadoop集群上计算资源的管理和做业调度。html
美团的YARN以社区2.7.1版本为基础构建分支。目前在YARN上支撑离线业务、实时业务以及机器学习业务。node
YARN面临高可用、扩展性、稳定性的问题不少。其中扩展性上遇到的最严重的,是集群和业务规模增加带来的调度器性能问题。从业务角度来看,假设集群1000台节点,每一个节点提供100个CPU的计算能力。每一个任务使用1个CPU,平均执行时间1分钟。集群在高峰期始终有超过10万CPU的资源需求。集群的调度器平均每分钟只能调度5万的任务。从分钟级别观察,集群资源使用率是50000/(100*1000)=0.5,那么集群就有50%的计算资源由于调度能力的问题而没法使用。linux
随着集群规模扩大以及业务量的增加,集群调度能力会随着压力增长而逐渐降低。假设调度能力依然保持不变,每分钟调度5万个任务,按照5000台节点的规模计算,若是不作任何优化改进,那么集群资源使用率为:50000/(100*5000) = 10%,剩余的90%的机器资源没法被利用起来。算法
这个问题解决后,集群在有空余资源的状况下,做业资源需求能够快速获得知足,集群的计算资源获得充分地利用。apache
下文会逐步将Hadoop YARN调度系统的核心模块展开说明,揭开上述性能问题的根本缘由,提出系统化的解决方案,最终Hadoop YARN达到支撑单集群万级别节点,支持并发运行数万做业的调度能力。性能优化
YARN负责做业资源调度,在集群中找到知足业务的资源,帮助做业启动任务,管理做业的生命周期。bash
YARN详细的架构设计请参考Hadoop官方文档。数据结构
YARN在cpu,memory这两个资源维度对集群资源作了抽象。多线程
class Resource{
int cpu; //cpu核心个数
int memory-mb; //内存的MB数
}
复制代码
做业向YARN申请资源的请求是:List[ResourceRequest]架构
class ResourceRequest{
int numContainers; //须要的container个数
Resource capability;//每一个container的资源
}
复制代码
YARN对做业响应是:List[Container]
class Container{
ContainerId containerId; //YARN全局惟一的container标示
Resource capability; //该container的资源信息
String nodeHttpAddress; //该container能够启动的NodeManager的hostname
}
复制代码
名词解释
调度流程
资源申请和分配是异步进行的。ResourceScheduler是抽象类,须要自行实现。社区实现了公平调度器(FairScheduler)和容量调度器(CapacityScheduler)。美团点评根据自身的业务模式的特色,采用的是公平调度器。
在公平调度器中,做业(App)是挂载以下图的树形队列的叶子。
对于每层队列进行以下流程:
例如,某次调度的路径是ROOT -> ParentQueueA -> LeafQueueA1 -> App11,此次调度会从node上给App11分配Container。
伪代码
class FairScheduler{
/* input:NodeId
* output:Resource 表示分配出来的某个app的一个container的资源量
* root 是树形队列Queue的根
*/
synchronized Resource attemptScheduling(NodeId node){
root.assignContainer(NodeId);
}
}
class Queue{
Resource assignContainer(NodeId node){
if(! preCheck(node) ) return; //预先检查
sort(this.children); //排序
if(this.isParent){
for(Queue q: this.children)
q.assignContainer(node); //递归调用
}else{
for(App app: this.runnableApps)
app.assignContainer(node);
}
}
}
class App{
Resource assignContainer(NodeId node){
......
}
}
复制代码
公平调度器是一个多线程异步协做的架构,而为了保证调度过程当中数据的一致性,在主要的流程中加入了FairScheduler对象锁。其中核心调度流程是单线程执行的。这意味着Container分配是串行的,这是调度器存在性能瓶颈的核心缘由。
上文介绍了公平调度器的架构,在大规模的业务压力下,这个系统存在性能问题。从应用层的表现看,做业资源需求得不到知足。从系统模块看,多个模块协同工做,每一个模块多多少少都存在性能问题。如何评估系统性能已经能够知足线上业务的需求?如何评估系统的业务承载能力?咱们须要找到一个系统的性能目标。所以在谈性能优化方案以前,须要先说一说调度系统性能评估方法。
通常来讲,在线业务系统的性能是用该系统可以承载的QPS和响应的TP99的延迟时间来评估,而调度系统与在线业务系统不一样的是:调度系统的性能不能用RPC(ResourceManager接收NodeManager和AppMaster的RPC请求)的响应延迟来评估。缘由是:这些RPC调用过程跟调度系统的调度过程是异步的,所以不论调度性能多么差,RPC响应几乎不受影响。同理,不论RPC响应多么差,调度性能也几乎不受影响。
首先从知足业务需求角度分析调度系统的业务指标。调度系统的业务目标是知足业务资源需求。指标是:有效调度(validSchedule)。在生产环境,只要validSchedule达标,咱们就认为目前调度器是知足线上业务需求的。
定义validSchedulePerMin表示某一分钟的调度性能达标的状况。达标值为1,不达标值为0。
validPending = min(queuePending, QueueMaxQuota)
if (usage / total > 90% || validPending == 0): validSchedulePerMin = 1 //集群资源使用率高于90%,或者集群有效资源需求为0,这时调度器性能达标。
if (validPending > 0 && usage / total < 90%) : validSchedulePerMin = 0;//集群资源使用率低于90%,而且集群存在有效资源需求,这时调度器性能不达标。
复制代码
设置90%的缘由是:资源池中的每一个节点可能都有一小部分资源由于没法知足任何的资源需求,出现的资源碎片问题。这个问题相似linux内存的碎片问题。因为离线做业的任务执行时间很是短,资源很快能够获得回收。在离线计算场景,调度效率的重要性远远大于更精确地管理集群资源碎片,所以离线调度策略暂时没有考虑资源碎片的问题。
validSchedulePerDay表示调度性能天天的达标率。 validSchedulePerDay = ΣvalidSchedulePerMin /1440
目前线上业务规模下,业务指标以下: validSchedulePerMin > 0.9; validSchedulePerDay > 0.99
调度系统的本质是为做业分配Container,所以提出调度系统性能指标CPS--每秒调度Container数。 在生产环境,只要validSchedule达标,代表目前调度器是知足线上业务需求的。而在测试环境,须要关注不一样压力条件下的CPS,找到当前系统承载能力的上限,并进一步指导性能优化工做。
CPS是与测试压力相关的,测试压力越大,CPS可能越低。从上文公平调度器的架构能够看到,CPS跟以下信息相关:
例如,集群1000个节点,同时运行1000个App,这些App分布在500个Queue上,每一个App的每一个Container执行时间是1分钟。在这样的压力条件下,调度系统在有大量资源需求的状况下,每秒能够调度1000个Container。那么在这个条件下,调度系统的CPS是1000/s。
在线上环境中,咱们能够经过观察上文提到的调度系统的指标来看当前调度性能是否知足业务需求。但咱们作了一个性能优化策略,不能直接到在线上环境去实验,所以咱们必须有能力在线下环境验证调度器的性能是知足业务需求的,以后才能把实验有效的优化策略推广到线上环境。
那咱们在线下也搭建一套跟线上规模同样的集群,是否就能够进行调度器性能优化的分析和研究呢?理论上是能够的,但这须要大量的物理机资源,对公司来讲是个巨大的成本。所以咱们须要一个调度器的压力模拟器,在不须要大量物理机资源的条件下,可以模拟YARN的调度过程。
社区提供了开源调度器的压力模拟工具--Scheduler Load Simulater(SLS)。
如上图,左侧是开源SLS的架构图,总体都在一个进程中,ResourceManager模块里面有一个用线程模拟的Scheduler。App和NM(NodeManager)都是由线程模拟。做业资源申请和NM节点心跳采用方法调用。
开源架构存在的问题有:
针对存在的问题,咱们进行了架构改造。右侧是改造后的架构图,从SLS中剥离Scheduler Wapper的模拟逻辑,用真实的ResourceManager代替。SLS仅仅负责模拟做业的资源申请和节点的心跳汇报。ResourceManager是真实的,线上生产环境和线下压测环境暴露的指标是彻底同样的,所以线上线下能够很直观地进行指标对比。详细代码参考:YARN-7672
利用调度压力模拟器进行压测,观察到validSchedule不达标,但依然不清楚性能瓶颈到底在哪里。所以须要细粒度指标来肯定性能的瓶颈点。因为调度过程是单线程的,所以细粒度指标获取的手段是侵入FairScheduler,在调度流程中采集关键函数每分钟的时间消耗。目标是找到花费时间占比最多的函数,从而定位系统瓶颈。例如:在preCheck函数的先后加入时间统计,就能够收集到调度过程当中preCheck消耗的时间。
基于以上的思路,咱们定义了10多个细粒度指标,比较关键的指标有:
第一次作压测,给定的压力就是当时线上生产环境峰值的压力状况(1000节点、1000做业并发、500队列、单Container执行时间40秒)。通过优化后,调度器性能提高,知足业务需求,以后经过预估业务规模增加来调整测试压力,反复迭代地进行优化工做。
下图是性能优化时间线,纵轴为调度性能CPS。
在核心调度流程中,第2步是排序子队列。观察细粒度指标,能够很清楚地看到每分钟调度流程总共用时50秒,其中排序时间占用了30秒,占了最大比例,所以首先考虑优化排序时间。
排序自己用的快速排序算法,已经没有优化空间。进一步分析排序比较函数,发现排序比较函数的时间复杂度很是高。
计算复杂度最高的部分是:须要获取队列/做业的资源使用状况(resourceUsage)。原算法中,每2个队列进行比较,须要获取resourceUsage的时候,程序都是现场计算。计算方式是递归累加该队列下全部做业的resourceUsage。这形成了巨大的重复计算量。
优化策略:将现场计算优化为提早计算。
提早计算算法:当为某个App分配了一个Container(资源量定义为containerResource),那么递归调整父队列的resourceUsage,让父队列的resourceUsage += containerResource。当释放某个App的一个Container,一样的道理,让父队列resourceUsage -= containerResource。 利用提早计算算法,队列resourceUsage的统计时间复杂度下降到O(1)。
优化效果:排序相关的细粒度指标耗时明显降低。
红框中的指标表示每分钟调度器用来作队列/做业排序的时间。从图中能够看出,通过优化,排序时间从每分钟30G(30秒)降低到5G(5秒)之内。 详细代码参考:YARN-5969
从上图看,优化排序比较函数后,蓝色的线有明显的增长,从2秒增长到了20秒。这条蓝线指标含义是每分钟调度器跳过没有资源需求的做业花费的时间。从时间占比角度来看,目前优化目标是减小这条蓝线的时间。
分析代码发现,全部队列/做业都会参与调度。但其实不少队列/做业根本没有资源需求,并不须要参与调度。所以优化策略是:在排序以前,从队列的Children中剔除掉没有资源需求的队列/做业。
优化效果:这个指标从20秒降低到几乎能够忽略不计。详细代码参考:YARN-3547
这时,从上图中能够明显看到有一条线呈现上升趋势,而且这个指标占了整个调度时间的最大比例。这条线对应的指标含义是肯定要调度的做业后,调度器为这个做业分配出一个Container花费的时间。这部分逻辑平均执行一次的时间在0.02ms之内,而且不会随着集群规模、做业规模的增长而增长,所以暂时不作进一步优化。
从核心调度流程能够看出,分配每个Container,都须要进行队列的排序。排序的时间会随着业务规模增长(做业数、队列数的增长)而线性增长。
架构思考:对于公平调度器来讲,排序是为了实现公平的调度策略,但资源需求是时时刻刻变化的,每次变化,都会引发做业资源使用的不公平。即便分配每个Container时都进行排序,也没法在整个时间轴上达成公平策略。
例如,集群有10个cpu,T1时刻,集群只有一个做业App1在运行,申请了10个cpu,那么集群会把这10个cpu都分配给App1。T2时刻(T2 > T1),集群中新来一个做业App2,这时集群已经没有资源了,所以没法为App2分配资源。这时集群中App1和App2对资源的使用是不公平的。从这个例子看,仅仅经过调度的分配算法是没法在时间轴上实现公平调度。
目前公平调度器的公平策略是保证集群在某一时刻资源调度的公平。在整个时间轴上是须要抢占策略来补充达到公平的目标。 所以从时间轴的角度考虑,没有必要在分配每个Container时都进行排序。
综上分析,优化策略是排序过程与调度过程并行化。要点以下:
优化效果以下:
队列排序效率:利用线程池对2000个队列进行一次排序只须要5毫秒之内(2ms-5ms),在一秒内至少能够完成200次排序,对业务彻底没有影响。
在并行运行1万做业,集群1.2万的节点,队列个数2000,单Container执行时间40秒的压力下,调度CPS达到5万,在一分钟内能够将整个集群资源打满,并持续打满。
上图中,15:26分,pending值是0,表示这时集群目前全部的资源需求已经被调度完成。15:27分,resourceUsage达到1.0,表示集群资源使用率为100%,集群没有空闲资源。pending值达到4M(400万 mb的内存需求)是由于没有空闲资源致使的资源等待。
线下压测的结果很是好,最终要上到线上才能达成业务目标。然而稳定上线是有难度的,缘由:
除了常规的单元测试、功能测试、压力测试、设置报警指标以外,咱们根据业务场景提出了针对集群调度系统的上线策略。
离线生产的业务高峰在凌晨,所以凌晨服务出现故障的几率是最大的。而凌晨RD同窗接到报警电话,执行一般的服务回滚流程(回滚代码,重启服务)的效率是很低的。而且重启期间,服务不可用,对业务产生了更长的不可用时间。所以咱们针对调度器的每一个优化策略都有参数配置。只须要修改参数配置,执行配置更新命令,那么在不重启服务的状况下,就能够改变调度器的执行逻辑,将执行逻辑切换回优化前的流程。
这里的关键问题是:系统经过配置加载线程更新了调度器某个参数的值,而调度线程也同时在按照这个参数值进行工做。在一次调度过程当中可能屡次查看这个参数的值,而且根据参数值来执行相应的逻辑。调度线程在一次调度过程当中观察到的参数值发生变化,就会致使系统异常。
处理办法是经过复制资源的方式,避免多线程共享资源引发数据不一致的问题。调度线程在每次调度开始阶段,先将当前全部性能优化参数进行复制,确保在本次调度过程当中观察到的参数不会变动。
优化算法是为了提高性能,但要注意不能影响算法的输出结果,确保算法正确性。对于复杂的算法优化,确保算法正确性是一个颇有难度的工做。
在“优化排序比较时间”的研发中,变动了队列resourceUsage的计算方法,从现场计算变动为提早计算。那么如何保证优化后算法计算出来的resourceUsage是正确的呢?
即便作了单元策略,功能测试,压力测试,但面对一个复杂系统,依然不能有100%的把握。 另外,将来系统升级也可能引发这部分功能的bug。
算法变动后,若是新的resourceUsage计算错误,那么就会致使调度策略一直错误执行下去。从而影响队列的资源分配。会对业务产生巨大的影响。例如,业务拿不到本来的资源量,致使业务延迟。
经过原先现场计算的方式获得的全部队列的resourceUsage必定是正确的,定义为oldResourceUsage。 算法优化后,经过提早计算的方式获得全部队列的resourceUsage,定义为newResourceUsage。
在系统中,按期对oldResourceUsage和newResourceUsage进行比较,若是发现数据不一致,说明优化的算法有bug,newResourceUsage计算错误。这时系统会向RD发送报警通知,同时自动地将全部计算错误的数据用正确的数据替换,使得错误获得及时自动修正。
本文主要介绍了美团点评Hadoop YARN集群公平调度器的性能优化实践。
单个YARN集群调度器的性能优化老是有限的,目前咱们能够支持1万节点的集群规模,那么将来10万,100万的节点咱们如何应对?
咱们的解决思路是:基于社区的思路,设计适合美团点评的业务场景的技术方案。社区Hadoop 3.0研发了Global Scheduling,彻底颠覆了目前YARN调度器的架构,能够极大提升单集群调度性能。咱们正在跟进这个Feature。社区的YARN Federation已经逐步完善。该架构能够支撑多个YARN集群对外提供统一的集群计算服务,因为每一个YARN集群都有本身的调度器,这至关于横向扩展了调度器的个数,从而提升集群总体的调度能力。咱们基于社区的架构,结合美团点评的业务场景,正在不断地完善美团点评的YARN Federation。
世龙、廷稳,美团用户平台大数据与算法部研发工程师。
数据平台资源调度团队,目标是建设超大规模、高性能、支持异构计算资源和多场景的资源调度系统。目前管理的计算节点接近 3 万台,在单集群节点过万的规模下实现了单日数十万离线计算做业的高效调度,资源利用率超过 90%。资源调度系统同时实现了对实时计算做业、机器学习模型 Serving 服务等高可用场景的支持,可用性超过 99.9%。系统也提供了对 CPU/GPU 等异构资源的调度支持,实现了数千张 GPU卡的高效调度,以及 CPU 资源的离线与训练混合调度,目前正在引入 NPU/FPGA 等更多异构资源,针对机器学习场景的特色实现更高效合理的调度策略。
咱们有多个岗位正在招聘,若是你对超大规模系统的挑战感到兴奋,若是你对异构计算资源的调度策略感到好奇,欢迎加入咱们,联系邮箱 sunyerui#meituan.com。