yarn的架构及原理

 1. YARN产生背景

  MapReduce本身存在着一些问题:

  1)JobTracker单点故障问题;如果Hadoop集群的JobTracker挂掉,则整个分布式集群都不能使用了。

  2)JobTracker承受的访问压力大,影响系统的扩展性。

  3)不支持MapReduce之外的计算框架,比如Storm、Spark、Flink等。

  与旧MapReduce相比,YARN采用了一种分层的集群框架,具有以下几种优势。

  1)Hadoop2.0提出了HDFSFederation;它让多个NameNode分管不同的目录进而实现访问隔离和横向扩展。对于运行中NameNode的单点故障,通过 NameNode热备方案(NameNode HA)实现 。

  2) YARN通过将资源管理和应用程序管理两部分剥离开来,分别由ResourceManager和ApplicationMaster进程来实现。其中,ResouceManager专管资源管理和调度,而ApplicationMaster则负责与具体应用程序相关的任务切分、任务调度和容错等。

  3)YARN具有向后兼容性,用户在MR1上运行的作业,无需任何修改即可运行在YARN之上。

  4)对于资源的表示以内存为单位(在目前版本的 Yarn 中没有考虑 CPU的占用),比之前以剩余 slot 数目为单位更合理。

  5)支持多个框架,YARN不再是一个单纯的计算框架,而是一个框架管理器,用户可以将各种各样的计算框架移植到YARN之上,由YARN进行统一管理和资源分配,由于将现有框架移植到YARN之上需要一定的工作量,当前YARN仅可运行MapReduce这种离线计算框架。

  6)框架升级容易,在YARN中,各种计算框架不再是作为一个服务部署到集群的各个节点上(比如MapReduce框架,不再需要部署JobTracker、 TaskTracker等服务),而是被封装成一个用户程序库(lib)存放在客户端,当需要对计算框架进行升级时,只需升级用户程序库即可。

2. 什么是YARN

 YARN是Hadoop2.0版本新引入的资源管理系统,直接从MR1演化而来。

 核心思想:将MP1中JobTracker的资源管理和作业调度两个功能分开,分别由ResourceManager和ApplicationMaster进程来实现。

  1)ResourceManager:负责整个集群的资源管理和调度。

  2)ApplicationMaster:负责应用程序相关的事务,比如任务调度、任务监控和容错等。

 YARN的出现,使得多个计算框架可以运行在一个集群当中。

  1)每个应用程序对应一个ApplicationMaster。

  2)目前可以支持多种计算框架运行在YARN上面比如MapReduce、Storm、Spark、Flink等。

 3. YARN的基本架构

从YARN的架构图来看,它主要由ResourceManager和NodeManager、ApplicationMaster和Container等组件组成。

ResourceManager(RM)

   YARN分层结构的本质是ResourceManager。这个实体控制整个集群并管理应用程序向基础计算资源的分配。ResourceManager 将各个资源部分(计算、内存、带宽等)精心安排给基础NodeManager(YARN 的每节点代理)。ResourceManager还与 ApplicationMaster 一起分配资源,与NodeManager 一起启动和监视它们的基础应用程序。在此上下文中,ApplicationMaster 承担了以前的 TaskTracker 的一些角色,ResourceManager 承担了 JobTracker 的角色。

  1)处理客户端请求;

  2)启动或监控ApplicationMaster;

  3)监控NodeManager;

  4)资源的分配与调度。

NodeManager(NM

 NodeManager管理一个YARN集群中的每个节点。NodeManager提供针对集群中每个节点的服务,从监督对一个容器的终生管理到监视资源和跟踪节点健康。MRv1通过插槽管理Map和Reduce任务的执行,而NodeManager 管理抽象容器(container),这些容器代表着可供一个特定应用程序使用的针对每个节点的资源。YARN继续使用HDFS层。它的主要 NameNode用于元数据服务,而DataNode用于分散在一个集群中的复制存储服务。

NodeManager是驻留在一个YARN集群中的每个节点上的代理,主要负责:

  • 容器生命周期管理。
  • 监控每个容器的资源(CPU、内存等)使用情况。
  • 跟踪节点健康状况。
  • 以“心跳”的方式与ResourceManager保持通信。
  • 向ResourceManager汇报作业的资源使用情况和每个容器的运行状态。
  • 接收来自ApplicationMaster的启动/停止容器的各种请求 。

需要说明的是,NodeManager主要负责管理抽象的容器(container),只处理与容器相关的事情,而不具体负责每个任务(Map任务或Reduce任务)自身状态的管理,因为这些管理工作是由ApplicationMaster完成的,ApplicationMaster会通过不断与NodeManager通信来掌握各个任务的执行状态。

ApplicationMaster(AM)

 ApplicationMaster管理一个在YARN内运行的应用程序的每个实例。ApplicationMaster 负责协调来自 ResourceManager 的资源,并通过 NodeManager 监视容器的执行和资源使用(CPU、内存等的资源分配)。请注意,尽管目前的资源更加传统(CPU 核心、内存),但未来会带来基于手头任务的新资源类型(比如图形处理单元或专用处理设备)。从 YARN 角度讲,ApplicationMaster 是用户代码,因此存在潜在的安全问题。YARN 假设 ApplicationMaster 存在错误或者甚至是恶意的,因此将它们当作无特权的代码对待。

  1)负责数据的切分;

  2)为应用程序申请资源并分配给内部的任务;

  3)任务的监控与容错。

