多图讲解YARN容量调度中的资源抢占

在前面的文章中讲过容量调度中队列的容量配置、容量调度中的优先级调度。

实际使用场景中可能会出现这么几种情况:

  • 某个队列中的正在运行的任务所使用的资源超过了队列配置的容量,那么提交到其他队列的任务可能因为资源不够而无法运行(只能等到运行的任务结束释放资源)

  • 同样,多个正在运行的低优先级任务占用了大量集群资源,导致新提交的高优先级任务无法运行。

对于这些场景就可以通过配置资源抢占来解决。保证队列的最低容量得到保证、或者高优先级的任务优先运行。

本文就来聊聊容量调度中的资源抢占。

【队列间的抢占】


容量调度中的资源抢占,最通用的方式就是在多个队列之间进行资源的抢占,保证每个队列的最小资源(队列的capacity配置)得以满足。

在rm的配置文件yarn-site.xml中增加如下配置,就可以开启队列间的抢占:

<property>
    <name>yarn.resourcemanager.scheduler.monitor.enable</name>
    <value>true</value>
</property>

配置完成后(需重启rm),就可以看到队列的抢占属性从disable变成enabled了。

接下来,我们就实际测试下抢占的效果。

测试的队列配置情况如下所示:


queue_test
spurs
default
capacity
10
40
50
maxCapacity
100
100
100

注:集群总的资源为18GB内存,为了任务可以正确提交和分配资源,临时将队列的am资源占比调高了。

测试步骤:

  • 使用三个用户向queue_test队列提交3个任务,即每个用户提交一个任务(为了保证每个任务都能分配到资源运行),任务包含两个container,一共申请5GB内存(3个任务共占用15GB资源)。

  • 等任务都成功分配资源运行后,再向其他队列(例如default队列)提交一个任务(共申请5GB内存),观察是否会触发抢占。

测试情况如下所示:

queue_test队列中已经有3个任务,并且都成功分配资源并运行了:

新提交任务到default队列后,am已经分配资源并成功运行,但此时由于集群资源不够,导致任务无法申请到资源。

等待一段时间后,观察到新提交的任务从queue_test队列抢占到了3GB的资源,而queue_test队列中其中一个任务运行结束释放了资源。

从rm的界面提供的信息中也可以证实,确实发生了抢占。

到此,可以看到抢占生效了。此后如果继续往default队列或spurs队列提交任务,还会继续从queue_test中抢占资源。

【队列内的抢占】


除了可以配置队列间的抢占,还可以配置队列内的任务是否可以抢占。也就是说不仅可以抢占其他队列的资源,还可以抢占任务自身所在队列的资源。

队列内的抢占有两种方式:一种是按任务的优先级来,即高优先级的任务处于待分配状态后,将从低优先级任务抢占资源;另一种是按用户资源来划分,即队列内多个用户提交的任务,从占用资源最多的那个用户进行抢占,其本质上是保证每个用户的资源使用趋于平等。这里就举例介绍下按任务优先级来抢占。

和队列间的抢占类似,也需要在yarn-site.xml中增加如下配置项才能启用队列内的抢占。

<property>
    <name>yarn.resourcemanager.monitor.capacity.preemption.infra-queue-preemption.enabled</name>
    <value>true</value>
</property>


<property>
    <name>yarn.resourcemanager.monitor.capacity.preemption.intra-queue-preemption.preemption-order-policy</name>
    <value>priority_first</value>
</property>

配置完成后(同样需重启rm),就可以看到对应属性从disable变成enabled了。

接下来仍旧是进行测试看看实际效果,测试方法和队列间的抢占基本一致,不过有如下两点不同:

  • 最后一次任务提交仍旧是提交到queue_test队列中(队列间的抢占是提交到default队列)

  • 每个任务提交时都指定了优先级,最后一个提交的任务的优先级比之前的都要高

测试情况如下所示:

queue_test队列中已经有3个任务,并且都成功分配了资源和运行,再次往队列提交任务,该任务的优先级比之前的都要高,任务的am已经分配的资源,但因集群资源不满足不会为任务分配资源(这里截图早了点,任务分配资源后会运行,其状态会变为RUNNING)

一段时间后,发生了队列间的抢占,优先级最低的任务其中一个container结束并释放了资源。

此后,再次向队列提交一个任务(优先级比正在运行的都要低),发现很长一段时间都没有发生抢占。这足以说明只有高优先级的任务可以抢占低优先级任务的资源,反过来却不行。

【抢占原理】


首先,只有使用的调度器实现了PreemptableResourceScheduler接口,并且启用了抢占;rm才会真正使用抢占这个功能。

而资源抢占是通过第三方策略触发的,这些策略通常被实现成一些可插拔式的组件类(实现指定SchedulingEditPolicy接口)。yarn提供了默认的实现类,当然,也可以通过参数配置进行指定。

rm会启动一个监测线程,在该线程中定期遍历这些策略,并调用具体实例的接口实现方法,决定是否进行抢占,抢占哪些container的资源。

