YARN通过两类长期运行的守护进程提供核心服务:管理集群上资源使用的资源管理器resource manager(RM),能够启动和监控容器(container)的节点管理器node manager(NM),container用于执行特定的应用程序进程。与HDFS一样,YARN也是master/slave结构,一个RM管理着多个NM。
YARN应用运行机制
1)client首先联系RM,要求它运行一个application master(AM);
2)RM找到一个能够在container中启动AM的NM;
3)AM运行起来之后做什么依赖于应用本身,有可能在所处的container中简单地执行一个计算并将结果返回给客户端,或是像RM请求更多的container,用于运行一个分布式计算,例如MapReduce。
YARN资源请求
YARN有一个灵活的资源请求模型,请求多个container时可以指定每个container的资源,还可以治container的本地限制要求。
本地化对于确保分布式数据处理算法高效使用集群带宽非常重要,本地限制有时也存在无法被满足的情况,例如一个NM节点已经运行了别的container而无法再启动新的container,此时若有应用请求该节点,则YARN会尝试在同一机架的其他节点上启动一个container,如果还不行则会尝试集群中的任意节点。
YARN应用可以在运行中的任意时刻提出资源申请,可以在最开始提出所有的请求,或者为满足不断变化的应用需要,采取更为动态的方式在需要更多资源时提出请求。Spark采用的就是第一种方式,MapReduce采用的就是第二种方式,在最开始时申请map任务的container,后期再申请reduce任务的container,如果任何任务出现失败,会另外申请容器重新运行失败的任务。
YARN和MapReduce1的对比
MapReduce1中由两类守护进程控制作业执行:一个jobtracker以及一个或多个tasktracker。jobtracker同时负责作业调度和任务进度监控,而在YARN中作业调度由RM负责,任务监控由每个MapReduce作业的AM负责。jobtracker同时负责存储已完成作业的作业历史,在YARN中与之等价的角色是时间轴服务器(timeline server)。yarn中与tasktracker等价的角色是节点管理器,与Slot等价的概念是container。
YARN中的调度
FIFO调度器(FIFO Scheduler):将应用放置在一个队列中,然后按照提交的顺序运行应用。优点是简单易懂,不需要任何配置,缺点是大应用会占用集群中的所有资源,小应用无法及时运行,会一直被阻塞至大应用完成,不适合共享集群。
容量调度器(Capacity Scheduler):可以为小作业分配一个单独的队列,在一个队列内使用FIFO调度。优点是小作业不会被阻塞,缺点是整个集群的利用率会降低,因为队列的容量是为队列中的作业保留的,与FIFO调度器相比大作业的执行时间会更长。 容量调度器是YARN中默认的资源调度器,但是在默认情况下只有root.default 一个queue。其配置文件为hadoop安装目录下etc/hadoop/capacity-scheduler.xml,如下:
yarn.scheduler.capacity.root.queues a,b yarn.scheduler.capacity.root.b.queues b1,b2 yarn.scheduler.capacity.root.a.capacity 60 yarn.scheduler.capacity.root.b.capacity 40 yarn.scheduler.capacity.root.b.maximum-capacity 55 yarn.scheduler.capacity.root.b.b1.capacity 50 yarn.scheduler.capacity.root.b.b2.capacity 50
以上配置文件在root队列下定义了两个队列a,b分别占用60%和40%的容量,b队列进一步被划分为b1,b2两个容量相等的队列。b队列最大容量被设置为55%,因此即使a队列空闲,b队列也不会占用全部集群资源,即a队列能即刻使用的可用资源比例总是能达到45%。由于没由对其他队列设置最大容量限制,b1或b2队列可能会占用b队列的所有容量(将近55%的集群资源),而a队列可能会占用全部集群资源。在MapReduce中可以通过属性mapreduce.job.queuename=b1来指定要使用的队列,如果不指定应用会被放在名为default的默认队列中。
公平调度器(Fair Scheduler):为所有运行的应用公平分配资源。假设用户A和B分别拥有自己的队列,A启动一个作业,在B没有需求时A会分到全部的集群资源,当A提交的作业仍在运行时B提交一个作业,一段时间后,每个作业都将用到一半的集群资源。此时若B启动第二个作业二其他作业仍在运行,B的第二个作业将和B的第一个作业共享资源,一段时间后B的两个作业将各占用1/4的集群资源,二A的作业仍占用一般的集群资源,资源在用户之间实现了公平共享。如果使用公平调度器,要在yarn-site.xml中将yarn.resourcemanager.scheduler.class设置为公平调度器的完全限定名:org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler。公平调度器通过分配文件fair-scheduler.xml配置(没有该分配文件时,每个应用放置在一个以用户名命名的队列中,队列是在用户提交第一个应用时动态创建的):
队列层次使用嵌套queue元素定义,所有队列都是root队列的孩子,即使没有在配置文件中将队列嵌套进root的queue元素里,这里dev队列又分成了eng和science队列。队列中有权重元素,用于公平共享计算,这个例子中当集群资源按照40:60的比例分配给prod和dev时被认为是公平的。eng和science没有指定权重,因此它们会被平均分配,权重不一定要使用和为100的两个数,只是表示一个比例关系,这里使用2和3的效果是一样的。每个队列可以有不同的调度策略,队列的默认调度策略通过顶层元素defaultQueueSchedulingPolicy设置,若省略默认使用公平调度。公平调度器也支持队列级别的FIFO(fifo)策略以及主导资源公平性(drf)策略。这里的例子中,prod队列使用fifo调度策略,在prod和dev之间、eng和science之间及内部划分资源仍然使用公平调度。每个队列还可以配置最大和最小资源数量及最大可运行的应用数量(上例中没有展示)。最小资源数量不是硬性限制,但调度器常用它对资源调度进行优先排序,如果两个队列的资源均低于他们的公平共享额度,那么远低于最小资源数量的那个队列被优先分配资源,最小资源数量还会用于抢占行为。
公平调度器使用一个基于规则的系统来确定应用该放到哪个队列。在上例中,queuePlacementPolicy元素包含了一个规则列表,每条规则会被依次匹配直至匹配成功。第一条specified表示把应用放到制定的队列中,如果没有指明或指明的队列不存在,则该规则不匹配,继续匹配下一条。第二条primaryGroup表示把应用放在以用户的主Unix组命名的队列中,如果没有这样的队列则继续尝试下一条规则而不是创建队列。default规则是一条兜底规则,其他规则都不匹配时启用该规则,把应用放到dev.eng队列中。省略queuePlacementPolicy元素时,队列放置默认遵循以下规则,即除非指定队列(不存在就创建),否则会以用户名为队列名创建队列:
另一个放置策略是将所有应用放置到同一个default队列中,这样在应用之间公平共享资源而不是用户之间公平共享,可通过将属性yarn.scheduler.fair.user-as-default-queue设为false实现,策略定义等价于以下规则:
公平调度器支持抢占功能。在一个集群中,作业被分配给一个空队列时(没有资源),作业不会立刻启动,直到已经运行的作业时放了资源。所为抢占就是允许调度器(scheduler)终止那些占用资源超过了其公平共享份额的队列的container,这些container资源释放后可以分配给资源数量低于应得份额的队列。抢占会降低整个集群的效率,因为被终止的container需要重新启动。
容量调度和公平调度都支持延迟调度,即如果一个应用请求某个节点,而此时有其他容器在该节点上运行,此时不是放宽本地需求取请求同一机架上的其他节点,而是等待很短的一小段时间,就会增加在所请求节点上分配到一个container的机会,从而提高集群效率。
容量调度和公平调度对单一类型资源如内存的调度比较容易确定,当有多种类型的资源需要调度时就会变得复杂,因为有的应用可能对CPU需求大对内存需求小,而有的应用对CPU需求小对内存需求大。主导资源公平性(Dominant Resource Fairness,DRF)将应用主导资源作为其对集群资源使用的度量。例如一个有100个CPU和10T内存的集群,应用A请求的每个container的资源为2个CPU和300G内存,应用B请求的每个container的资源为6个CPU和100G内存。A请求的资源中CPU占集群的2%,内存占3%,主导资源为内存。同理B的主导资源为CPU,占集群资源的6%。则B申请的资源就是A的两倍(6% vs 3%),那么公平调度下B分配到的container数量将是A的一半。默认情况下不使用DRF。