标签:
在YARN中,资源调度器(Scheduler)是ResourceManager中的重要组件,主要负责对整个集群(CPU,内存)的资源进行分配和调度,分配以资源Container的形式分发到各个应用程序中(如MapReduce作业),应用程序与资源所在节点的NodeManager协作利用Container完成具体的任务(如Reduce Task)。
Scheduler以可插拔的形式来配置,框架默认提供了三种Scheduler:
1)FIFO Scheduler:
先进先出即先来后到。作业提交作业作为资源分配优先级的重要因素。
2)Capacity Scheduler:
以Capacity为中心,把资源划分到若干个队列中,各个队列内根据自己的逻辑分配资源。例如下图中队列A可以调度的资源可以占80%,队列B占有剩下的20%,各队列接受相应的作业请求,在自己的资源中分配。
3)Fair Scheduler:
秉承公平性原则,尽可能让各个作业得到的资源平均。下图中的作业2提交之后,原本Job1占有的资源拨出一般给作业2,从而达到“公平”。
采用Capacity调度的集群,资源被划分到一系列的Queue中,每个队列管理整个集群资源的一部分。队列内部可以再嵌套,形成层级结构。队列内资源采用FIFO的方式分配。
通常情况下,作业不能使用超过队列容量的资源,但是如果一个队列中有多于一个的作业,并且有空闲的资源,则调度器会为作业分配资源,即使这会导致队列超出容量限制。该特性叫队列弹性(queue elasticity).为了避免队列占用过多其他队列的资源,可以配置一个最大容量,队列只能使用该容量以内的资源,但是会牺牲一定的弹性。
假设我们有下面的队列层次:
在这个层次结构中,root队列下定义了prod和dev两个队列,假设占有集群资源容量的比例分别为40%和60%。dev下又分为各占50%容量的eng和science队里。下面展示的是该结构下的一个配置文件(capacity-scheduler.xml):
<configuration>
<property>
<name>yarn.scheduler.capacity.root.queues</name>
<value>prod,dev</value>
</property>
<property>
<name>yarn.scheduler.capacity.root.dev.queues</name>
<value>eng,science</value>
</property>
<property>
<name>yarn.scheduler.capacity.root.prod.capacity</name>
<value>40</value>
</property>
<property>
<name>yarn.scheduler.capacity.root.dev.capacity</name>
<value>60</value>
</property>
<property>
<name>yarn.scheduler.capacity.root.dev.maximun-capacity</name>
<value>75</value>
</property>
<property> <name>yarn.scheduler.capacity.root.dev.eng.capacity</name>
<value>50</value>
</property>
<property> <name>yarn.scheduler.capacity.root.dev.science.capacity</name>
<value>50</value>
</property>
</configuration>
这些配置采用嵌套的格式,队列层次通过点号来表示。注意到dev队列配置了一个maximun-capacity属性,值为75%。也就是说,当prod处于空闲时,dev所能用的最大资源比例只能达到75%这样保证prod只是有25%的资源立即可用。
除了容量配置外,还可以配置单个用户或者程序能够使用的最大资源数,同时可以运行几个应用,权限ACL控制等。
队列划分好之后,应用程序也需要配置使用哪一个队列中的资源。例如,对于MapReduce作业,mapreduce.job.queuename
属性配置作业所使用的队列。如果这个队列不存在,在作业提交的时候就会报错。如果没有指定队列,作业被放到一个叫default的队列中。注意这里配置的队列名称只需要最后一段,即只需要使用eng
,而不能使用root.dev.eng
公平调度器试图保证运行中的应用程序都能分配到平均的资源,这里的平均是通过资源的数量来定义的,例如内存大小。事实上,公平调度器也使用Queue机制来工作。使用下图来解释分配过程:
假设有两个用户A和B,分别持有队列queue A和B。在一开始的时候,A启动了job1,此时因为B没有资源需要,因此job1霸占了所有资源。一段时间后B启动了job2,一段时间后(等待job1释放资源),job1和job2各占一半资源。如果用户B接着启动job3,则job2和job3将共享队列B,也就是各占集群总资源的25%。当job2结束之后,job3占据整个队列B(50%)。
因此我们得出一个结论,公平性是指用户之间的公平性,而不是应用之间的公平性。上述例子中用户A和B各占50%资源,当B启动两个作业时,并不是3个作业各占三分之一,而是A和B各占50%,B内部再分给两个作业。
使用哪一种调度器,通过yarn-site.xml
中的yarn.resourcemanager.scheduler.class
.默认使用的是Capacity Scheduler,虽然Cloudera的发行版CDH默认使用的是Fair Scheduler。要使用Fair Scheduler,我们将这个属性设置为:
org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler
Fair Scheduler的配置信息放在fair-scheduler.xml文件中,RM从classpath中加载该文件,文件名可以通过设置yarn.scheduler.fait.allocation.file
来改变。
下面是一个配置文件:
<?xml version="1.0"?>
<allocations>
<defaultQueueSchedulingPolicy>fair</defaultQueueSchedulingPolicy>
<queue name="prod">
<weight>40</weight>
<schedulingPolicy>fifo</schedulingPolicy>
</queue>
<queue name="dev">
<weight>40</weight>
<queue name="eng"/>
<queue name="science"/>
</queue>
<queuePlacementPolicy>
<rule name="specified" create="false"/>
<rule name="primaryGroup" create="false"/>
<rule name="default" queue="dev.eng"/>
</queuePlacementPolicy>
</allocations>
基于上述的配置,当prod和dev分别占有40%和60%时被认为是公平的,eng和science各占dev的50%时认为时公平的。注意这里重要的是比例,而不是百分比,例如40和60可以替换成2和3,只需比例不变。
prod使用的调度策略为FIFO,dev没有配置,所有使用文档级别的默认策略,即defaultQueueSchedulingPolicy元素配置的fair策略。虽然各个队列可以配置自己的调度策略,但是prod和dev之间,eng和science之间仍然使用的是Fair公平策略。
应用放在哪个队列中,通过queuePlacementPolicy配置,从一系列rule从上往下依次匹配,直到成功。在上述的例子中,首先尝试把应用放在其本身配置的queue中,如果没有配置或者配置的队列不存在,则使用第二条规则primaryGroup,尝试将应用运行在名称为用户所在Primary Unix组名的队列。如果还是没有,使用默认的dev.eng。
应用的放置规则可以完全不配置,默认采用如下规则:
<queuePlacemntPolicy>
<rule name="specified" />
<rule name="user"/>
</queuePlacementPolicy>
也可以使用下面的配置使得所有应用都运行在默认队列中:
<queuePlacemntPolicy>
<rule name="default"/>
</queuePlacementPolicy>
当一个集群处于繁忙状态时,如果我们提交一个作业到其中一个空的队列,此时需要等待其他作业释放资源才能开始运行。为了使得等待释放的时间可以控制在预测范围内,Fair Scheduler支持优先权(preemption)。
优先权运行Scheduler杀死占用(按公平原则划分)别的队列的Container,腾出空间被占用队列的应用。优先权可能会降低集群的效率,因为被杀死的Container都需要重新执行。杀死之前的等待时间通过allocaion文件中的queue元素内的fairSharePreemptionTimeut
元素配置,没有配置时使用全局的defaultFairSharePreemptionTimeut
元素,而fairSharePreemptionThreshold和defaultFairSharePreemptionThreshold则用于设置等待时间内,原本属于队列的资源可用比例,例如设为0.5,表示当等待时间到时,如果原本属性该队列(按照公平原则)的资源可用比例还没有达到一半,则会启动杀死其他应用的Container。
当应用程序请求在某个特定的节点上运行时,如果该节点刚好处于忙碌状态无法分配给应用,一种选择方式是就近选择同一机架中的其他节点。但是在实际中发现,稍微等一小段时间,很有可能就能够分配到指定的节点,进而提高集群效率。这个特性叫延迟调度(delay scheduling)。Capacity Scheduler和FairScheduler支持这个特性。
默认情况下,集群中的NodeManager每一秒中都想RM发送心跳,心跳张包含节点当前正在运行的Container以及可用于新Container的资源。因此每次心跳都是一个调度机会(scheduling opportunity)。当启用延迟调度时,对于有节点位置要求的资源请求,调度器不会简单地使用第一次调度机会(牺牲数据locality),而是会尝试等待直到最大次数的调度机会,一旦发现符合要求的调度机会,及分配给应用。如果直到最大次数还是没有合适的节点资源,则放宽数据locality要求,退而求其次采用同一机架。
最大opportunity数的配置,在Capacity Scheduler中使用yarn.scheduler.capacity.node-locality-delay
配置,取值为正整数。Fair Scheduler略有不同,配置的是yarn.scheduler.fair.lcality.threshold.node
属性,例如设为0.5,代表要等待集群中一半的节点发送心跳提供scheduling opportunity,才决定是否放宽要求采用同一机架上的其他节点。对应的yarn.scheduler.fair.lcality.threshold.rack
配置放弃同一机架要求采用不同机架上的机器之前,需要等待多少节点发送心跳。
当集群中只有一种资源需要被调度时,公平性比较容易定义。例如只有内存资源,那么很容易通过内存大小来确定公平性。但是当有多重资源需要调度时,事情开始变得复杂。例如一个应用请求分配很大的内存和很少的CPU,另一个应用则相反,需要大量的CPU和少量的内存,此时要如何比较?
YARN采用的做法是通过占统治地位的资源使用来衡量,这个特性叫Dominant Resource Fair,简称DRF。我们使用一个简单例子来说明。假设一个集群有100颗CPU和10TB的内存。应用A请求(2 CPU,200GB)的容器,占比为(2%和3%),此时内存使用要求占统治地位(3%>2%)。B应用请求(6%,1%)的容器,则CPU占主导地位。因此A应用和B应用的比值为3%:6%,基于公平原则,B应用分配到的Container个数将会是A应用的一半。
默认情况下,DRF是不打开的,因此计算公平性时只考虑了内存,CPU被忽略。要启用DRF,Capacity Scheduler需要在capacity-scheduler.xml
中将属性yarn.scheduler.capacity.resource-calculator
设置为org.apache.hadoop.yarn.util.resource.DominantResourceCalculator
。Fair Scheduler则在相应的allocation xml文件中将defaultQueueSchedulingPolicy设为drf。
标签:
原文地址:http://blog.csdn.net/bingduanlbd/article/details/52000151