Container

       对任务运行环境进行抽象,封装CPU、内存等多维度的资源以及环境变量、启动命令等任务运行相关的信息。比如内存、CPU、磁盘、网络等,当AM向RM申请资源时,RM为AM返回的资源便是用Container表示的。YARN会为每个任务分配一个Container且该任务只能使用该Container中描述的资源。

  要使用一个YARN集群,首先需要来自包含一个应用程序的客户的请求。ResourceManager 协商一个容器的必要资源,启动一个ApplicationMaster 来表示已提交的应用程序。通过使用一个资源请求协议,ApplicationMaster协商每个节点上供应用程序使用的资源容器。执行应用程序时,ApplicationMaster 监视容器直到完成。当应用程序完成时,ApplicationMaster 从 ResourceManager 注销其容器,执行周期就完成了。

 

4. YARN的原理

YARN 的作业运行,主要由以下几个步骤组成:

 

1)作业提交

     client调用job.waitForCompletion方法,向整个集群提交MapReduce作业 (第1步) 。 新的作业ID(应用ID)由资源管理器分配(第2步). 作业的client核实作业的输出, 计算输入的split,将作业的资源(包括Jar包, 配置文件, split信息)拷贝给HDFS(第3步). 最后, 通过调用资源管理器的submitApplication()来提交作业(第4步).

2)作业初始化

      当资源管理器收到submitApplication()的请求时, 就将该请求发给调度器(scheduler), 调度器分配container, 然后资源管理器在该container内启动应用管理器进程, 由节点管理器监控(第5a和5b步)。
  MapReduce作业的应用管理器是一个主类为MRAppMaster的Java应用。其通过创造一些bookkeeping对象来监控作业的进度, 得到任务的进度和完成报告(第6步)。然后其通过分布式文件系统得到由客户端计算好的输入split(第7步)。然后为每个输入split创建一个map任务, 根据mapreduce.job.reduces创建reduce任务对象。

3)任务分配  

      如果作业很小,应用管理器会选择在其自己的JVM中运行任务。如果不是小作业, 那么应用管理器向资源管理器请求container来运行所有的map和reduce任务(第8步). 这些请求是通过心跳来传输的, 包括每个map任务的数据位置, 比如存放输入split的主机名和机架(rack). 调度器利用这些信息来调度任务, 尽量将任务分配给存储数据的节点, 或者退而分配给和存放输入split的节点相同机架的节点.

4)任务运行

       当一个任务由资源管理器的调度分配给一个container后, 应用管理器通过联系节点管理器来启动container(第9a步和9b步). 任务由一个主类为YarnChild的Java应用执行. 在运行任务之前首先本地化任务需要的资源, 比如作业配置, JAR文件, 以及分布式缓存的所有文件(第10步). 最后, 运行map或reduce任务(第11步).
  YarnChild运行在一个专用的JVM中, 但是YARN不支持JVM重用.

5)进度和状态更新 

  YARN中的任务将其进度和状态(包括counter)返回给应用管理器,客户端每秒(通过mapreduce.client.progressmonitor.pollinterval设置)向应用管理器请求进度更新,展示给用户。

6)作业完成

        除了向应用管理器请求作业进度外,客户端每5分钟都会通过调用waitForCompletion()来检查作业是否完成。时间间隔可以通过mapreduce.client.completion. pollinterval来设置。作业完成之后, 应用管理器和container会清理工作状态, OutputCommiter的作业清理方法也会被调用。作业的信息会被作业历史服务器存储以备之后用户核查。

 

5. MapReduce on YARN

 1、MapReduce on YARN

  1)YARN负责资源管理和调度;

  2)ApplicationMaster负责任务管理。

 2、MapReduce ApplicationMaster

  1)MRAppMaster;

  2)每个MapReduce启动一个MRAppMaster;

  3)MRAppMaster负责任务切分、任务调度、任务监控和容错。

 3、MRAppMaster任务调度

  1)YARN将资源分配给MRAppMaster;

  2)MRAppMaster进一步将资源分配给内部任务。

 4、MRAppMaster容错

  1)MRAppMaster运行失败后,由YARN重新启动;

  2)任务运行失败后,由YARN重新申请资源。

 

 6. YARN HA(高可用)

