Hadoop的资源管理系统 —— Yarn
一、背景和作用
Yarn 是 Hadoop 的资源管理系统,用于取代 MapReduce1 的资源调度,改善 MapReduce 的实现,并且有足够的通用性,可以支持其他的分布式计算模式
一般情况下,应用不直接使用 Yarn 的API,而是通过一些分布式计算框架(MapReduce、Spark等)来间接实现资源调度管理,使用这些框架的 Yarn 应用运行在集群计算层(Yarn)和集群存储层(HDFS、HBase)上。
二、运行机制
Yarn 主要由两部分组成:resource manager、node manager。
资源管理器(resource manager)管理集群上资源的使用,节点管理器(node manager)运行在集群中所有节点上且能够启动和监控容器(container)。容器用于执行特定应用程序的进程,每个容器都有资源限制(内存、CPU)。
在 Yarn 上运行一个应用的步骤如图所示:
① 客户端向资源管理器提交一个运行一个应用的请求;
② 资源管理器找到一个 NM,在上面创建一个 container,并启动应用进程;
③ 应用可能只是简单地运行一个计算,并将结果返回给客户端;也可能向 RM 请求更多的容器来运行一个分布式计算;
④ RM 响应应用要求创建更多的 container 并启动应用进程执行运算。
三、与MapReduce1 的对比
1、MapReduce1 的管理
在 MapReduce1中,有两类守护进程控制作业执行过程:jobtracker、tasktracker。
jobtracker 通过调度 tasktracker 上运行的任务来协调所有运行在系统上的作业,记录每项作业任务的整体进度情况,若有任务失败,则在另一个 tasktracker 节点上重新调度该任务。
tasktracker 在运行任务的同时将运行进度报告发送给 job tracker。
2、Yarn 的管理
MapReduce1 的 jobtracker 既要负责资源管理(作业的调度分配),将任务分配给不同的 tasktracker;又要负责任务进度的监控。如果集群非常繁忙,每时每刻都有大量的作业,每个作业下又有很多任务,jobtracker 需要面面俱到了解每个任务的执行情况,负担很重。
在 MapReduce2 对 Yarn 的应用中,一般是会先让 RM 启动容器运行一个 Application Master 的进程,然后该进程负责创建和监控所有的 map task 和 reduce task,了解每个 task 的执行进度,每个 task 都会运行在一个单独的 container 中,这些 container 都是 Application Master 统一调度负责向 RM 申请的,这样就把资源分配和作业运行调度监控解耦,让 Yarn 专注于资源调度。
Yarn 相比 MapReduce1的优势:
① 可扩展性
Yarn 只负责资源分配作业调度,而 MapReduce1 要同时管理资源分配和任务执行。
② 可用性
每个作业都有一个专门的 application master 负责管理任务,Yarn 专注资源管理,不需要保留太多的元数据,实现 HA 时可较快转移,从而实现资源管理的高可用。而 Hadoop 实现作业的高可用也不用关心资源管理的问题,分而治之。
③ 利用率
MapReduce1 会在 tasktracker 中配置有若干固定长度的 slot,通过 slot 指定某些 slot 为 map slot,某些 slot 为 reduce slot,被指定的 slot 只能用于执行 map 或 reduce,可能会出现 map slot 空闲但是 reduce slot 不够用的情况,资源管理粗放,利用率低。
④ 多租户
Yarn 作为一个资源调度管理系统,不仅可用于 MapReduce,还可以用于 Spark、Tez 等分布式应用,间接向它们开放了 Hadoop。
四、调度
1、FIFO调度器
FIFO 调度器将应用放置在一个队列中,然后按照提交的顺序(先入先出)运行应用。
【优点】简单易懂,不需要任何配置。
【缺点】不适合共享集群。大的应用会占用集群的所有资源,每个应用必须等待直到轮到自己运行,作业平均等待时间较长。
2、容量调度器
(1)概述
为了避免小作业被大作业阻塞,容量调度器会创建几个队列,其中会有专门队列给小作业执行,保证一提交就可以启动。
每个队列都被分配了一定比例容量的资源,保证大作业不会占用整个集群的所有资源。一般每个队列对应一个组织,这样就允许了多个组织共享一个 Hadoop 集群,每个组织可以分配到集群资源的一部分。队列可以进一步按层次划分,这样每个组织内的不同用户能够共享该组织队列所分配的资源。
在一个队列内,使用 FIFO 调度策略对应用进行调度,但是一个job可能使用不了整个队列的资源。然而如果这个队列中运行多个job,如果这个队列的资源够用,那么就分配给这些job。
(2)特性
-
分层队列- 支持队列分层以确保在允许其他队列使用空闲资源之前在组织的子队列之间共享资源,从而提供更多的控制和可预测性。
-
容量保证- 队列被分配了电网容量的一小部分,因为一定容量的资源将可供他们使用。所有提交到队列的应用程序都可以访问分配给队列的容量。管理员可以为分配给每个队列的容量配置软限制和可选的硬限制。
-
安全性- 每个队列都有严格的 ACL,用于控制哪些用户可以将应用程序提交到各个队列。此外,还有一些安全措施可确保用户无法查看和/或修改其他用户的应用程序。此外,还支持每个队列和系统管理员角色。
-
弹性- 可以将空闲资源分配给超出其容量的任何队列。当在未来某个时间点运行低于容量的队列对这些资源有需求时,随着在这些资源上调度的任务完成,它们将被分配给运行低于容量的队列上的应用程序(也支持抢占)。这确保了资源以可预测和弹性的方式可用于队列,从而防止集群中的人为资源孤岛,这有助于利用。
-
多租户- 提供全面的限制,以防止单个应用程序、用户和队列独占队列或整个集群的资源,以确保集群不会不堪重负。
-
可操作性
- 运行时配置 - 队列定义和属性(如容量、ACL)可以在运行时由管理员以安全的方式进行更改,以最大限度地减少对用户的干扰。此外,还为用户和管理员提供了一个控制台,以查看系统中各个队列的当前资源分配情况。管理员可以在运行时添加额外的队列,但不能在运行时删除队列。
-
排空应用程序 - 管理员可以在运行时停止队列,以确保在现有应用程序运行完成时,不能提交新的应用程序。如果队列处于
STOPPED
状态,则无法将新应用程序提交给它自己或其任何子队列。现有应用程序继续完成,因此可以优雅地排空队列。管理员还可以启动已停止的队列。 -
基于资源的调度- 支持资源密集型应用程序,其中应用程序可以选择指定比默认更高的资源要求,从而适应具有不同资源要求的应用程序。目前,内存是支持的资源要求。
-
基于用户或组的队列映射- 此功能允许用户根据用户或组将作业映射到特定队列。
(3)配置
官方文档:https://hadoop.apache.org/docs/r2.7.3/hadoop-yarn/hadoop-yarn-site/CapacityScheduler.html。
容量调度器是 Hadoop2.7 默认的调度器,在 yarn-site.xml 中通过以下参数配置:
<property>
<name>yarn.resourcemanager.scheduler.class</name>
<value>org.apache.hadoop.yarn.server.resourcemanager.capacity.CapacityScheduler</value>
</property>
"弹性队列":如果队列 queue 资源不够用了,而其他队列可能是有空闲可用的资源,那么容量调度器可能会将空余的资源分配给队列 queue 中的队列,这种特性称为 "弹性队列"。可以通过 yarn.scheduler.capacity.<queue-path>.maximum-capacity
参数来控制队列最大占用集群资源容量的比例。
示例:
<configuration>
<property>
<name>yarn.scheduler.capacity.root.queues</name>
<value>prod,dev</value>
</property>
<property>
<name>yarn.scheduler.capacity.root.dev.queues</name>
<value>eng,science</value>
</property>
<property>
<name>yarn.scheduler.capacity.root.prod.capacity</name>
<value>40</value>
</property>
<property>
<name>yarn.scheduler.capacity.root.dev.capacity</name>
<value>60</value>
</property>
<property>
<name>yarn.scheduler.capacity.root.dev.maximum-capacity</name>
<value>75</value>
</property>
<property>
<name>yarn.scheduler.capacity.root.dev.eng.capacity</name>
<value>50</value>
</property>
<property>
<name>yarn.scheduler.capacity.root.dev.science.capacity</name>
<value>50</value>
</property>
</configuration>
在 root 队列下定义两个队列:prod、dev。分别占用集群 40%、60% 的容量。
prod 没有设置最大容量限制,所以当 dev 队列空闲,prod 资源不足时,可能会占用整个集群 100% 的资源。
dev 队列设置了最大容量显示为 75%,也就是及时另外 25% 的资源空闲,dev 最大也只能占用整个集群 75% 的资源。dev 队列下还有子队列 eng、science,容量都是 dev 容量的 50%,没有为子队列设置最大容量,所以每个子队列最大都可能占用 dev 队列 100% 的资源,所以占用的整个集群的绝对资源大小为 30%~75%。
(4)演示
将上述示例的配置添加到 hadoop 配置文件目录下的 capacity-scheduler.xml 中,启动 Yarn,上控制台(http://192.168.190.111:8088/cluster/scheduler)可以看到配置的队列容量限制。
查看配置的每个队列的容量限制是否生效。
可以看到 prod 队列容量限制是 40%,最大容量限制不设置则默认为 100%。
dev 的两个子队列,占用 dev 队列的相对容量大小为 50%~100%,占用整个集群的绝对容量大小为 30%~100%。
默认的容量调度器配置
<configuration>
<property>
<name>yarn.scheduler.capacity.maximum-applications</name>
<value>10000</value>
<description>
Maximum number of applications that can be pending and running.
</description>
</property>
<property>
<name>yarn.scheduler.capacity.maximum-am-resource-percent</name>
<value>0.1</value>
<description>
Maximum percent of resources in the cluster which can be used to run
application masters i.e. controls number of concurrent running
applications.
</description>
</property>
<property>
<name>yarn.scheduler.capacity.resource-calculator</name>
<value>org.apache.hadoop.yarn.util.resource.DefaultResourceCalculator</value>
<description>
The ResourceCalculator implementation to be used to compare
Resources in the scheduler.
The default i.e. DefaultResourceCalculator only uses Memory while
DominantResourceCalculator uses dominant-resource to compare
multi-dimensional resources such as Memory, CPU etc.
</description>
</property>
<property>
<name>yarn.scheduler.capacity.root.queues</name>
<value>default</value>
<description>
The queues at the this level (root is the root queue).
</description>
</property>
<property>
<name>yarn.scheduler.capacity.root.default.capacity</name>
<value>100</value>
<description>Default queue target capacity.</description>
</property>
<property>
<name>yarn.scheduler.capacity.root.default.user-limit-factor</name>
<value>1</value>
<description>
Default queue user limit a percentage from 0.0 to 1.0.
</description>
</property>
<property>
<name>yarn.scheduler.capacity.root.default.maximum-capacity</name>
<value>100</value>
<description>
The maximum capacity of the default queue.
</description>
</property>
<property>
<name>yarn.scheduler.capacity.root.default.state</name>
<value>RUNNING</value>
<description>
The state of the default queue. State can be one of RUNNING or STOPPED.
</description>
</property>
<property>
<name>yarn.scheduler.capacity.root.default.acl_submit_applications</name>
<value>*</value>
<description>
The ACL of who can submit jobs to the default queue.
</description>
</property>
<property>
<name>yarn.scheduler.capacity.root.default.acl_administer_queue</name>
<value>*</value>
<description>
The ACL of who can administer jobs on the default queue.
</description>
</property>
<property>
<name>yarn.scheduler.capacity.node-locality-delay</name>
<value>40</value>
<description>
Number of missed scheduling opportunities after which the CapacityScheduler
attempts to schedule rack-local containers.
Typically this should be set to number of nodes in the cluster, By default is setting
approximately number of nodes in one rack which is 40.
</description>
</property>
<property>
<name>yarn.scheduler.capacity.queue-mappings</name>
<value></value>
<description>
A list of mappings that will be used to assign jobs to queues
The syntax for this list is [u|g]:[name]:[queue_name][,next mapping]*
Typically this list will be used to map users to queues,
for example, u:%user:%user maps all users to queues with the same name
as the user.
</description>
</property>
<property>
<name>yarn.scheduler.capacity.queue-mappings-override.enable</name>
<value>false</value>
<description>
If a queue mapping is present, will it override the value specified
by the user? This can be used by administrators to place jobs in queues
that are different than the one specified by the user.
The default is false.
</description>
</property>
</configuration>
3、公平调度器
(1)概述
公平调度器就是在队列内,所有的作业平等地分配资源,如果队列中只有一个作业,可以占用 100% 的资源;此时进来一个新的作业,则会调度到每个作业占用 50% 的资源,以此类推。
公平调度器不仅实现了队列内部的公平,还实现了队列之间的公平。
现在有两个队列 A、B。当 A 执行第一个作业,而 B 没有作业时,A可以占用整个集群的资源;当 A 作业还没完成,B 执行一个作业,则经过一段时间之后,两个作业各占用集群一半的资源;当 B 启动第二个作业时,队列内部的两个队列共享队列 B 的资源,经过一段时间,各占用集群 1/4 的资源,A 继续占用一半的集群资源。最终结果就是资源在用户之间实现了公平共享。
(2)配置
官方文档:https://hadoop.apache.org/docs/r2.7.3/hadoop-yarn/hadoop-yarn-site/FairScheduler.html。
启用公平调度器,在 yarn-site.xml 中添加以下配置:
<property>
<name>yarn.resourcemanager.scheduler.class</name>
<value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler</value>
</property>
<property>
<name>yarn.scheduler.fair.allocation.file</name>
<value>/root/training/hadoop-2.7.3/etc/hadoop/fair-scheduler.xml</value>
<description>配置资源池以及其属性配额的XML文件路径(本地路径)</description>
</property>
<property>
<name>yarn.scheduler.fair.preemption</name>
<value>true</value>
<description>开启资源抢占,default is True</description>
</property>
<property>
<name>yarn.scheduler.fair.user-as-default-queue</name>
<value>true</value>
<description>设置成true,当任务中未指定资源池的时候,将以用户名作为资源池名。这个配置就实现了根据用户名自动分配资源池。default is True</description>
</property>
<property>
<name>yarn.scheduler.fair.allow-undeclared-pools</name>
<value>false</value>
<description>是否允许创建未定义的资源池。如果设置成true,yarn将会自动创建任务中指定的未定义过的资源池。设置成false之后,任务中指定的未定义的资源池将无效,该任务会被分配到default资源池中。,default is True</description>
</property>
创建公平调度配置文件 fair-scheduler.xml,内容如下:
<?xml version="1.0"?>
<allocations>
<defaultQueueSchedulingPolicy>fair</defaultQueueSchedulingPolicy>
<queue name="prod">
<weight>40</weight>
<schedulingPolicy>fifo</schedulingPolicy>
</queue>
<queue name="dev">
<weight>60</weight>
<queue name="eng"/>
<queue name="science"/>
</queue>
<!-- 调度规则列表 -->
<queuePlacementPolicy>
<rule name="specified" create="false" />
<rule name="primaryGroup" create="false" />
<rule name="default" queue="dev.eng"/>
</queuePlacementPolicy>
</allocations>
公平调度器使用一个规则列表来确定应用应该放到哪个队列,可配置的值和含义如下:
-
specified:将应用放进指明的队列中。
# 示例配置 <rule name="specified" create="false" /> # 提交任务时指定队列为 "dev",则任务会放到队列 dev,若队列不存在,则匹配下一条规则。
-
user:将应用放进提交任务的用户名的队列中,用户名中的句点将替换为 “dot”,比如用户 “first.last” 的队列名称为 “first_dot_last”。
# 示例配置 <rule name="user" create="false" /> # 提交任务时的用户名为 "prod",则任务会放到队列 prod 中,若队列不存在,则匹配下一条规则。 # 注意:user规则匹配的必须是跟队列下的用户队列,比如 prod,如果是 eng 则不行
-
primaryGroup:将应用放进以用户所属的主要组命名的队列中,比如用户所属的组为 “one.two”,则队列名为 “one_dot_two”。
-
secondaryGroupExistingQueue:将应用放进以用户所属的次要组命名的队列中,比如用户所属的组为 “one.two”,则队列名为 “one_dot_two”。
-
nestedUserQueue :应用程序被放入一个队列中,用户名在嵌套规则建议的队列下。这类似于“user”规则,区别在于“nestedUserQueue”规则,用户队列可以在任何父队列下创建,而“user”规则只能在根队列下创建用户队列。请注意,仅当嵌套规则返回父队列时才会应用 nestedUserQueue 规则。可以通过将队列的“类型”属性设置为“父”或通过在该队列下配置至少一个叶子来配置父队列,使其成为父母。请参阅示例用例的示例分配。
# 示例配置 <rule name="nestedUserQueue"> <rule name="secondaryGroupExistingQueue" create="false" /> </rule> # 提交任务的用户名所属的次要组为 eng,而 eng 队列是被嵌套在 dev 队列中的,则可以提交到队列 root.dev.eng 中
-
default:应用程序被放置在默认规则的“队列”属性中指定的队列中。如果未指定“队列”属性,则应用程序将被放入“root.default”队列。
-
reject:应用程序被拒绝。
(3)演示
将上述的配置配置好,启用 Yarn,web console 上看到的调度器如下: