从零爬Hadoop系列_3-《Hadoop技术内幕2》RM-资源调度器

资源调度器

资源调度器对应昨天总结的RM中的资源调度模块,它是RM中一个插拔式的服务组件,对于资源调度器的理解,可以从两个角度出发:

  1. 资源:从资源角度出发,资源调度器负责管理NM向RM反馈的资源情况,并根据一定策略合理的进行资源分配
  2. 任务:从任务即应用程序出发,资源调度器负责从任务队列中,根据一定策略挑选出合适的任务分配给它资源,让它运行

MRv1中的资源调度器

单队列FIFO调度机制,适用于批处理场景,面对的发展背景:随着Hadoop的普及,单个Hadoop集群上的用户量和应用程序种类不断增加。

  1. FIFO的缺点:
    • 不同应用程序对硬件资源需求侧重不同,FIFO不能充分利用集群资源
    • 不同用户对应用程序的服务质量要求(QoS:Quality of Service)不同,FIFO不能满足多样化的需求:
      • 批处理作业:耗时长,对反应时间没有严格要求,如数据挖掘、机器学习
      • 交互式作业:要求及时性,如SQL查询(Hive)
      • 生产性作业:要求有一定量的资源保证,如统计值计算、垃圾数据分析等
  2. 新的需求:适用于多用户的资源调度器,能够根据不同用户需求或不用应用程序的QoS针对性地进行资源分配和调度,充分利用集群资源
  3. 多用户资源调度器设计思路:
    • 在一个物理集群上虚拟多个Hadoop集群,每个集群各自拥有全套独立的Hadoop服务,每个虚拟集群满足不同的需求,典型的代表是HOD:Hadoop On Demand
    • 扩展YARN调度器,使之支持多个队列多用户,典型代表:
      • Yahoo!的Capacity Scheduler
      • Facebook的Fair Scheduler
  4. HOD:主要依赖于一个资源管理器(Torque)为它分配、回收节点和管理各个节点上的作业运行情况,HOD只需在资源管理器分配的节点上运行Hadoop守护进程和MR作业即可,HOD只在Hadoop 1.0中提供,Hadoop 2.0不再提供,这里也不再详述,主要缺陷如下:
    • 多个虚拟集群不方便管理
    • 多个独立的虚拟集群不能共享资源,利用率低下
    • 虚拟集群通常使用一个外部全局共享的HDFS,即丧失了数据本地特性

YARN资源调度器

