这是一篇译文,原文地址

主要介绍了Capacity Scheduler相关的内容。

Capacity Scheduler在资源分配时主要考虑两个主要属性:用户名和应用程序ID。在指定最小用户百分比和用户限制因素的队列中,用户之间共享资源时,关注的是用户名本身。在队列中,应用程序获取资源分配由叶子队列排序策略驱动:FIFO或FAIR,它只关心应用程序而不关心哪个用户正在运行它。在FIFO中,资源首先被分配给队列中最先提交的应用程序,并且只有当它不再需要任何请求时,下一个应用程序才会获得分配。对于使用最少资源的FAIR应用程序,首先询问是否存在待处理的分配,如果存在则分配,则如果不存在,则检查具有最少资源的下一个应用程序;这有助于与通常使用它们的应用程序平均共享队列。

容量和分层设计

​YARN对其可分配的资源定义了最小分配量和最大分配量,目前这些资源包括内存和内核。可调度资源是由NodeManage提供的,来自所有节点的资源聚合为可被Capacity调度的总资源,总资源组成root队列。

​Capacity调度的基础设置是如何布置队列以及为它们分配资源。队列以分层设计布局,最顶层的父节点是集群队列的“根”,可以从根分配子队列,或者可以在自己队列的基础上再次分配子队列。资源被分配给这些队列,作为层次结构中父级资源的最小和最大百分比。 最小容量是如果群集上的所有队列都已运行,并且集群总资源已用完,此时各队列应该可以使用的资源量。 最大容量是类似弹性的容量,允许队列利用不用于填充其他队列中的最小容量需求的资源。
队列设计

最低用户百分比和用户限制因素

​最小用户百分比和用户限制因子是控制如何将正在使用的队列中的资源分配给他们的方法。 最小用户百分比是单个用户在请求时应访问的最小资源量百分比,是一个软限制。 例如,10%的最小用户百分比意味着10个用户将获得10%; 这个值不是硬性规定的,因为如果其中一个用户要求更少,我们可能会将更多用户放入队列。

​用户限制因子是一种控制单个用户可以使用的最大资源量的方法。 用户限制因子设置为队列最小容量的倍数。如果用户限制因子为1表示用户可以使用队列的整个最小容量, 如果用户限制因子大于1,则用户可能会增长到最大容量,如果该值设置为小于1,例如0.5,则用户只能获得队列最小容量的一半。 如果您希望用户能够增长到队列设置的最大容量,则大于1的值将允许用户多次超过最小容量。
限制因子

​最初可能不直观的常见设计点是按工作负载而不是应用程序创建队列,然后使用用户限制因子来防止单个用户通过使用小于1.0的值单独接管队列。 此模型支持更简单的操作,不允许通过为每个LoB创建一个队列来创建队列,而是通过按工作负载创建队列来创建可预测的队列行为。 一旦群集完全使用并且应用程序排队等待运行时,较低的用户限制因素将是控制租户之间资源共享的关键。

​最初,由于使用较小的用户限制来限制用户资源,这可能无法在其Hadoop平台旅程开始时提供群集利用率,有许多方法,但需要考虑的是,最初允许单个租户接管一个小集群(比如10个节点),这可能是合理的,但是当扩展平台占用空间时,降低用户限制因子,以便每个租户保留与添加新节点之前相同数量的可分配资源。 这可以让用户保持最初的体验,但是无法接管整个集群,因为随着整个群集的增长,新用户可以轻松获得分配的容量,同时不会降低原始用户的体验。

队列设计

​队列设计时需要考虑如下相关场景:

  • 即时查询
    主要是些临时统计需求,任务的出现没有规律,主要集中在工作时间,由用户主动触发
  • 紧急需求
    主要用于处理一些紧急需求,比如紧急补数
  • 机器学习
    这部分需求是需要大量的资源并且可能会长时间占用资源,但是紧急度和运行时长要求不是很高
  • 实时计算
    这部分需求是长时间运行并且没有明确的结束标识,对实时要求较高,不能容忍延迟,需要严格保证资源不被其它需求抢占
  • 批处理
    是指一些常规数据ETL,主要关注任务的吞吐量而不是当个任务是否延迟

​队列中资源使用模型可以认为是一些containers像常量那样一直存在和新启动的。 这种使用模型对于具有良好行为的集群非常重要,因为队列可以快速重新平衡到其最小容量,并公平地平衡其用户之间的队列容量。 不好的使用模式是长期存在的容器,资源一直在被自己使用并且永远不会释放,这样它可以阻止资源的适当重新平衡,在某些情况下它可以完全阻止其他应用程序启动或其他队列恢复其最小容量。 当不使用抢占时,如果队列扩容弹性空间但从不释放其容器,则弹性空间永远不会被返回给其他队列来保证他们的最小容量。 如果在队列中运行的应用程序具有长期存在容器,请特别注意并考虑使用诸如用户限制因子,抢占或甚至没有弹性容量的专用队列等功能来减轻其对其他用户的影响的方法。