ResourceManager由一对分别处于Active和Standby状态的ResourceManager组成,它使用基于Zookeeper的选举算法来决定ResourceManager的状态。其中,ZKFC仅为ResourceManager的一个进程服务,不是单独存在的(区别于HDFS,它是独立存在的进程),负责监控ResourceManager的健康状况并定期向Zookeeper发送心跳。ResourceManager通过RMStateStore(目前有基于内存的、基于文件系统的和基于Zookeeper的等,此处使用后者)来存储内部数据、主要应用数据和标记等。

 

 7.Yarn中的三种资源调度策略

什么是资源?

对一个资源管理系统而言,首先要定义出资源的种类,然后将每种资源量化,才能管理。这就是资源抽象的过程。

先抛开YARN不谈,在一个分布式、多用户的系统中,什么是资源?
我们通常所说的“资源”都是硬件资源,包括CPU使用/内存使用/磁盘用量/IO/网络流量等等。这是比较粗粒度的。也可以是抽象层次更高的TPS/请求数之类的。其实广义来看,时间、人力等“软件”也是资源,可惜一般很难量化。

为什么要有“资源”这个概念?首先可以用来衡量系统的瓶颈,系统能否充分利用资源?什么时候应该扩容?在多用户的系统中,“资源”还额外承担着限制用户的功能。比如当总体资源紧张时,重要的用户可以优先获得资源,有更高的资源上限,普通的用户更难获得资源。

YARN的资源抽象比较简单,只有两种资源:内存和CPU。而资源数量是管理员手动设置的,每个NM节点可以贡献一定数量的内存(MB)和CPU,由RM统一管理,不一定是真实的内存和CPU数。
其中内存资源是比较关键的,直接决定任务能否成功。如果某个任务需要的内存过多,可能无法执行,或者OOM。CPU资源的限制比较弱,只限定了一台NM上能并发执行多少任务。如果并发的过多,执行的可能比较慢。

Container

Container是RM分配资源的基本单位。每个Container包含特定数量的CPU资源和内存资源,用户的程序运行在Container中,有点类似虚拟机。RM负责接收用户的资源请求并分配Container,NM负责启动Container并监控资源使用。如果使用的资源(目前只有内存)超出Container的限制,相应进程会被NM杀掉。
可见Container这个概念不只用于资源分配,也用于资源隔离。理论上来说不同Container之间不能互相影响。可惜现阶段YARN的隔离做的还不太好。

Container的另一个特性是客户端可以要求只在特定节点上分配,这样用户的程序可以只在特定的节点上执行。这跟计算本地性有关。后面会讲到。

Container是有最大资源限制的。在我们的设置中,每个Container最多只能有8G内存,8个CPU。这是由RM端的参数yarn.scheduler.maximum-allocation-mb和yarn.scheduler.maximum-allocation-vcores决定的。

调度器与队列

在YARN中,调度器是一个可插拔的组件,常见的有FIFO,CapacityScheduler,FairScheduler。可以通过配置文件选择不同的调度器。
在RM端,根据不同的调度器,所有的资源被分成一个或多个队列(queue),每个队列包含一定量的资源。用户的每个application,会被唯一的分配到一个队列中去执行。队列决定了用户能使用的资源上限。
所谓资源调度,就是决定将资源分配给哪个队列、哪个application的过程

可见调度器的两个主要功能:1.决定如何划分队列;2.决定如何分配资源。此外,还有些其他的特性:ACL、抢占、延迟调度等等。
后文会对这几种调度器分别介绍下。

pull-based

通俗的说,AM通过心跳向RM申请资源,但当次心跳申请的资源不能马上拿到,而是要再经过若干次心跳才能拿到。这是一种pull-based模型。

AM通过RPC协议ApplicationMasterProtocol与RM通信。这个协议在服务端的实现会调用YarnScheduler的allocate方法(所有调度器都必须实现YarnScheduler接口)。allocate方法有两个作用:1.申请、释放资源;2.表示AM是否存活。超过一段时间AM没有调用这个方法,RM会认为AM挂掉并尝试重新提交。

allocate方法有3个参数:。调度器会暂存这个application的资源请求,同时取出上次心跳后新分配给这个application的container,包装为一个Allocation对象返回。如果上次要求的资源也还没分配,那返回的Allocation对象就不包含任何资源。

那真正分配container是什么时候?答案是NM的心跳时。当NM向RM发送心跳时,会触发一个NODE_UPDATE事件。schduler会handle这个事件尝试在这个node上分配container。里面有一系列判断,比如当前节点是否有足够资源、优先给哪个application分配资源。如果成功分配container,就加入一个List中,等待AM下次心跳来取。

这点跟以前的JobTracker比较像,也是TaskTracker各自去拉取任务。

常见调度器

前文说过,调度器的两个主要作用:1.决定如何划分队列;2.决定如何分配资源,这里又分两种情况:为队列分配资源和为单个application分配资源。从这两方面看下常见的调度器,重点分析下FairScheduler。

