YARN中的资源调度
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了YARN中的资源调度相关的知识,希望对你有一定的参考价值。
参考技术A 理想情况下, YARN 应用发出资源请求时应该立刻给予满足。但是在实际生产环境中资源是有限的,在一个繁忙的集群上,一个应用经常需要等待才能分配到所需要的资源。
YARN 调度器的工作就是根据既定策略为应用能够分配资源。调度是一个难题,
并没有一个所谓 “最好” 的调度策略,在 YARN 提供了多种调度器和可配置策略供我们选择
YARN 中有三种调度器可用: FIFO 调度器( FIFO Scheduler ),容量调度器( Capacity Scheduler )
和公平调度器( Fair Scheduler )。
下面来详细介绍一下这三种调度方式
FIFO Scheduler(FIFO 调度器):
FIFO 调度器将所有提交的应用放置在一个队列中,按照提交的顺序(先进先出)运行应用。
首先为队列中的第一个应用的请求分配资源,当第一个应用的请求被满足后在依次为队列中的下一个应用服务。
FIFO 调度器的优点是,简单易懂,不需要任何配置,但是不适合共享集群。大的应用会占用集群中的所有资源,
所以在队列中的所有应用必须等待知道轮到自己运行。在一个共享集群中,更适合使用容量调度器或者公平调度器。
这两种调度器都允许长时间运行的作业能够及时完成,同时也允许正在进行较小的临时查询的用户能够在合理的
时间内能够及时运行并且得到返回结果。
Capacity Scheduler( 容量调度器 ):
容量调度器允许多个组织共享一个 Hadoop 集群,每个组织可以分配到全部集群资源的一部分。
每个组织对的应用被分配到一个专门的队列,每个队列被配置为可以使用一定的集群资源。队列可以进一步按层次划分,
这样每个组织内的不同用户能够共享该组织队列所分配的资源。在一个队列内,使用 FIFO 调度策略对应用进行调度。
单个作业使用的资源不会超过其队列的资源容量。在队列中有多个作业,并且该队列资源不足时,这时如果集群中
仍有可使用资源,容量调度器可能会将空余的资源分配给队列中的作业,哪怕这样做会超出该队列的资源容量。
这也被称为 “弹性队列” ( queue elasticity )。
正常操作时,容量调度器不会通过强行终止作业来抢占容器(container)。因此,如果一队列一开始资源够用,然后随着程序的运行或是需求的增长,资源开始不够用时,那么合格队列就只能等待其他队列释放容器资源。缓解这种情况的方法是,为队列设置一个最大的容量限制,这样这个队列就不会过多的侵占其他队列资源的容量了。当然,这样做是以牺牲队列弹性作为代价的,因此需要不断的尝试和失败中找出一个最合理的配置。
如果属性 yarn.scheduler.capacity.<queue-path>.user-limit-factor 设置为大于一(默认值)时,
那么一个作业可以使用超过其队列容量的资源。
HDP ( Ambari) 方式搭建的 Hadoop 集群,默认实现为(容量调度器)
Fair Scheduler(公平调度器):
公平调度器旨在为所有运行的应用公平分配集群资源。接下来解释资源是如何在队列之间公平共享的。
想象两个用户 A 和 B, 分别拥有自己的队列。当 A 启动一个作业,在 B 没有需求是 A 用户
会分配到全部可用资源:当 A 的作业仍在运行是 B 启动一个作业,一段时间后,按照我们先前看到的
方式,每个作业都用到了集群中一般的资源。这时,如果 B 启动第二个作业并且其他作业仍在运行(A作业和B作业),
那么 B 启动的第二个作业将和 B 启动的第一个作业共享资源,也就是说 B 的每个作业占用四分之一的集群资源,
而 A 用户的作业仍然占用一半的集群资源。实现了资源在用户之间的公平共享。
CDH 方式搭建的 Hadoop 集群,默认实现为(公平调度器)
Delay Scheduler (延迟调度):
所有的 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
队列又可以继续分为 dev1
和 dev2
两个子队列,分别占用 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.<queue-path>.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
队列。
指定队列时应选择子节点,不能将任务提交到父队列。例如:指定 dev1
和 dev2
是正确的队列名,而指定 root.dev.dev1
和 dev.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
扫一扫,我们的故事就开始了。
:::
以上是关于YARN中的资源调度的主要内容,如果未能解决你的问题,请参考以下文章