基本架构

  1. YARN自带三种常用资源调度器,而且是插拔式的,用户可以按照接口规范自定义实现自己的:
    • FIFO
    • Capacity Scheduler
    • Fair Scheduler
  2. YARN的资源调度器本质上是一个事件处理器,共需要处理来自外部的6种事件类型,并做不同的相应处理,其中NODE_UPDATE是最重要的事件,它会触发资源调度器最核心的资源分配机制;如下图:
    资源调度器事件处理

  3. 资源表示模型:

    • 抽象为Container,目前仅支持虚拟CPU和物理内存
    • 其中CPU参数是虚拟的,比如机器A有16个CPU,机器B有8个CPU,而且每个CPU的性能都是机器A的一倍,则两者CPU的配置参数可以都是16或100等
    • 两者参数都有取整参数配置,比如CPU默认取整单位为1,即CPU需求3.5,即取为4
  4. 资源调度模型:
    • 双层资源调度模型:
      • 第一层:RM中的Scheduler将资源分配给各个AM,资源调度器主要关注这一层;
      • 第二层:AM进一步将资源分配给它内部的各个任务,该层完全由用户应用程序决定;
    • 资源分配过程:
      • 分配过程是异步的,分为两个阶段:
        1. AM周期性地心跳,请求资源并获取已经分配到的资源(不是当次请求的,而是之前的);
        2. NM向RM汇报各个Container运行状态,如果RM发现有空闲的,则进行一次资源分配,并将分配的资源保存到对应的应用程序数据结构中,等待下次AM发送心跳信息时取走(即阶段1);
      • 详细步骤:
        1. 步骤1:NM通过周期性心跳汇报节点信息
        2. 步骤2:RM为NM返回一个心跳应答,包括需要释放的Container列表等信息
        3. 步骤3:RM收到来自NM的信息后,会触发一个NODE_UPDATE事件
        4. 步骤4:RecourceScheduler收到NODE_UPDATE事件后,会按照一定的策略将该节点上的空闲资源(步骤2中有释放的资源)分配各应用程序,并将分配结果放到一个内存数据结构中,等待AM下次心跳时领取
        5. 步骤5:AM向RM发送周期性的心跳,汇报资源需求,并领取已经分配到的Container
        6. 步骤6:RM收到来自AM心跳信息后,把新的资源需求更新到对应的数据结构中,并返回为它分配的Container
        7. 步骤7:AM收到新分配的Container列表后,会将这些Container进一步分配给它内部的各个任务
    • 资源保证机制:
      • 增量资源分配(YARN采用的):缺点是预留资源会导致资源浪费,利用率降低
      • 一次性资源分配:缺点是会产生饿死现象
    • 资源分配算法:
      • 主资源公平调度算法(Dominant Resource Fairness,DRF),适用于多资源和复杂需求的环境,主要思想是把多维度资源调度问题转化为单资源调度问题,比例大的资源即为主资源,然后把资源分配给主资源所需份额最小的
      • 最大最小公平算法(max-min fairness):优点是小作业可以快速获取资源并完成
    • 资源抢占模型:
      • 资源抢占:在资源调度器中,每个队列可设置一个最小资源量和最大资源量,作用顾名思义,但最小资源量并不是绝对的底线,当某个队列不需要任何资源时,会将空闲资源暂时借给其他需要的队列使用,所谓的抢占就是:当某个把资源借出去的队列突然收到提交的应用程序急需资源时,就需要把借出去的资源抢占回来,采用的策略是先等待再强制抢占
      • 大致步骤:
        1. 步骤1:SchedulingEditPolicy探测到需要抢占的资源,将需要抢占的资源通过事件DROP_RESERVATION和PREEMPT_CONTAINER发送给RM
        2. 步骤2:RM调用ResourceScheduler的dropContainerReservation和preemptContainer函数,标注待抢占的Container
        3. 步骤3:RM收到来自AM的心跳,并通过应答的形式把待释放的资源总量和待抢占的Container列表返回给它,AM收到列表后,可以选择如下操作:
          • 杀死这些Container
          • 选择并杀死其他Container凑够数量
          • 不做任何处理,过段时间可能有Container自行释放或被RM杀死
        4. 步骤3:SchedulingEditPolicy探测到一段时间内,AM为自行杀死约定的Container,则将这些Container封装到KILL_CONTAINER事件中发送给RM
        5. 步骤5:RM收到后调用ResourceScheduler的killContainer函数,标注这些待杀死的Container
        6. 步骤6:RM收到来自NM的心跳,并以应答的形式把待杀死的Container列表返回给它,NM收到后,将这些Container杀死,并通过心跳告知RM
        7. 步骤7:RM收到来自AM的心跳,并通过应答的形式把已经杀死的Container列表发送给它(可能AM早已通过内部通信机制知道了)
      • 需要考虑的问题:
        • 资源抢占,并不是简单的把借出去的原原本本的抢回来,而是通过一定的计算,来决定抢占哪些队列的资源(大致是抢占这种队列:资源使用量超过了参数设定的本应使用的量)
        • 如何使资源抢占代价最小:
          • YARN优先选择优先级低的Container
          • 并没有直接杀死,而是先告知AM自行处理,此时AM可以做一些进度保存等

层级队列管理机制:

YARN以树状层次的结构组织多个队列,其中用户只能把应用程序提交到最底层的叶子队列,非叶子队列的属性即下边所有叶子队列的属性和。
层级队列

  1. 特点:
    • 子队列;
    • 最小容量:
      • 每个子队列都有一个最少容量比,表示可以使用父队列的容量的百分比
      • 调度器总是优先把资源分配给资源使用率最低的队列,比如队列Q1和Q2的最小容量分别是10和20,当前两个队列以使用的资源都是5,则优先分配给Q2
      • 如上所述,不是绝对的底线,可以借出使用
    • 最大容量
  2. 队列管理机制:
    • 用户权限管理:基础OS用户管理之上,增加了“队列”这一用户组织单元,同一用户或用户组可以对应一个或多个队列
    • 系统资源管理:管理员设置每个队列的资源容量信息,调度器按照约束进行调度
  3. 命名规则:ROOT.A.A1,避免重名

