0012 - YARN三种资源调度器解析

Posted 小左先森

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了0012 - YARN三种资源调度器解析相关的知识,希望对你有一定的参考价值。

大数据梦工厂(0012 - YARN三种资源调度器解析)


1 - YARN资源调度器简介

资源调度器(ResourceScheduler)是 Hadoop YARN 中最核心的组件之一,且是 ResourceManager 中的一个服务组件,负责整个 Hadoop 集群资源的分配和管理,用于解决各应用程序之间集群资源抢占的问题。

在 YARN 中,有三种资源调度器可供选择:

  • FIFO Scheduler(先进先出调度器)
  • Capacity Scheduler(容量调度器)
  • Fair Scheduler(公平调度器)

2 - YARN资源调度器架构

YARN 的资源调度器(ResourceScheduler)实际上是一个事件处理器(EventHandler),主要处理 6 种 Scheduler EventType 类型的事件,如下图所示:

  • NODE_ADDED - 集群中增加一个计算节点,资源调度器将新增的资源量添加到可分配资源总量中。
  • NODE_REMOVED - 集群中移除一个计算节点(故障、缩容、主动移除等)。资源调度器将从分配资源总量中移除相应的资源量。
  • NODE_UPDATE - NodeManager 通过心跳机制向 ResourceManager 汇报信息。如果有 Container 得到释放,则会触发资源分配。
  • APP_ADDED - ResourceManager 收到一个新的 ApplicationMaster。将该应用程序添加到一个相应的独立数据结构队列中。
  • APP_REMOVED - 当一个应用程序结束(成功、失败)。ResourceManager 在相应的独立数据结构队列中删除对应的应用程序。
  • CONTAINER_EXPIRED - 当 ApplicationMaster 收到资源调度器新分配的一个 Container 后,必须在一定时间(默认为 10 分钟)内在对应的 NodeManager 上使用该 Container,否则资源调度器会对该 Container 进行回收后再分配。

3 - FIFO Scheduler

3.1 - FIFO 调度器概述

在 Hadoop-1.x 系列版本中,默认使用的调度器是 FIFO Scheduler(先进先出调度器)

FIFO Scheduler 采用队列方式将每个任务按照时间先后顺序进行服务。也就意味着在集群中同时只能有一个任务运行。所有的任务按照提交顺序来执行,在上一个任务执行完成后,下一个任务按照队列中的顺序执行。在进行资源分配时,首先为队列中第一个任务的请求分配资源,第一个任务的请求被满足后再依次为队列中下一个任务服务。

3.2 - FIFO 调度器配置

若要启用 FIFO 调度器,修改 yarn-site.xml 文件即可:

<configuration>
  <property>
    <name>yarn.resourcemanager.scheduler.class</name>
    <value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.fifo.FifoScheduler</value>
  </property>
</configuration>

4 - Capacity Scheduler

4.1 - Capacity 调度器概述

在 Hadoop-2.x/3.x 系列版本中,默认使用的调度器是 Capacity Scheduler(容量调度器)。是 Yahoo 开发的多用户调度器,以队列为单位划分集群资源,每个队列可设定资源的最低保证和使用上限。

Capacity 调度器是一种多用户、多队列的资源调度器,其本身也属于弹性队列,但默认情况下只有 root.default 这一个队列。当不同用户提交任务时,任务都会在该队列中按照 FIFO 先进先出 的策略执行调度。因此,需要配置多个队列。

例如:下图是 Capacity 调度器中一个队列树。

上图通过队列树方式把 YARN 集群资源池(CPU、内存)做了一个分配,在 root 队列下面分成 default 和 dev 两个子队列,分别占用 20% 和 80% 的资源;而 dev 队列又可以继续分为 dev1dev2 两个子队列,分别占用 dev 队列 80% 中的 40% 和 60% 的资源。

注意:

同一个父队列下的同一级别的子队列 Capacity 之和必须为 100。
例如:root 下有两个子队列 default 和 dev,两个子队列的 Capacity 求和必须为 100;dev 下又设置了 dev1 和 dev2,而 dev 下两个子队列之和必须也是 100。

