了解Yarn的架构之前,先说说为什么会出现Yarn。在Hadoop v1版本中,存在最大的问题就是资源管理和任务管理耦合在一起,且整个集群的扩展性、可靠性(JobTracker的单节点故障问题)很差,以及最重要的一个问题,集群资源的利用率低。

    随着数据规模的膨胀,大家已经不满足于仅仅能在Hadoop集群上运行MapReduce程序,更希望能够有一套合理的管理机制来控制整个集群的资源调度,于是Yarn平台应运而生。先来看看Yarn平台的基本架构:

    

hadoop yarn 与npm yarn命令冲突 hadoop和yarn的关系_资源调度

    在Yarn的结构中,把原来JobTracker管的事儿(资源管理、任务调度)拆开了,资源调度让ResourceManager干,任务调度让ApplicationMaster管,这样的好处就是能够让各个模块各司其职,专一干一件事,就好比一个大领导每天如果不专心管理管队,老跑去敲代码,最后这个团队必然存在问题。言归正传,既然要了解Yarn的架构,在这里有必要先解释一下用户提交一个任务都走了哪些流程:

hadoop yarn 与npm yarn命令冲突 hadoop和yarn的关系_大数据_02

    知道了各模块的“位置”后,下面着重对上图中的各模块作详细介绍:



    1. ResourceManager

    ResourceManager(下文简称“RM”)是整个Yarn架构的核心部件,有且仅有一个,它负责整个集群的资源(内存、cpu等)管理,RM主要处理6中事件:

    (1) NODE_REMOVED:事件NODE_REMOVED表示集群中被移除一个计算节点(可能是节点故障或者管理员主动移除),资源调度器收到该事件时需要从可分配资源总量中移除相应的资源量。
    (2) NODE_ADDED:事件NODE_ADDED表示集群中增加了一个计算节点,资源调度器收到该事件时需要将新增的资源量添加到可分配资源总量中。
    (3) APPLICATION_ADDED:事件APPLICATION_ADDED 表示ResourceManager收到一个新的Application。通常而言,资源管理器需要为每个application维护一个独立的数据结构,以便于统一管理和资源分配。资源管理器需将该Application添加到相应的数据结构中。
    (4) APPLICATION_REMOVED:事件APPLICATION_REMOVED表示一个Application运行结束(可能成功或者失败),资源管理器需将该Application从相应的数据结构中清除。
    (5) CONTAINER_EXPIRED:当资源调度器将一个container分配给某个ApplicationMaster后,如果该ApplicationMaster在一定时间间隔内没有使用该container,则资源调度器会对该container进行再分配。
    (6) NODE_UPDATE:NodeManager通过心跳机制向ResourceManager汇报各个container运行情况,会触发一个NODE_UDDATE事件,由于此时可能有新的container得到释放,因此该事件会触发资源分配,也就是说,该事件是6个事件中最重要的事件,它会触发资源调度器最核心的资源分配机制。

hadoop yarn 与npm yarn命令冲突 hadoop和yarn的关系_资源调度_03

    NodeManager启动的时候,会向ResourceManager注册自己的内存、CPU使用率等资源信息,供NodeManager调度。

    



    2. ApplicationMaster

    ApplicationMaster 负责管理应用程序的整个生命周期,每个应用程序都对应一个AM,主要功能有:

    (1) 与RM的调度器通讯,协商管理资源分配。

    (2) 与NM合作,在合适的容器中运行对应的task,并监控这些task执行。

    (3) 如果container出现故障,AM会重新向调度器申请资源。

    (4) 计算应用程序所需的资源量,并转化成调度器可识别的协议。

    (5) AM出现故障后,ASM会重启它,而由AM自己从之前保存的应用程序执行状态中恢复应用程序。 

   



    3. NodeManager

    NodeManager替代了Hadoop v1版本中的TaskTracker,每个节点都会有一个NM,主要功能有:

    (1) 为应用程序启动容器,同时确保申请的容器使用的资源不会超过节点上的总资源。

    (2) 为task构建容器环境,包括二进制可执行文件,jars等。

    (3) 为所在的节点提供了一个管理本地存储资源的简单服务,应用程序可以继续使用本地存储资源即使他没有从RM那申请。比如:MapReduce可以使用该服务程序存储map task的中间输出结果。

    一个NodeManager上面可以运行多个Container,Container之间的资源互相隔离,类似于虚拟机的多个系统一样,各自使用自己分配的资源。NodeManager会启动一个监控进行用来对运行在它上面的Container进行监控,当某个Container占用的资源超过约定的阈值后,NodeManager就会将其杀死。

    4. Container

    Container可以说是一个对Application使用资源描述的集合(或容器),可以看做一个可序列化的java对象,封装了一些描述信息,例如:

message ContainerProto {
    optional ContainerIdProto id = 1; //container id
    optional NodeIdProto nodeId = 2; //container(资源)所在节点
    optional string node_http_address = 3;
    optional ResourceProto resource = 4; //container资源量
    optional PriorityProto priority = 5; //container优先级
    optional hadoop.common.TokenProto container_token = 6; //container token,用于安全认证
    }

    Container的一些基本概念和工作流程如下:

    (1) Container是YARN中资源的抽象,它封装了某个节点上一定量的资源(CPU和内存两类资源)。它跟Linux Container没有任何关系,仅仅是YARN提出的一个概念(从实现上看,可看做一个可序列化/反序列化的Java类)。

    (2) Container由ApplicationMaster向ResourceManager申请的,由ResouceManager中的资源调度器异步分配给ApplicationMaster;
    (3) Container的运行是由ApplicationMaster向资源所在的NodeManager发起的,Container运行时需提供内部执行的任务命令(可以使任何命令,比如java、Python、C++进程启动命令均可)以及该命令执行所需的环境变量和外部资源(比如词典文件、可执行文件、jar包等)。

    

    另外,一个应用程序所需的Container分为两大类,如下:

    (1) 运行ApplicationMaster的Container:这是由ResourceManager(向内部的资源调度器)申请和启动的,用户提交应用程序时,可指定唯一的ApplicationMaster所需的资源;
    (2) 运行各类任务的Container:这是由ApplicationMaster向ResourceManager申请的,并由ApplicationMaster与NodeManager通信以启动之。

以上两类Container可能在任意节点上,它们的位置通常而言是随机的,即ApplicationMaster可能与它管理的任务运行在一个节点上。

      

    5. YARN平台的资源管理方案

    在YARN中,用户以队列的形式组织,每个用户可属于一个或多个队列,且只能向这些队列中提交application。每个队列被划分了一定比例的资源。
    YARN的资源分配过程是异步的,也就是说,资源调度器将资源分配给一个application后,不会立刻push给对应的ApplicaitonMaster,而是暂时放到一个缓冲区中,等待ApplicationMaster通过周期性的RPC函数主动来取,也就是说,采用了pull-based模型,而不是push-based模型,这个与MRv1是一致的。

    相比于MRv1中的资源调度器,尽管YANR的调度器也是插拔式的,但由于YARN采用了事件驱动的模型,因此编写起来更加复杂,难度也远远大于MRv1。
    同MRv1一样,YARN也自带了三种常用的调度器,分别是FIFO,Capacity Scheduler和Fair Scheduler,其中,第一个是默认的调度器,它属于批处理调度器,而后两个属于多租户调度器,它采用树形多队列的形式组织资源,更适合公司应用场景。需要注意的是,这三种调度器采用的算法与MRv1中的完全一致,只不过是根据YARN中资源调度器的对外接口重新实现了一遍,在此不再赘述。