Capacity Scheduler和Fair Scheduler

  1. Capacity Scheduler特点:
    • 容量保证:可以设定上下限,同一队列的应用程序共享队列资源
    • 灵活性:空闲资源可以外借出去和抢占回来,利用率高
    • 多重租赁:管理员可以按用户或按应用程序类别分不同的队列,并给不同队列设定多重约束,支持多用户共享集群和多应用程序同时运行
    • 安全保证:
      • 每个队列有严格的ACL列表控制访问用户
      • 每个用户可指定哪些用户可以查看或控制自己的应用程序
      • 管理员可指定队列管理员或集群管理员
    • 动态更新配置文件:动态修改各种配置参数,支持在线集群管理
  2. Fair Scheduler特点:
    • 资源公平共享:在每个队列内部,选择应用程序时默认采用Fair策略,即队列中n个应用程序,每个应用程序可以得到1/n的资源
    • 支持资源抢占:先等待再强制回收
    • 负载均衡:用户除了可以自定义负载均衡机制,还可以使用默认的基于任务数目的均衡机制,尽可能将系统中的任务均匀的分配到各个节点上
    • 调度策略配置灵活:管理员可以为每个队列单独设置调度策略:FIFO、Fair、DRF三种
    • 提高小应用程序响应时间:即第一点提到的,Fair策略可以使小应用程序快速获取资源并运行完成
  3. 两者比较:
    • 大体思路相同:
      1. 处理心跳信息:
        • 第一类信息:最新启动的Container:资源调度器通知RM,将对应的Container从超时监控队列中删除
        • 第二类信息:运行完成的Container:回收资源,进行分配
      2. 资源分配:
        • 当NM有运行完成的资源上报时,RS将按照指定的调度策略,从层级队列的树根遍历,依次选择队列、应用程序、Container请求进行资源分配(调度结果可能是一个或多个Container请求),即所谓的三级资源分配策略
    • 三级资源分配策略不同点:
      1. 队列选择(从树形层级结构中选择叶子队列):
        • CS根据资源使用率(已使用资源量/队列资源容量)排序选择
        • FS按照指定策略排序选择(FIFO、Fair、DRF)
      2. 应用程序选择(从叶子队列中选择APP):
        • CS按照提交时间排序选择
        • FS默认按照Fair策略排序选择
      3. Container请求选择(同一个应用程序内部有多个子任务,对应多个资源请求):
        • CS按照优先级/本地性进行选择
        • FS同上
    • 随着两者功能逐步完善,两者同质化严重,基本相同,但因为Fair Scheduler比Capacity Scheduler多了特点<调度策略配置灵活>,即每个队列有三种调度策略选择,所以可以说FS具备CS所有的功能

思考问题

  1. 为什么采用层级队列组织方式,对“多用户多队列”的理解:
    • 从用户角度管理:因为不同用户,可能有不同的权限,对提交的应用程序的QoS要求不同,这时便可以把用户按照权限或QoS要求分类放置到不同的队列中进行分类管理
    • 从应用程序角度管理:因为不同的应用程序有不同的特点和要求,对资源的需求也不同,这时便可以把应用程序按照特点或资源需求分类放置在不同的队列中分类管理(其实还是对用户分类,因为只能对队列设定可以提交任务的用户)
    • 从资源的角度管理:如果是单队列,可能出现某个用户或某个应用程序霸占整个集群资源的情况,无法均衡地支持多用户多应用程序并行,这时便可以给不同的队列设定不同的资源上下限,进行资源分配管理,平衡地支持多用户多应用程序共享资源,并行运行
  2. 前一篇的疑问:AM为任务申请到资源后,为什么由AM通知NM,而不是RM通知NM:
    • 双层资源调度模型:
      • 使RM集中关注各个AM的资源分配,分担压力,功能划分
      • 下放资源分配的权限,AM内部资源分配由用户定义,灵活扩展
  3. Container只是对资源逻辑上的划分和规定,在实际使用中,如何保证严格的物理划分或隔离?
  4. 用户提交的应用程序和应用程序提交的内部任务,使用的都是这种层级队列吗?同一个吗?
  5. 通过对两种调度器三级资源分配策略的分析,可以发现在选择Container请求时,好像并没有检查将要分配出去的Container是否满足请求中的需求量,是这样吗?