此时,已经对 YARN 集群资源池进行了分配,如果某一个队列的资源不足,同时集群又有空闲资源时,那么该队列就可以使用空闲队列的空闲资源,使用多少根据其具体配置而定。

但是,这样就存在一个问题:

  • (1)假设 A 队列 分配了 40% 资源,B 队列 分配了 60% 资源;
  • (2)此时 A 队列 使用资源很少,而 B 队列 又资源不足,那么 B 队列 就会抢占 A 队列 的资源;
  • (3)但过一会可能又有任务提交到 A 队列,如果 A 队列 资源不足,那么该任务只能等待 B 队列 执行完成后,释放资源。

为了避免资源不被其它队列过多抢占,可以为队列设置最大容量限制(yarn.scheduler.capacity.&lt;queue-path&gt;.maximum-capacity)。例如:root.dev 队列分配了 80% 的资源,但可能在某种情况下,该队列资源不足,而集群其它队列资源充足,该队列的资源占比最高可以达到 90%(配置如下)。如果一个队列没有配置 maximum-capacity,理论上该队列可以占用整个集群 100% 的资源(子队列需要看父队列的资源占比和最大资源占比限制)。

4.2 - Capacity 调度器配置

若要启用 Capacity 调度器,修改 yarn-site.xml 文件即可:

<configuration>
  <property>
    <name>yarn.resourcemanager.scheduler.class</name>
    <value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler</value>
  </property>
</configuration>

修改 capacity-scheduler.xml 文件,配置队列的部分参数如下:

<configuration>
  <property>
    <name>yarn.scheduler.capacity.root.queues</name>
    <value>default,dev</value>
    <description>配置 root 队列的所有子队列</description>
  </property>

  <property>
    <name>yarn.scheduler.capacity.root.default.capacity</name>
    <value>20</value>
    <description>配置 root.default 队列的集群资源占比</description>
  </property>

  <property>
    <name>yarn.scheduler.capacity.root.default.maximum-capacity</name>
    <value>40</value>
    <description>配置 root.default 队列可使用的资源上限</description>
  </property>

  <property>
    <name>yarn.scheduler.capacity.root.dev.queues</name>
    <value>dev1,dev2</value>
    <description>配置 root.dev 队列的所有子队列</description>
  </property>

  <property>
    <name>yarn.scheduler.capacity.root.dev.dev1.capacity</name>
    <value>40</value>
    <description>配置 root.dev.dev1 队列的集群资源占比</description>
  </property>

  <property>
    <name>yarn.scheduler.capacity.root.dev.dev2.capacity</name>
    <value>60</value>
    <description>配置 root.dev.dev2 队列的集群资源占比</description>
  </property>
</configuration>

4.3 - Capacity 调度器提交队列

提交作业时,如果不指定提交队列名,会默认提交到 default 队列。

指定队列时应选择子节点,不能将任务提交到父队列。例如:指定 dev1dev2 是正确的队列名,而指定 root.dev.dev1dev.dev1 是无效的队列名。

在 MapReduce 中,提交到指定队列时,通过设置 mapreduce.job.queuename 参数指定队列。示例如下:

[root@hadoop-01 ~]# hadoop jar /opt/hadoop-2.10.1/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.10.1.jar pi -Dmapreduce.job.queuename=dev1 2 2

5 - Fair Scheduler

5.1 - Fair 调度器概述

Fair Scheduler(公平调度器)是 Facebook 开发的多用户、多队列调度器,实现 YARN 上运行的任务能公平的、动态的调度集群资源,其中各个队列使用的资源根据设置的权重(weight)来实现资源的公平分配。

默认情况下只有 root.default 这一个队列。如果不指定提交队列,默认会提交到 default 队列。

1、公平调度之单个队列
假设 job1 被分配到某个队列中,且没有其它任务运行,job1 则获得集群的全部资源。如果此时 job2 也被分配到该队列中,则 job2 会先等待 job1 逐渐释放集群的一半资源给 job2 使用。一段时间后,job2 运行完成后,但 job1 仍在运行,则 job1 又会重新占用该队列的所有资源。该过程如下图所示:

