- 随着Hadoop的普及,单个Hadoop集群的用户量越来越大,不同用户提交的应用程序往往具有不同的服务质量要求,典型的应用有以下几种:
- 批处理作业。这种作业往往耗时较长,对完成时间一般没有严格要求,如数据挖掘、机器学习等方面的应用程序
- 交互式作业。这种作业期望能及时返回结果,如用HIVE执行查询
- 生产性作业。这种作业要求有一定量的资源保证,如统计值计算、垃圾数据分析等
基本架构
- 资源调度器是YARN中最核心的组件之一,且是插拔式的,它定义了一整套接口规范以便用户可按照需要实现自己的调度器
- YARN自带FIFO、Capacity Scheduler和Fair Scheduler三种常用资源调度器,当然,用户可按照接口规范编写一个新的资源调度器,并通过简单的配置使它运行起来
- YARN的资源管理器实际上是一个事件处理器,它需要处理来自外部的6种Scheduler-EventType类型的事件,并根据事件的具体含义进行相应的处理,6种事件含义如下:
- NODE_REMOVED:表示集群中移除了一个计算节点(可能是节点故障或者管理员主动移除),资源调度器收到该事件时需要从可分配资源总量中移除相应的资源
- NODE_ADDED:表示集群中增加了一个计算节点,资源调度器收到该事件时需要将新增的资源量添加到可分配资源总量中
- APPLICATION_ADDED:表示ResourceManager收到一个新的Application。通常而言,资源管理器需要为每个Application维护一个独立的数据结构,以便于统一管理和资源分配。资源管理器将该Application添加到相应的数据结构中
- APPLICATION_REMOVED:表示一个Application运行结束(可能成功或失败),资源管理器将该Application从相应的数据结构中清除
- CONTAINER_EXPIRED:当资源调度器将一个Container分配给某个Application-Master后,如果该ApplicationMaster在一定时间内没有使用该Container,则资源调度器会对该Container进行(回收后)再分配
- NODE_UPDATE:ResourceManager收到NodeManager通过心跳机制汇报的信息后,会触发一个NODE_UPDATE事件,由于此时可能有新的Container得到释放,因此该事件会触发资源分配。也就是说,该事件是6个事件中最重要的事件,它会触发资源调度器最核心的资源分配机制
资源表示模型
- NodeManager启动时会向ResourceManager注册,注册信息中包含该节点可分配的CPU和内存总量,这两个值均可通过配置选项设置,具体如下:
-
yarn.nodemanager.resource.memory-mb
:可分配的物理内存总量,默认是8G -
yarn.nodemanager.vmem-pmem-ratio
:任务使用单位物理内存量对应最多可使用的虚拟内存,默认值是2.1,表示使用1M的物理内存,最多可以使用2.1MB的虚拟内存总量 -
yarn.nodemanager.resource.cpu-vcores
:可分配的虚拟CPU个数,默认是8。为了更细粒度地划分CPU资源和考虑到CPU性能差异,YARN允许管理员根据实际需要和CPU性能将每个物理CPU划分成若干个虚拟CPU,而管理员可为每个节点单独配置可用的虚拟CPU个数,且用户提交应用程序时,也可指定每个任务需要的虚拟CPU数
-
- YARN支持的调度语义:
- 请求某个节点上的特定资源量
- 请求某个特定机架上的特定资源量
- 将某些节点加入(或移除)黑名单,不再为自己分配这些节点上的资源
- 请求归还某些资源
- 不支持的调度语义:
- 请求任意节点上的特定资源量
- 请求任意机架上的特定资源量
- 请求一组或几组符合某种特质的资源
- 超细粒度资源。比如CPU性能要求、绑定CPU等
- 动态调整Container资源,允许根据需要动态调整Container资源量
资源调度模型
- YARN采用了双层资源调度模型
- 第一层中,ResourceManager中的资源调度器将资源分配给各个ApplicationMaster
- 第二层中,ApplicationMaster再进一步将资源分配给它的内部任务
- YARN的资源分配过程是异步的,也就是说,资源调度器将资源分配给一个应用程序后,它不会立刻push给对应的ApplicationMaster,而是暂时放到一个缓冲区中,等待ApplicationMaster通过周期性的心跳主动来取(pull-based通信模型)
- YARN采用了增量资源分配机制(当应用程序申请的资源暂时无法保证时,为应用程序预留一个节点上的资源直到累计释放的空闲资源满足应用程序需求),这种机制会造成浪费,但不会出现饿死现象
- YARN资源调度器采用了主资源公平调度算法,DRF的基本设计思想则是将最大最小化公平算法应用于主资源上,进而将多维资源调度问题转化为单资源调度问题,即DRF总是最大化所有主资源中最小的
资源抢占模型
- 在资源调度器中,每个队列可设置一个最小资源量和最大资源量,其中,最小资源量是资源紧缺情况下每个队列需保证的资源量,而最大资源量则是极端情况下队列也不能超过的资源使用量
- 为了提高资源利用率,资源调度器(包括Capacity Scheduler和Fair Scheduler)会将负载较轻的队列的资源暂时分配给负载重的队列(即最小资源量并不是硬资源保证,当队列不需要任何资源时,并不会满足它的最小资源量,而是暂时将空闲资源分配给其他需要资源的队列),仅当负载较轻队列突然收到新提交的应用程序时,调度器才进一步将本属于该队列的资源分配给它。但由于此时资源可能正被其他队列使用,因此调度器必须等待其他队列释放资源后,才能将这些资源“物归原主”,这通常需要一段不确定的等待时间。为了防止应用程序等待时间过长,调度器等待一段时间后若发现资源并未得到释放,则进行资源抢占
- 仅当启用的调度器实现了PreemptableResourceScheduler接口,且参数
yarn.resourcemanager.secheduler.monitor.enable
的值被置为true(默认值为false)时,ResourceManager才启用资源抢占功能。资源抢占是通过第三方策略触发的,这些策略被实现成一些插拔式的组件类(实现SchedulingEditPolicy接口),并通过参数yarn.resourcemanager.schduler.monitor.policies
指定(默认情况下,YARN提供了默认实现ProporitonalCapacityPreemptionPolicy) - ResourceManager将依次遍历这些策略类,并由监控类SchedulingMonitor进一步封装它们,SchedulingMonitor将周期性调用策略类的editSchedule()函数,以决定抢占哪些Container的资源
- 在YARN中,资源抢占整个过程可概括为如下步骤:
- SchedulingEditPolicy探测到需要抢占的资源,将需要抢占的资源通过事件DROP_RESERVATION和PREEMPT_CONTAINER发送给ResourceManager
- ResourceManager调用ResourceScheduler的dropContainerReservation和preemptContainer函数,标注待抢占的Container
- ResourceManager收到来自ApplicationMaster的心跳信息,并通过心跳应答将待释放的资源总量和待抢占Container列表返回给它。ApplicationMaster收到该列表后,可选择如下操作:
- 杀死这些Container
- 选择并杀死其他Container以凑够总量
- 不做任何处理,过段时间可能有Container自行释放资源或者由ResourceManager杀死Container
- SchedulingEditPolicy探测到一段时间内,ApplicationMaster未自行杀死约定的Container,则将这些Container封装到KILL_CONTAINER事件中发送给ResourceManager
- ResourceManager调用ResourceScheduler的killContainer函数,而ResourceScheduler则标注这些待杀死的Container
- ResourceManager收到来自NodeManager的心跳信息,并通过心跳应答将待杀死的Container列表返回给它,NodeManager收到该列表后,将这些Container杀死,并通过心跳告知ResourceManager
- ResourceManager收到来自ApplicationMaster的心跳信息,并通过心跳应答将已杀死的Container列表发送给它
- 在YARN中,队列是按照树形结构组织的,一个队列当前实际可以使用的资源量R取决于最小资源量A(由管理员配置)、队列资源需求量(处于等待或者运行状态的应用程序尚需的资源总量)和同父兄弟队列的空闲资源量C(多余资源可共享给其他队列),这意味着R在不同时间点的取值是不同的,可以按照递归算法求出R=F(A, B, C),这样,如果一个队列当前正在使用资源量U>R,则需从该队列中抢占(U-R)资源
- 为了尽可能避免资源浪费,YARN优先选择优先级低的Container作为资源抢占对象,且不会立刻杀死Container,而是将释放资源的任务留给应用程序自己:ResourceManager将待杀死的Container列表发送给对应的ApplicationMaster,以期望它采取一定的机制自行释放这些Container占用的资源,比如先进行一些状态保存工作后,再将对应的Container杀死,以避免计算浪费,如果一段时间后,ApplicationMaster尚未主动杀死这些Container,则ResourceManager再强制杀死这些Container
层级队列管理机制
- 层级队列组织方式具有以下特点:
- 子队列
- 队列可以嵌套,每个队列可以包含子队列
- 用户只能将应用程序提交到最底层的队列,即叶子队列
- 最少容量
- 每个子队列均有一个“最少容量比”属性,表示可以使用父队列的容量百分比
- 调度器总是优先选择当前资源使用率(当时使用的/最少容量)最低的队列,并为之分配资源
- 最少容量不是“总会保证的最低容量”,也就是说,如果一个队列的最少容量为20,而该队列中所有队列仅使用了5,那么剩下的15可能会分配给其他需要的队列
- 最少容量的值为不小于0的数,但也不能大于“最大容量”
- 最大容量
- 为了防止一个队列超量使用资源,可以为队列设置一个最大容量,这是一个资源使用上限,任何时刻使用的资源总量都不能超过该值
- 默认情况下队列的最大容量是无限大,这意味着,当一个队列只分配了20%的资源,所有其他队列没有应用程序时,该队列可能使用100%的资源,当其他队列有应用程序提交时,再逐步归还
- 用户权限管理
- 管理员可配置每个叶子队列对应的操作系统用户和用户组(Hadoop允许一个操作系统用户或者用户组可对应一个或多个队列),也可以配置每个队列的管理员,他可以杀死该队列中任何应用程序,改变任何应用程序的优先级等(默认情况下用户只能管理自己的应用程序)
- 系统资源管理
- YARN资源管理和调度均由调度器完成,管理员可在调度器中设置每个队列的资源容量,每个用户资源量等信息,而调度器则按照这些资源约束对应用程序进行调度
- 子队列
参考:《Hadoop 技术内幕:深入解析 YARN 架构设计与实现原理》