前面说了YARN与MapReduce 1的区别,这篇就来说说YARN是怎么为任务调度资源的.

YARN中有三种调度器:FIFO(先进先出)调度器,容量调度器,公平调度器.需要注意的是,这三种调度器没有哪种最好的说法,在不同的环境下,每个都会体现出它的优点.下面就分别来说一说这三种调度器.

(1)FIFO调度器

在Java中,我们知道有一个接口叫做Queue,即队列,Queue就遵循了先进先出的原则:

队列是一个典型的先进先出(FIFO)的容器.即从容器的一端放入事物,从另一端取出,并且事物放入容器的顺序与取出的顺序是相同的.                                                                  ----------------Java编程思想(第4版) 236页,11章11节开始对Queue的介绍.

FIFO调度器简单易懂,实现起来比较简单:先提交的任务就先执行,后提交的任务排队等待执行,且不需要修改任何配置.但是缺点也比较明显:不适合需要执行很多任务的大型集群,原因很简单,大型集群往往有大量的任务,最优的策略显然是充分利用资源并行执行,而不是集全体之力每次只执行一个任务.

 

 

假如有一个任务很大,需要大量的时间,后提交的任务即使很小,也需要一直等待直到大任务完成.这对大型集群来说显然是十分不利的.这种情况下,就要考虑容量调度器与公平调度器.

(2) 容量调度器

FIFO调度器中,小任务需要在大任务后排队等待,在容量调度器中,就不存在这个问题:存在一个专门用来执行小任务的队列.可以想到,大型任务的执行时间要比FIFO调度器长,因为它不能获得集群的全部资源.而且在没有小任务的时候,预留的队列是空闲的,所以集群利用率会有一定的降低.

容量调度器允许多个组织共享一个Hadoop集群,每个组织获得全部资源的一部分.每个组织有一个专门的队列,队列内还可以进一步划分,在同一个队列中,使用FIFO调度策略进行调度.

单个作业时用的资源不会超过它所在的队列容量.但是在如果队列中有多个作业,且资源不够用的时候,如果仍然有可用的空闲资源,那么容量调度器会将剩余的空闲资源分给队列中的作业.这称为弹性队列.

 

正常情况下,容量调度器不会通过强行终止来抢占容器.因此假如一个队列一开始资源够用,随着任务增多,变得不够用时,这个队列就只能等待其他队列释放容器资源.我们可以通过设置队列最大容量限制来缓解这种情况,当然这是牺牲队列弹性的,要在多次尝试中发现一个平衡点.

将应用放置在哪个队列中,取决于应用本身.如,在MapReduce中,可以通过设置属性mapreduce.job.queuename来指定要使用的队列.如果队列不存在,提交时会发送一个错误.不指定队列的话,会放入default队列中.

 (3) 公平调度器

不需要预留资源,因为调度器会在运行的作业之间动态平衡资源:第一个作业(用户A)启动时,由于是唯一运行的任务,所以它独占全部资源.当第二个作业(用户B)启动时,它会分配到一半资源.当然,第二个作业从启动到获得公平共享资源的时间会有滞后,因为必须等待第一个任务的容器用完并释放出资源,当第二个作业完成后,第一个作业会再次独占全部资源.

这时,如果用户B又启动了一个任务,这个任务会与他先前提交的作业共享资源:即每个占用1/4的资源,此时用户A所占用的资源不会受到影响:仍然占有一半资源.

 

 

值得一提的是,公平调度器支持抢占功能.具体来说就是,允许调度器终止那些占用资源超过其公平共享份额的队列的容器,这些容器资源释放后可以分配给资源数量低于应得份额的队列.当然因为抢占会终止容器,所以集群整体效率会降低,毕竟终止的容器还需要重新执行.

延迟调度

所有的YARN容器都试图以本地请求为重.在一个繁忙的集群上,如果应用请求某个节点,那么极有可能此时有其他容器在该节点上运行.常规情况下,应该立刻放宽本地性要求,在同机架中分配一个容器.然而在实践中发现,如果此时可以多等待一段时间,就可以提升在所请求的节点上得到一个容器的机会,从而提高了集群的效率.容量调度器和公平调度器都支持延迟调度.

YARN中的每个节点管理器周期性的(每秒一次)向资源管理器发送心跳(heartbeat)请求.心跳中包括了节点管理器中正在运行的容器,新容器可用资源等信息,这样对于一个计划运行一个容器的应用而言,每个心跳就是一个潜在的调度机会.

在使用延迟调度时,调度器不会兼得的使用它收到的第一个调度机会,而是等待设定的最大数目的调度机会发生,然后才会放松本地性限制并接受下一个调度机会.

容量调度器中,可以通过设置yarn.scheduler.capacity.node-locality-delay来配置延迟调度.设置为正整数,表示调度器在放松节点限制,改为匹配同一机架上的其他借点钱,准备错过的调度机会的数量.