2、公平调度之多个队列
也就是集群的资源是如何在队列之间公平共享的?

假设有两个用户 A 和 B,分别拥有一个自己的队列 Queue A 和 Queue B。当 A 启动一个 job1 而 B 没有提交任务时,A 会获得集群的全部资源(Queue A + Queue B);当 A 的 job1 仍在运行,且 B 启动第一个 job2 时,Queue A 会逐渐释放一半资源,两个任务会各自占用集群的一半资源。如果此时 B 再启动第二个 job3 并且其它 job 仍在运行时,则它将会和 B 的第一个 job2 共享队列 B 的资源,也就是 Queue B 的两个 job 将会分别占用集群的四分之一资源,而 A 的 job1 仍然占用集群的一半资源。

此时,这个过程中,job1 和 job2 分别占用集群的一半资源,后来由于 job3 的加入,job2 和 job3 平分 Queue B 的资源。最终结果就是集群的资源在两个用户之间实现了公平共享。该过程如下图所示:

5.2 - Fair 调度器三种策略

不同于 FIFO 调度器和 Capacity 调度器固定的调度策略,对于所有提交到某一个队列中的任务,Fair 调度器都为该队列提供了 3 种调度策略。

  • fair: 仅根据内存公平调度资源(默认)。
  • fifo: FIFO 调度先进先出策略,拥有子队列的队列不能是 FIFO。
  • dfr: Dominant Resource Fairness(主导资源公平)策略。根据 CPU 或内存公平调度资源。 (建议)
Dominant Resource Fairness(drf,主导资源公平策略):首先查看任务的主导资源是 CPU 还是内存,然后根据任务之间主导资源的占比进行资源分配。例如:
任务 集群总 CPU 集群总内存 主导资源
job1 所需 1% 所需 3% 内存,占比 3%
job2 所需 9% 所需 3% CPU,占比 9%

因此,job1 和 job2 申请资源比例为 3% : 9%1 :3),job2 分配的 Container 数量为 job1 的三倍。即 job1 占 1/4 数量 Container,job2 占 3/4 数量 Container。

5.3 - Fair 调度器配置

1、启用Fair 调度器
若要启用 Fair 调度器,修改 yarn-site.xml 文件即可:

<configuration>
  <property>
    <name>yarn.resourcemanager.scheduler.class</name>
    <value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler</value>
  </property>
</configuration>

2、配置队列
修改 fair-scheduler.xml 文件,配置队列的部分参数如下:

<allocations>
    <!-- 全局配置:所有 User 最多可以同时运行的 App 数 -->
    <userMaxAppsDefault>1000</userMaxAppsDefault>

    <!-- 定义 root 队列、配置队列属性 -->
    <queue name="root">
        <!-- weight 资源池权重 -->
        <weight>1.0</weight>
        <!-- 队列调度策略:fifo/fair/drf -->
        <schedulingPolicy>drf</schedulingPolicy>

        <!-- 定义 root.default 队列 -->
        <queue name="default">
            <weight>1.0</weight>
            <schedulingPolicy>drf</schedulingPolicy>
        </queue>

        <!-- 定义 root.user 队列 -->
        <queue name="users">
            <weight>1.0</weight>
            <schedulingPolicy>drf</schedulingPolicy>

            <!-- 定义 root.users.hive 队列 -->
            <queue name="hive">
                <!-- 队列最大可用资源 -->
                <maxResources>70.0%</maxResources>
                <weight>7.0</weight>
                <schedulingPolicy>drf</schedulingPolicy>
            </queue>

            <!-- 定义 root.users.hdfs 队列 -->
            <queue name="hdfs">
                <!-- 队列最小可用资源 -->
                <minResources>1024 mb, 1 vcores</minResources>
                <!-- 队列最大可用资源 -->
                <maxResources>10240 mb, 10 vcores</maxResources>
                <weight>3.0</weight>
                <schedulingPolicy>drf</schedulingPolicy>
            </queue>
        </queue>
    </queue>

    <!--设置 App 提交到队列中的规则 -->
    <queuePlacementPolicy>
        <!-- 如果提交命令时指定了队列,那么提交到指定队列中 -->
        <rule name="specified" create="true"/>
        <!-- 提交到与用户名相同的队列中,队列不存在就创建 -->
        <rule name="user" create="true" queue="users"/>
        <!-- 提交到 default 队列中 -->
        <rule name="default"/>
    </queuePlacementPolicy>

    <!-- user 标签为指定的用户做特定的配置 -->
    <user name="jenkins">
        <!-- jenkins 用户最多可以同时运行的 App 数量 -->
        <maxRunningJobs>100</maxRunningJobs>
    </user>