资源抢占的整个过程可以概括为如下步骤:

  1. 监测线程根据队列当前已使用资源大小、实际配置使用资源大小、是否允许抢占等因素,重新计算出每个队列最终分配的资源大小,需要抢占的资源大小,以及哪些container的资源将被抢占。然后将需要抢占的资源通过以事件机制的方式通知rm。

  2. rm处理对应事件,并标记待抢占的container。

  3. rm收到任务ApplicationMaster(简称AM)的心跳信息后,通过心跳应答将待释放的资源总量和待抢占container列表返回给AM。AM收到心跳后,可选择如下操作方式:

    a. 杀死这些container

    b. 杀死其他container以凑够待释放资源的总量

    c. 不做任何处理,因为可能有其他container结束自行释放资源,或者由rm选择杀死container

  4. 监测线程对应定时器到期后,发现AM未按照指定列表kill待抢占的container,则将发送包含这些container列表的kill事件给rm。

  5. rm收到消息后标注这些container将要被kill。并在NodeManager的心跳应答中,告知NodeManager需要将指定的container进行kill,以释放资源。

这就是整个资源抢占的处理逻辑,个人认为,最核心的步骤在于资源分配的重新计算和标注哪些container的资源将要被抢占。

【其他配置项】


除了上面队列内抢占、队列间抢占提到的配置外,还有如下相关配置项:

<!-- 设置为观察模式,true表示不进行真正的抢占动作 -->
<property>
    <name>yarn.resourcemanager.monitor.capacity.preemption.observe_only</name>
    <value>true</value>
</property>


<!-- 资源抢占策略 -->
<property>
    <name>yarn.resourcemanager.scheduler.monitor.policies</name>
    <value>org.apache.hadoop.yarn.server.resourcemanager.monitor.capacity.ProportionalCapacityPreemptionPolicy</value>
</property>


<!-- 抢占监测d额时间间隔 单位: 毫秒 -->
<property>
    <name>yarn.resourcemanager.monitor.preemption.monitoring_interval</name>
    <value>3000</value>
</property>


<!-- kill container之前的等待时长 单位: 毫秒 -->
<property>
    <name>yarn.resourcemanager.monitor.capacity.preemption.max_wait_before_kill</name>
    <value>15000</value>
</property>


<!-- 每次监测触发抢占的资源的最大值(集群资源的百分比) -->
<property>
    <name>yarn.resourcemanager.monitor.capacity.preemption.total_preemption_per_round</name>
    <value>0.1</value>
</property>


<!-- 队列(超额)使用资源可忽略不进行抢占的百分比 -->
<!-- 即队列当前已使用资源超过了capacity,但还未超过(1+0.1)*capacity,则对该队列进行抢占 -->
<property>
    <name>yarn.resourcemanager.monitor.capacity.preemption.max_ignored_over_capacity</name>
    <value>0.1</value>
</property>

此外,还可以单独对队列进行设置,例如指定某个队列不允许抢占。

<!-- 队列禁止抢占 -->
<property>
    <name>yarn.scheduler.capacity.$QUEUE_PATH.disable_preemption</name>
    <value>true</value>
</property>


<!-- 禁止队列内抢占 -->
<property>
    <name>yarn.scheduler.capacity.$QUEUE_PATH.intra-queue-preemption.disable_preemption</name>
    <value>true</value>
</property>

【其他问题整理】


  • 父队列设置了抢占,子队列没有任何配置时,默认情况是怎样的?

    A:子队列没有任何配置时,默认继承父队列的抢占属性。

  • 父队列设置不允许抢占,子队列设置允许抢占,该子队列是否还允许抢占?

    A:以子队列的配置为准,因为最终任务都是提交到子队列中,也就是资源的实际使用都是在子队列中。因此只要子队列配置了允许抢占,即使父队列设置了不允许抢占,实际还是会发生抢占的。

  • 每次触发抢占时的资源调整上限仍旧无法满足其他队列待分配需求时会怎样?

    A:此时抢占不会生效。

    例如上面的例子中,集群总资源为18GB,假设每次抢占的最大资源大小为0.1。

    即每次最多抢占18*1024*1024*0.1=1843.2MB,而单个container需要3072MB的资源,因此,即便全部抢占过来也无法满足单个container分配所需的资源,所以实际上不会发生抢占的逻辑。

  • 队列内的抢占和队列间的抢占是否可以分别设置?

    A:可以分别设置,互不影响。

【总结】


小结一下,本文讲述了容量调度中的资源抢占,包括队列间的资源抢占和队列内的资源抢占的配置使用,对抢占的原理、相关配置、使用上一些场景的FAQ也简单进行了简要说明。

好了,本文就介绍到这里,原创不易,点赞,在看,分享是最好的支持, 谢谢~

推荐相关阅读:


YARN——正确理解容量调度的capacity参数

YARN——容量调度中决定用户资源的几个参数

YARN——队列内的优先级调

  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值