公平调度器也使用调度机会的数量来决定延迟时间,尽管是使用集群规模的比例来表示这个值.例如将yarn.scheduler.fair.locality.threshold.node设置为0.5,表示调度器在接受同一机架中的其他节点之间,将一直等待直到集群中的一半节点都已经给过调度机会.yarn.scheduler.fair.locality.thredhold.rack表示在接受替代所申请的机架之间需要等待的时长阈值. 

Max-min fairness算法

FairScheduler主要关注“公平”,那么在一个共享的集群中,怎样分配资源才算公平?
常用的是max-min fairness算法:wiki。这种策略会最大化系统中一个用户收到的最小分配。如果每一个用户都有足够地请求,会给予每个用户一份均等的资源。尽量不让任何用户被“饿死”。

一个例子:资源总量是10,有3个用户A/B/C,需要的资源分别是5/4/3,应该怎样分配资源?
第一轮:10个资源分成3份,每个用户得到3.33
第二轮:3.33超出了用户C的需求,超出了0.33,将这多余的0.33平均分给A和B,每个用户得0.16
所以最后的分配结果是A=3.49,B=3.49,C=3

上面的例子没有考虑权重,如果3个用户的权重分别是0.5/1/0.8,又应该如何分配资源?
第一轮:将权重标准化,3个用户的权重比是5:10:8。将所有资源分成5+10+8=23份,按比例分配给各个用户。A得到10*5/23=2.17,B得到10*10/23=4.35,C得到10*8/23=3.48
第二轮:B和C的资源超出需求了,B超过0.25,C超过0.48。将多出资源分配给A。
最后的分配结果是A=2.9,B=4,C=3
由于进位的问题会有些误差。

更多用户的情况下同理。

DRF

Max-min fairness解决了单一资源下,多用户的公平分配。这个算法以前主要用来分配网络流量。但在现代的资源管理系统中,往往不只有一种资源。比如YARN,包含CPU和内存两种资源。多种资源的情况下,如何公平分配?Berkeley的大牛们提出了DRF算法

DRF的很多细节不提了。核心概念在于让所有application的“主要资源占比”尽量均等。比如集群总共X内存,Y CPU。app1和app2是CPU密集型的,app1每次请求3个CPU,app2每次请求4个CPU;app3和app4是内存密集型的,app3每次请求10G内存,app4每次请求20G内存。设分给app1、app2、app3、app4的请求数分别是a、b、c、d。DRF算法就是希望找到一组abcd,使得3a/X=4b/X=10c/Y=20d/Y。
如何判断CPU/内存密集型?如果任务需要的CPU资源/集群总的CPU资源 > 需要的内存资源/集群总的内存资源,就是CPU密集型,反之是内存密集型。
实际应用中一般没有最优解,而是一个不断动态调整的过程。和max-min fairness一样,也要经过多轮分配,才能达到一个公平的状态。

如果考虑权重的话,算法会更复杂一点。另外在单一资源的情况下,DRF会退化为max-min fairness。

以上图为例,共有3种对象:ParentQueue(root是一个特殊的ParentQueue)、LeafQueue、Application,只有LeafQueue才能提交app。
每个Queue可以定义自己的SchedulingPolicy,这个policy主要用于Scheduable对象的排序。目前共有3种SchedulingPolicy的实现:FifoPolicy、FairSharePolicy、DominantResourceFairnessPolicy,FIFO只能用于LeafQueue,其他两种Policy可以用于任意Queue。默认是FairSharePolicy。

分配Container是一次深度优先搜索:从root节点开始,首先检查当前节点资源是否用满,是则直接返回(这里会同时考虑CPU和内存)。如果当前节点是ParentQueue,就将所有子节点排序(SchedulingPolicy决定了这里的顺序),依次尝试在每个子节点上分配container;如果当前节点是LeafQueue,就将下面的app排序(也是SchedulingPolicy决定,但加入了一些app特有的判断条件),依次尝试为每个app分配资源;如果当前节点是app,会比较当前app的资源需求与节点的剩余资源,如果能满足,才真正分配container。至此整个资源分配过程才完成。如果找不到合适的app,最后会返回null。

从上面的过程可以看出,每次NM心跳时,都会触发一次资源分配,而且只能分配一个container。所以NM的心跳频率会影响到整个集群的吞吐量。另外可以配置参数yarn.scheduler.fair.assignmultiple让一次心跳分配多个container,默认是false。

下面看下默认的FairSharePolicy是如何排序的。这个policy只考虑内存资源,但跟max-min failness不太一样。max-min fairness关注整体资源的公平分配,而FairSharePolicy目的在于公平分配“被调度的机会”,所以最终的资源分配可能不是算法上的最优解。但目的是一样的,都是让所有app有机会运行,不会被饿死。