</allocations>

3、抢占
在 Fair 调度器中,当一个新的任务提交到某一个队列,而该队列并没有空闲资源分配给它,这时该任务需要等待其它任务完成一部分 Container 计算然后释放资源给新任务,以达到公平运行的目的。

为了使作业从提交到执行所需的时间可控,可以设置抢占模式(yarn.scheduler.fair.preemption 设置为 true,即开启抢占模式),当等待时间超过一定阈值时即启动抢占资源,强迫队列中其它任务立刻让出一部分资源给新任务,达到强行公平运行的目的。但至少配置有以下两个参数中的一个:

yarn-site.xml 文件,配置参数如下:

<configuration>
  <property>
    <!-- 开启抢占模式 -->
    <name>yarn.scheduler.fair.preemption</name>
    <value>true</value>
  </property>

  <property>
    <!-- 队列内使用资源超过 80%,则开始抢占其余空闲队列资源 -->
    <name>yarn.scheduler.fair.preemption.cluster-utilization-threshold</name>
    <value>0.8f</value>
  </property>

  <property>
    <!-- 判断是否需要抢占的时间间隔,默认 500ms -->
    <name>yarn.scheduler.fair.update-interval-ms</name>
    <value>500</value>
  </property>

</configuration>

fair-scheduler.xml 文件,配置参数如下:

<allocations>
    <queue name="root">
        <!-- 低于最小资源时,为所有队列配置开始抢占前的超时时间(单位:秒 -->
        <defaultMinSharePreemptionTimeout>200</defaultMinSharePreemptionTimeout>
        <!-- 低于共享资源时,为所有队列配置开始抢占前的超时时间(单位:秒 -->
        <defaultFairSharePreemptionTimeout>300</defaultFairSharePreemptionTimeout>
        <!-- 为所有队列配置抢占共享资源的阈值 -->
        <defaultFairSharePreemptionThreshold>0.5f</defaultFairSharePreemptionThreshold>

        <queue name="users">
            <!-- 低于最小资源时,为 root.users 队列配置开始抢占前的超时时间(单位:秒 -->
            <minSharePreemptionTimeout>200</minSharePreemptionTimeout>
            <!-- 低于共享资源时,为 root.users 队列配置开始抢占前的超时时间(单位:秒 -->
            <FairSharePreemptionTimeout>300</FairSharePreemptionTimeout>
            <!-- 为 root.users 队列配置抢占共享资源阈值 -->
            <fairSharePreemptionThreshold>0.5f</fairSharePreemptionThreshold>
        </queue>
    </queue>
</allocations>
  • minSharePreemptionTimeout: 最小资源抢占超时时间。若指定等待时间内未获得最小共享资源,则会启动抢占。
  • fairSharePreemptionTimeout: 公平资源抢占超时时间。若指定等待时间内未获得公平共享资源,则会启动抢占;公平共享资源由公平资源抢占阈值(fairSharePreemptionThreshold)和该队列公平资源分配值的乘积决定。例如:当前某个队列一共提交了 2 个 job,job1 独占了队列资源,job2 的公平资源理论上为该队列的 0.5 倍资源,如果 fairSharePreemptionThreshold 为 0.8(默认为 0.5),则给 job2 的队列资源为 0.8 * 0.5 = 0.4

注意:

因为队列配置是嵌套的,所以父队列的参数优先级会高于子队列。

5.4 - Fair 调度器提交队列

在 MapReduce 中,提交到指定队列时,通过设置 mapreduce.job.queuename 参数指定队列。示例如下:

[root@hadoop-01 ~]# hadoop jar /opt/hadoop-2.10.1/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.10.1.jar pi -Dmapreduce.job.queuename=dev1 2 2

6 - 延迟调度

6.1 - 延迟调度概述

YARN 的资源管理器(ResourceManager)为任务分配某个节点是基于任务所需的数据先本地后远程的原则,本地如果有资源就优先分配本地节点,如果本地没有资源则进行延迟等待(不超过几秒),待本地资源释放后就可以启动该任务,不再需要寻找远程节点,从而提高集群的效率,这种行为称为延迟调度(delay scheduling)

Capacity 容量调度器和 Fair 公平调度器都支持延迟调度。前者的调度次数是根据规则计算,后者的调度次数是通过配置指定,但实际的含义是一样。

6.2 - 延迟调度的心跳机制

YARN 中的每个节点管理器(NodeManager)周期性的(默认每秒一次)向资源管理器(ResourceManager)发送心跳请求。心跳中携带了节点管理器中正在运行的容器(Container)数量、以及可分配给新容器的资源等信息,这对于即将申请节点的应用来说,每次心跳都是一个潜在的调度机会(scheduling opportunity)

6.3 - 调度机会

如何使用 Capacity 容量调度器和 Fair 公平调度器的延迟调度,也就是配置需等待多少次心跳信息之后能分配到本地资源。

1、Capacity 容量调度器
capacity-scheduler.xml 文件,通过设置 yarn.scheduler.capacity.node-locality-delay 来配置延迟调度。

<property>
  <name>yarn.scheduler.capacity.node-locality-delay</name>
  <value>-1</value>
</property>

如果设置为一个正整数,表示调度器等待本地资源释放期间最多错过多少个远程资源释放的机会(·调度机会的数量·)。例如: 设置为 2,表示最多等待 2 次远程资源释放的信息后,如果该节点的资源仍然没释放,就不再等待,直接寻找远程节点的资源。

2、Fair 公平调度器
yarn-site.xml 文件,通过设置 yarn.scheduler.fair.locality.threshold.node 来配置延迟调度。

<property>
  <!-- 任务指定执行节点时,在分配容器时选择跳过分配次数与节点数的比例,默认 -1.0 不跳过 -->
  <name>yarn.scheduler.fair.locality.threshold.node</name>
  <value>-1.0</value>
</property>

<property>
  <!-- 任务指定执行机架时,在分配容器时选择跳过分配次数与节点数的比例,默认 -1.0 不跳过 -->
  <name>yarn.scheduler.fair.locality.threshold.rack</name>
  <value>-1.0</value>
</property>

这是一个 0-1 之间的浮点数。例如: 设置为 0.5,表示调度器将会等待集群中超过一半的节点发送资源释放信息(·调度机会·)之后,再考虑远程节点。此外,还有 yarn.scheduler.fair.locality.threshold.rack 参数,表示调度器在接受另一个机架替代本地节点所在机架之前需要等待的调度机会数量比。

注意:
一般来说,小型 Hadoop 集群(·YARN 节点不超过 100 个)使用Fair Scheduler(公平调度器),而大型 Hadoop 集群(YARN 超过 100 个节点)推荐使用Capacity Scheduler(容量调度器)`。


::: hljs-center
扫一扫,我们的故事就开始了。
:::

以上是关于0012 - YARN三种资源调度器解析的主要内容,如果未能解决你的问题,请参考以下文章

Hadoop-yarn组件的三种调度器

YARN详解(YARN架构设计常用命令三种调度器)

Yarn三种调度策略对比

[Hadoop]-Yarn-调度器篇

Yarn的资源调度与隔离

Yarn调度队列