CAPACITY调度特性

CPU调度

​默认情况下Capacity调度不支持CPU调度。如果开启CPU调度的话,可能会降低批处理任务的吞吐量。支持CPU调度的算法是Dominant Resource Fairness (DRF)。

​CPU调度主要包含两部分:

  1. 分配和放置
  2. 强制限制

​只要开启CPU调度即可解决分配和放置问题,调度器会使用DRF算法和VCores Node Manager的汇报结果。 这解决了一些新问题,例如用户使用1个或2个executors来调度他的Spark应用程序,但是每个executor分配8个core,然后由于可用内存过多而继续向这个节点分配任务,由于core的限制影响这些任务的运行。 通过启用简单的CPU调度,如果正在使用所有core,则不再将其他任务分配到该节点上,并找到群集中其它节点来放置任务。

​至于强制限制可以通过Cgroup来解决。Cgroup可以让YARN确保分配给某个container的vcore个数不会超过请求的数量。可以启用未使用时共享vcores,也可以仅严格执行已安排的内容。 节点管理器还可以配置为服务器上允许所有任务总和的最大CPU使用量,这样可以保证操作系统的功能正常使用core。

抢占

​当应用程序在队列A中使用了弹性容量,此时另一个队列B提交了一个应用程序,队列B的资源被队列A的任务抢先使用中,此时正常情况下队列B中的应用程序必须等待队列A中的任务释放自由之后才能运行。如果开启抢占功能,就可以立马回收其他队列中的资源,以便满足队列的最小容量需求。抢占功能并不会彻底杀死一个应用程序,优先抢占map任务的资源,最后才是reduce的资源,因为被kill掉的任务必须重新运行,reduce比map要重复更多的工作。 从排序的角度来看,抢占时优先考虑最新的应用程序和大多数超额订阅的应用程序。

​抢占有些功能并不能像用户预期的那样发挥作用。最常见的行为是认为队列可以在其自身内部抢占以平衡所有用户的资源。 这种假设是错误的,因为抢占只能在队列之间,在队列中用户之间的资源平衡需要查看其他控制方式,例如用户限制因子和队列FIFO / FAIR策略。

队列的排序策略

​Capacity调度目前支持两种排序策略:FIFO和FAIR。队列默认使用的是FIFO,但是这不是用户所希望的排序策略。在叶子队列中配置FIFO或者FAIR,可以在应用程序的吞吐量和共享公平之间合理选择。关于排序策略的一个重要事情是,排序是针对队列中的应用程序级别,而不关心哪个用户拥有应用程序。

​FIFO策略是按照应用程序提交的顺序来排序的。如果应用程序有未完成的资源请求,则会根据先到先得的原则分配。 这样做的结果是,如果应用程序有足够的未完成请求,它们将在完成之前多次请求,可能会使用整个队列,它们将阻止其他应用程序启动。 用户甚至可以使用自己的应用程序占满整个集群资源而阻止其它应用程序提交,正是这种行为对用户来说是最不可接受的。

​使用FAIR策略能很容易的解决上面的问题。在子队列上使用FAIR策略时,应首先对应用程序已占用的资源量进行评估,资源使用最少的应用程序会优先分配请求。 这样,进入队列的没有进行资源分配的新应用程序将首先进行资源分配。 一旦队列中的所有应用程序都得到资源,这样就在用户之间得到平衡。

​最重要的是,只有在队列中有良好的资源交互时才会出现此行为。由于队列内部不存在抢占资源,因此无法在队列中强制重新分配资源,而且FAIR排序策略仅涉及新资源分配而非当前已使用资源的再分配;这是什么意思? 如果队列中资源被当前任务使用且任务永远不会完成或长时间运行而不允许在队列中发生容器结束,那么将保留资源并仍然阻止应用程序执行。

默认队列映射

​通常是在配置文件中指定用户提交的队列,Capacity调度可以使用默认队列映射通过用户名或者用户组将用户路由到指定的队列。但是需要注意的是默认队列的路由规则是哪个规则先满足就使用那个规则进行路由。也就是说如果组用户映射规则在用户映射规则前面,则先使用组用户映射规则对用户进行路由。

优先级

​当资源在队列之间分配时,具有最低相对容量的队列首先获得资源。如果你想让某个队列总是比其它队列优先获得资源,那么最简单的方法就是提高这个队列的优先级。

​在这两个队列之上提供了如何使用未经队列优先级修改的相对容量的示例。在这种情况下,即使队列A小于队列B并且队列B使用更多绝对资源,也会选择继续接收它们,因为它的相对容量低于队列A的相对容量。如果所需行为需要队列A始终接收资源分配首先,队列优先级应该增加到高于队列B的优先级。当分配优先级时,更高的值表示更高的优先级。