每个Schedulable对象都有minShare、maxShare、fairShare 3个属性,其中minShare、maxShare用于排序,fairShare用于抢占式调度,后文会讲到。此外还有权重属性(weight),也会用于排序。对于queue而言,minShare、maxShare就是fair-scheduler.xml里配置的minResource和maxResource,weight也是直接配置的。对于application而言minResource直接返回0,maxResource直接返回Integer.MAX_VALUE,weight如果没有配置yarn.scheduler.fair.sizebasedweight=true就直接返回1.0,意味着所有app的权重是相同的。

FairSharePolicy在比较两个Schedulable对象时,先看是否有已分配资源小于minShare的,如果是说明当前Scheduable处于饥饿状态,应该被优先满足。如果两个Schedulable都处于饥饿状态,就看谁占minShare的比例更小(谁更饿)。如果没有饥饿状态的,就比较两个Schedulable已用资源与权重的比例,这个比例越大,说明占用了越多的资源,为了公平,应该给另一个Schedulable分配资源。

DominantResourceFairnessPolicy是YARN中DRF算法的实现,会考虑内存和CPU两种资源,排序逻辑会更复杂些,这里略过。

 

任务分配过程

任务分配过程决定一个app被分到哪个队列。相对于资源分配过程,这个过程简单的多。因为app在提交的时候一般会指定队列名。

第一步:检查提交的app是否指定了队列名。如果没有指定,检查是否存在和用户同名的队列。如果还不存在,就提交到default队列。default队列可以在配置文件中指定,也可以在调度器初始化时默认创建。
第二步:检查ACL,当前用户是否有向指定队列提交任务的权限。
第三步:如果通过ACL检查,发出一个APP_ACCEPTED事件。app加入LeafQueue的children,开始等待资源分配。

FairScheduler的一个特点是客户端可以动态创建队列,即指定一个不存在的队列。但生产环境中这一般是不允许的。

抢占式调度

FairScheduler特有的功能。当某个队列资源不足时,调度器会杀死其他队列的container以释放资源,分给这个队列。这个特性默认是关闭的。
关键点有两个:1.启动抢占式调度的条件?2.选择哪些container去杀掉?

前文说过每个Schedulable对象都有minShare、fairShare属性。这两个属性是抢占式调度的阈值。当一个Schedulable使用的资源小于fairShare*0.5、或者小于minShare,并且持续超过一定时间(这两种情况的超时时间不同,可以设置),就会开始抢占式调度。

Schedulabe的fairShare是会不断变化的(minShare一般不会变化)。如果队列的minResource、maxResource、权重等属性变化,fairShare都要重新计算。application开始或结束,也都要重新计算fairShare。FairScheduler中有一个线程UpdateThread,默认每0.5秒调用一次update方法,就会重新计算fairShare。
计算fairShare的过程就是将“上层”Schedulable的fairShare,“公平”的分配给下层的Schedulable。计算过程从root queue开始。root queue的fairShare就是整个集群的可用资源。怎样才算公平?要综合考虑各个Schedulable的权重、minShare、maxShare,算法也是由SchedulingPolicy决定的。默认是FairSharePolicy。这个计算逻辑跟max-min fairness类似。

当FairScheduler决定开始抢占时,首先会计算要抢得的资源量。对于使用资源量小于minShare的,要恢复到minShare;对于使用量小于fairShare*0.5的,需要恢复到fairShare。将所有要恢复的资源量相加,得出要抢的的资源总量。然后遍历所有LeafQueue,找到所有资源用量大于fairShare的app,将他们在运行的container加入一个List,按优先级升序排列。然后遍历,优先杀死优先级低的container。当释放足够的资源后,抢占停止。

如何确定container的优先级?这是由AM在申请资源的时候决定的。用一个整数表示,数字越大优先级越低。以MapReduce为例,AM Container是0,Reduce Container是10,Map Contaienr是20。意味着一个map任务更容易被杀死。

抢占式调度可以一定程度上保证公平,但不可控因素比较多。如果用户的长时间任务因此失败,是不可接受的。所以生产环境一般关闭这个特性。

计算本地性

从MapReduce时代开始,“移动计算比移动数据更经济”的概念就深入人心。在YARN中,当然也继承了这一传统。这一特性主要是用来配合HDFS的,因为HDFS的多副本,任务应该尽量在选择block所在的机器上执行,可以减少网络传输的消耗。如果开启了Short-Circuit Read特性,还可以直接读本地文件,提高效率。

本地性有3个级别:NODE_LOCAL、RACK_LOCAL、OFF_SWITCH,分别代表同节点、同机架、跨机架。计算效率会依次递减。

根据前文所述,Container在申请时可以指定节点,但这不是强制的。只有NM心跳的时候才会分配资源,所以container无法一般确定自己在那个节点上执行,基本是随机的。scheduler能做的只是尽量满足NODE_LOCAL,尽量避免OFF_SWITCH。计算本地性更多的要AM端配合,当AM拿到资源后,优先分配给NODE_LOCAL的任务。

但FairScheduler中,允许一个app错过若干次调度机会,以便能分到一个NODE_LOCAL的节点。由yarn.scheduler.fair.locality.threshold.node控制。这个参数是一个百分比,表示相对整个集群的节点数目而言,一个app可以错过多少次机会。
比如yarn.scheduler.fair.locality.threshold.node为0.2,集群节点数为10。那么FairScheduler分配这个资源时,发现当前发来心跳的NM不能满足这个app的NODE_LOCAL要求,就会跳过,继续寻找下一个APP。相当于这个app错过一次调度机会,最多可以错过2次。
对RACK_LOCAL而言,有一个参数yarn.scheduler.fair.locality.threshold.rack,作用差不多。

 

二、1.0和2.0版本的区别

1.MapReduce1的组件

  • JobTracker: 主要负责资源监控管理和作业调度

  (a)监控所有TaskTracker 与job的健康状况,一旦发现失败,就将相应的任务转移到其他节点; 
  (b)同时JobTracker会跟踪任务的执行进度、资源使用量等信息,并将这些信息告诉任务调度器,而调度器会在资源出现空闲时,选择合适的任务使用这些资源。

  • TaskTracker:是JobTracker与Task之前的桥梁

  (a)从JobTracker接收并执行各种命令:运行任务、提交任务、Kill任务、重新初始化任务; 
  (b)周期性地通过心跳机制,将节点健康情况和资源使用情况、各个任务的进度和状态等汇报给JobTracker。
Task Scheduler: 任务调度器(默认FIFO,先按照作业的优先级高低,再按照到达时间的先后选择被执行的作业)

2.MapReduce1的缺陷

Hadoop1.x的MapReduce框架的主要局限: 
(1)JobTracker 是 Map-reduce 的集中处理点,存在单点故障,可靠性差; 
(2)JobTracker 完成了太多的任务,造成了过多的资源消耗,当 map-reduce job 非常多的时候,会造成很大的内存开销,潜在来说,也增加了 JobTracker 失效的风险,这也是业界普遍总结出老 Hadoop 的 Map-Reduce 只能支持 4000 节点主机的上限,扩展性能差。 
(3)可预测的延迟:这是用户非常关心的。小作业应该尽可能快得被调度,而当前基于TaskTracker->JobTracker ping(heartbeat)的通信方式代价和延迟过大,比较好的方式是JobTracker->TaskTracker ping, 这样JobTracker可以主动扫描有作业运行的TaskTracker。

3. MapReduce2架构设计

 

 

  • 从官网上我们可以看到,在Apache Hadoop 2.x中,我们将资源管理和作业调度功能分解为Apache Hadoop YARN来进行管理,它一种通用的分布式应用程序管理框架,而Apache Hadoop MapReduce(又名MRv2)是一个纯粹的分布式计算框架。官网地址
  • 之前的MapReduce运行时(又名MRv1)已经被重用,没有进行较大的变化。因此,MRv2能够确保与MRv1应用的令人满意的兼容性。但是,由于一些改进和代码重构,一些API已经向后兼容。
  • 可以看出不同的是资源管理和作业管理系统,MRv1中资源管理和作业管理均是由JobTracker实现的,集两个功能于一身,而在MRv2中,将这两部分分开了。

MRv2最基本的设计思想是将JobTracker的两个主要功能,即资源管理和作业调度/监控分成两个独立的进程。在该解决方案中包含两个组件:全局的ResourceManager(RM)和与每个应用相关的ApplicationMaster(AM)。这里的“应用”指一个单独的MapReduce作业。RM和与NodeManager(NM,每个节点一个)共同组成整个数据计算框架。RM是系统中将资源分配给各个应用的最终决策者。AM实际上是一个具体的框架库,它的任务是【与RM协商获取应用所需资源】和【与NM合作,以完成执行和监控task的任务】。

 

三、MapReduce2相关介绍

1.组成部分

ResourceManager(RM)包含两个主要的组件:定时调用器(Scheduler)以及应用管理器(ApplicationManager) 
(1)调度器(Scheduler):根据容量,队列等限制条件,将系统中的资源分配给各个正在运行的应用。这里的调度器是一个“纯调度器”,因为它不再负责监控或者跟踪应用的执行状态等,此外,他也不负责重新启动因应用执行失败或者硬件故障而产生的失败任务。调度器仅根据各个应用的资源需求进行调度,这是通过抽象概念“资源容器”完成的,资源容器(Resource Container)将内存,CPU,磁盘,网络等资源封装在一起,从而限定每个任务使用的资源量。总而言之,定时调度器负责向应用程序分配资源,它不做监控以及应用程序的状态跟踪,并且它不保证会重启由于应用程序本身或硬件出错而执行失败的应用程序。

(2)应用管理器(ApplicationsManager,ASM):ASM主要负责接收作业,协商获取第一个容器用于执行AM和提供重启失败AM container的服务。

NodeManager:NM是每个节点上的框架代理,主要负责启动应用所需的容器,监控资源(内存,CPU,磁盘,网络等)的使用情况并将之汇报给调度器(Scheduler)。

ApplicationMaster:每个应用程序的ApplicationMaster负责从Scheduler申请资源,以及跟踪这些资源的使用情况以及任务进度的监控。

Container:是YARN中资源的抽象,它将内存、CPU、磁盘、网络等资源封装在一起。当AM向RM申请资源时,RM为AM返回的资源便是用Container表示的。

2.MapReduce2提交应用程序的过程分析

 

1.作业提交阶段,client向RM提交一个job,这时RM会进行检查,如果没有问题,会返回作业提交的路径和jobID,client向hdfs上传文件(Jar包、配置文件),准备就绪后请求RM运行作业。

2.作业初始化阶段,用户将应用程序提交到RM后,RM为该作业分配第一个Container,并与对应的NM通信,在Container中启动作业的MRAppMaster。

3.MRAppMaster首先向ResourceManager注册,这样用户可以直接通过ResourceManage查看应用程序的运行状态,然后它将为各个任务申请资源,并监控它的运行状态;

4.MRAppMaster采用轮询的方式式通过RPC协议向RM申请任务所需资源;

5.一旦MRAppMaster申请到资源后,便与对应的NodeManager通信,要求它启动任务;

6.NodeManager为任务设置好运行环境(包括环境变量、JAR包、二进制程序等)后,将任务启动命令写到一个脚本中,并通过运行该脚本启动任务;

7.各个任务通过某个RPC协议向MRAppMaster汇报自己的状态和进度,以让MRAppMaster随时掌握各个任务的运行状态,从而可以在任务失败时重新启动任务。在应用程序运行过程中,用户可随时通过RPC向MRAppMaster查询应用程序的当前运行状态;

8.应用程序运行完成后,ApplicationMaster向ResourceManager注销并关闭自己。

Job 提交过程

  job的提交通过调用submit()方法创建一个JobSubmitter实例,并调用submitJobInternal()方法。整个job的运行过程如下:

  1、向ResourceManager申请application ID,此ID为该MapReduce的jobId。

  2、检查output的路径是否正确,是否已经被创建。

  3、计算input的splits。

  4、拷贝运行job 需要的jar包、配置文件以及计算input的split 到各个节点。

  5、在ResourceManager中调用submitAppliction()方法,执行job

 

Job 初始化过程

  1、当resourceManager收到了submitApplication()方法的调用通知后,scheduler在其中一个NodeManager中启动container,随之ResouceManager发送applicationMaster的相关信息,告知每个nodeManager管理器。

  2、由applicationMaster决定如何运行tasks,如果job数据量比较小,applicationMaster便选择将tasks运行在一个JVM中。那么如何判别这个job是大是小呢?当一个job的mappers数量小于10个只有一个reducer或者读取的文件大小要小于一个HDFS block时,(可通过修改配置项mapreduce.job.ubertask.maxmaps,mapreduce.job.ubertask.maxreduces以及mapreduce.job.ubertask.maxbytes 进行调整)

  3、在运行tasks之前,applicationMaster将会调用setupJob()方法,随之创建output的输出路径(这就能够解释,不管你的mapreduce一开始是否报错,输出路径都会创建)

Task 任务分配

  1、接下来applicationMaster向ResourceManager请求containers用于执行map与reduce的tasks(step 8),这里map task的优先级要高于reduce task,当所有的map tasks结束后,随之进行sort(这里是shuffle过程后面再说),最后进行reduce task的开始。(这里有一点,当map tasks执行了百分之5%的时候,将会请求reduce,具体下面再总结)

  2、运行tasks的是需要消耗内存与CPU资源的,默认情况下,map和reduce的task资源分配为1024MB与一个核,(可修改运行的最小与最大参数配置,mapreduce.map.memory.mb,mapreduce.reduce.memory.mb,mapreduce.map.cpu.vcores,mapreduce.reduce.reduce.cpu.vcores.)

Task 任务执行

  1、这时一个task已经被ResourceManager分配到一个container中,由applicationMaster告知nodemanager启动container,这个task将会被一个主函数为YarnChild的java application运行,但在运行task之前,首先定位task需要的jar包、配置文件以及加载在缓存中的文件

  2、YarnChild运行于一个专属的JVM中,所以任何一个map或reduce任务出现问题,都不会影响整个nodemanager的crash或者hang

  3、每个task都可以在相同的JVM task中完成,随之将完成的处理数据写入临时文件中。

Job 完成

   最终,applicationMaster会收到一个job完成的通知,随后改变job的状态为successful。最终,applicationMaster与task containers被清空。

转载于:https://www.cnblogs.com/yn-huang/p/10919302.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值