本文档介绍了机会容器执行的概念,并讨论了机会容器的分配和执行方式。
我们首先简要概述机会容器,包括用户如何启用此功能以及使用此类容器运行示例作业。
与仅在存在未分配资源时才在节点中计划的现有 YARN 容器不同,即使无法立即在该节点上启动执行,也可以将机会容器分派到 NM。在这种情况下,机会容器将在该 NM 中排队,直到资源可用。机会容器执行的主要目标是提高集群资源利用率,从而增加任务吞吐量。对于包含相对较短任务(以秒为单位)的工作负载,资源利用率和任务吞吐量的提高更为明显。
要启用机会容器分配,以下两个属性必须存在于 conf/yarn-site.xml 中
属性 | 说明 | 默认值 |
---|---|---|
yarn.resourcemanager.opportunistic-container-allocation.enabled |
启用机会容器分配。 | false |
yarn.nodemanager.opportunistic-containers-max-queue-length |
确定可在 NM 中排队的机会容器的最大数量。 | 0 |
上面的第一个参数必须设置为 true
。第二个参数必须设置为正值,以允许在 NM 中对机会容器排队。值为 10
可用于开始尝试使用机会容器。最佳值取决于作业特性、集群配置和目标利用率。
默认情况下,机会容器的分配是通过 RM 集中执行的。但是,用户可以选择启用机会容器的分布式分配,这可以进一步提高短任务的分配延迟。通过将以下参数设置为 true
,可以启用分布式计划(请注意,非机会容器将继续通过 RM 计划)
属性 | 说明 | 默认值 |
---|---|---|
yarn.nodemanager.distributed-scheduling.enabled |
启用分布式计划。 | false |
为了向已启用 AMRMProxy 的集群提交作业,必须为提交作业的客户端创建一组单独的配置。在这些配置中,conf/yarn-site.xml 应具有以下其他配置
属性 | 值 | 说明 |
---|---|---|
yarn.resourcemanager.scheduler.address |
localhost:8049 |
将作业重定向到节点管理器的 AMRMProxy 端口。 |
可以使用以下命令运行示例 pi map-reduce 作业,使用机会容器执行 40% 的映射器
$ hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar pi -Dmapreduce.job.num-opportunistic-maps-percent="40" 50 100
通过更改上述命令中 mapreduce.job.num-opportunistic-maps-percent
的值,我们可以指定可通过机会容器执行的映射器的百分比。
另一个示例作业是分布式 shell,它允许我们在容器组上运行给定的 shell 命令。以下命令可用于在 10 个机会容器中运行sleep 10
命令
$ yarn org.apache.hadoop.yarn.applications.distributedshell.Client -jar share/hadoop/yarn/hadoop-yarn-applications-distributedshell-3.3.6.jar.jar -shell_command sleep -shell_args 10 -num_containers 10 -container_type OPPORTUNISTIC
通过在上述命令中将container_type
的值更改为OPPORTUNISTIC
或GUARANTEED
,我们可以指定在机会容器或保证容器中运行的任务。默认类型为GUARANTEED
。通过向上述命令添加标志-promote_opportunistic_after_start
,应用程序主机会尝试在启动所有机会容器后将其全部提升为保证容器。通过向上述命令添加标志‘-enforce_execution_type’,调度程序将遵守容器的执行类型。
启用机会容器分配后,可以在 Web UI 的节点页面(rm-address:8088/cluster/nodes
)中观察到以下新列
单击节点上正在运行的特定容器时,还会显示容器的执行类型。
在本文档的其余部分中,我们将提供机会容器的深入描述,包括有关其分配和执行的详细信息。
YARN 中的现有调度程序(公平调度程序和容量调度程序)仅在调度容器时该节点有未分配资源的情况下才将容器分配给节点。这种保证执行类型具有以下优点:一旦 AM 将容器调度到节点,容器执行将立即开始,因为可以保证有可用资源。此外,除非违反公平性或容量约束,否则可以保证容器运行到完成而不会被抢占。
虽然此设计提供了更可预测的任务执行,但它有两个主要缺点,可能导致集群资源利用率不佳
为了缓解上述问题,除了现有容器(我们在此之后称之为保证容器)之外,我们引入了机会性容器的概念。即使在调度时没有可用的(未分配的)资源,也可以将机会性容器调度到 NM。在这种情况下,机会性容器将在 NM 处排队,等待资源可用以开始执行。机会性容器的优先级低于保证容器,这意味着可以抢占它们以使保证容器开始执行。因此,它们可用于提高集群资源利用率,而不会影响现有保证容器的执行。
机会性容器的另一个优点是,它们引入了NM 上的执行优先级的概念。例如,不需要严格执行保证的低优先级作业可以使用机会性容器或其任务的容器执行类型组合。
我们引入了两种分配机会性容器的方法:集中式和分布式。在集中式调度中,通过 YARN RM 分配机会性容器,而在分布式调度中,通过驻留在每个 NM 上的本地调度程序分配机会性容器。集中式分配允许做出更高质量的放置决策,并为跨应用程序实施更复杂的共享策略(例如,公平性)。另一方面,分布式调度可以提供更快的容器分配,这对于短任务很有用,因为它避免了往返 RM 的过程。在这两种情况下,保证容器的调度保持不变,并通过 YARN RM(使用现有的 Fair 或 Capacity Scheduler)进行。
请注意,在当前实现中,我们根据已分配(而非已利用)资源分配容器。因此,我们解决了上面提到的“反馈延迟”问题,但没有解决“已分配与已利用资源”问题。目前正在进行的工作 (YARN-1011
) 使用机会性容器来解决后一个问题。
下面,我们将更详细地描述容器执行类型,以及机会容器的执行(包括在 NM 中对容器进行排队)和分配。然后,我们将讨论如何通过一些高级配置参数对机会容器进行微调。最后,我们将讨论未来工作的开放式问题。
我们引入了以下两种类型的容器
当 AM 向 RM 提交其资源请求时,它为每个容器指定类型(默认值为保证),确定容器的分配方式。随后,当 AM 在 NM 中启动容器时,其类型决定了 NM 将如何执行它。
当一个容器到达 NM 时,其执行由 NM 中的可用资源和容器类型决定。保证容器立即开始执行,如果需要,NM 将终止正在运行的机会容器,以确保有足够的资源供保证容器启动。另一方面,如果在机会容器到达 NM 时没有可用的资源来启动其执行,则它们可以在 NM 中排队。为了实现这一点,我们通过允许在每个节点排队容器来扩展 NM。NM 监视本地资源,当有足够的可用资源时,它将启动队列首部的机会容器的执行。
特别是,当一个容器到达 NM 时,将执行本地化(即,下载所有必需的资源),然后容器移动到SCHEDULED
状态,在该状态下容器排队,等待其执行开始
在下面的未来工作项目中,我们讨论了对任务执行(队列重新排序)进行优先级排序和杀死机会容器为保证容器腾出空间的不同方法。
如上所述,我们提供了集中式和分布式两种分配机会容器的方式,我们将在下面进行描述。
我们在 RM 中引入了一项新服务,即 OpportunisticContainerAllocatorAMService
,它扩展了 ApplicationMasterService
。当启用集中式机会分配时,AM 的资源请求由 OpportunisticContainerAllocatorAMService
在 RM 端提供服务,它将这些请求拆分为两组资源请求
ApplicationMasterService
,随后由 Fair 或 Capacity Scheduler 处理。OpportunisticContainerAllocator
处理,它执行机会容器到节点的调度。OpportunisticContainerAllocator
维护一个列表,其中包含集群中每时每刻的负载最低的节点,并以循环方式向它们分配容器。请注意,在当前实现中,我们故意不考虑节点局部性约束。由于机会容器(与保证容器不同)可能会在其执行开始前在 NM 的队列中等待,因此将其分配到负载较低的节点(即队列延迟较小)比遵守其局部性约束更为重要。此外,我们目前不考虑机会容器的共享(公平性/容量)约束。如果需要,将来可以添加对局部性和共享约束的支持。
为了启用机会容器的分布式调度,我们在每个 NM 中引入了一项新服务,称为 AMRMProxyService
。AMRMProxyService
实现了 ApplicationMasterService
协议,并充当在该节点上运行的 AM 与 RM 之间的代理。当启用 AMRMProxyService
(通过参数)时,我们将强制在特定节点上运行的所有 AM 与同一节点的 AMRMProxyService
通信,而不是直接访问 RM。此外,为了确保 AM 不会直接与 RM 通信,当新的 AM 初始化时,我们将用由 AMRMProxyService
签名的令牌替换其 AMRMToken
。
可以向 AMRMProxyService
注册一系列拦截器。其中一个拦截器是 DistributedScheduler
,它负责以分布式方式分配机会容器,而无需联系 RM。此模块化设计使 AMRMProxyService
在其他场景中也很有用,例如 YARN 联合(YARN-2915
)或限制行为不当的 AM,只需在拦截器链中添加其他拦截器即可启用这些功能。
当启用分布式机会调度时,每个 AM 会将其资源请求发送到在同一节点上运行的 AMRMProxyService
。AMRMProxyService
将资源请求分成两组
AMRMProxyService
只是充当 AM 与 RM 之间的代理,并且容器分配保持不变(使用公平或容量调度程序)。DistributedScheduler
处理。具体来说,DistributedScheduler
维护一个列表,其中包含集群中最少负载的节点,并以循环方式向它们分配容器。RM 通过 NM-RM 心跳定期向 DistributedScheduler
报告最少负载的节点。上述过程类似于上面描述的集中式机会调度中 OpportunisticContainerAllocatorAMService
执行的过程。主要区别在于,在分布式情况下,将请求拆分为已保证和机会请求在节点本地发生,并且只有已保证的请求会转发到 RM,而机会请求则在不联系 RM 的情况下处理。
每个 NM 会通过 NM-RM 心跳定期向 RM 报告正在运行的已保证和机会容器的数量,以及排队的机会容器的数量。RM 从所有节点收集此信息,并确定最少负载的节点。
在机会容器的集中分配的情况下,此信息可立即获得,因为分配是集中进行的。在分布式调度的情况下,最少加载节点的列表通过 RM 对 NM 的心跳响应传播到所有 NM(因此对DistributedSchedulers
可用)。发送到 NM 的最少加载节点数是可配置的。
目前,我们仅考虑每个节点上排队的机会容器的数量,以估计如果将机会容器发送到该节点,它将需要等待的时间,从而确定最少加载的节点。如果 AM 向我们提供了有关估计任务持续时间的信息,我们可以考虑这些信息,以便更好地估计队列等待时间。
有时可能会对机会容器做出较差的放置选择(由于队列长度估计过时),这会导致节点之间的负载不平衡。在集群负载较高的情况下,这个问题更为明显,并且在分布式调度的情况下也是如此(多个DistributedSchedulers
可能会将容器放置在同一 NM 上,因为它们不会相互协调)。为了解决 NM 队列之间的这种负载不平衡,我们执行负载分流以动态地在 NM 之间重新平衡负载。具体来说,在 RM 中汇总每个 NM 发布的队列时间估计时,我们构建一个分布并找到 NM 队列长度的目标最大值(基于分布的平均值和标准差)。然后,RM 通过心跳响应将此值传播到各个 NM。随后,NM 使用此信息,如果节点的队列长度高于阈值,则会放弃机会容器以满足此最大值。这迫使关联的各个 AM 在其他地方重新调度这些容器。
启用机会容器分配并在集中式和分布式分配之间进行选择的主要属性已在本文档开头的快速指南中进行了描述。这里我们介绍更高级的配置。请注意,在大多数情况下,使用这些参数的默认值就足够了。以下所有参数都必须在conf/yarn-site.xml文件中定义。
为了确定在调度机会容器时将使用的最少加载节点的数量以及刷新此列表的频率,我们使用以下参数
属性 | 说明 | 默认值 |
---|---|---|
yarn.resourcemanager.opportunistic-container-allocation.nodes-used |
机会容器分配器在容器分配期间调度容器时要使用的最少加载节点数。较高的值可以改善大型集群中的负载平衡。 | 10 |
yarn.resourcemanager.nm-container-queuing.sorting-nodes-interval-ms |
计算最少加载节点的频率。 | 1000 |
如上文 节点负载重新平衡 部分所述,RM 会定期收集所有 NM 队列长度,并计算其平均值 (avg
) 和标准差 (stdev
),以及值 avg + k*stdev
(其中 k
为浮点数)。此值通过 NM-RM 心跳传播到所有 NM,只要其当前队列长度介于 queue_min_length
和 queue_max_length
值之间(这些值分别用于避免从非常短的队列中取消排队任务和积极地从长队列中取消排队任务),所有 NM 都应尊重该值。可以按如下方式指定参数 k
、queue_min_length
和 queue_max_length
属性 | 说明 | 默认值 |
---|---|---|
yarn.resourcemanager.nm-container-queuing.queue-limit-stdev |
k 参数。 |
1.0f |
yarn.resourcemanager.nm-container-queuing.min-queue-length |
queue_min_length 参数。 |
5 |
yarn.resourcemanager.nm-container-queuing.max-queue-length |
queue_max_length 参数。 |
15 |
最后,如果使用分布式调度,还可以通过另外两个属性进一步调整 AMRMProxyService
属性 | 说明 | 默认值 |
---|---|---|
yarn.nodemanager.amrmproxy.address |
AMRMProxyService 绑定的地址/端口。 |
0.0.0.0:8049 |
yarn.nodemanager.amrmproxy.client.thread-count |
每个 NM 中用于为不同作业向 AMRMProxyService 提供拦截器注册服务的线程数。 |
3 |
在此,我们描述了多种可用于扩展/增强机会性容器的分配和执行的方法。我们还提供了跟踪每个事项的 JIRA。
YARN-1011
)。如前所述,为了进一步提高集群资源利用率,我们可以根据实际使用的资源而不是分配的资源来调度容器。在超额提交资源时,如果已运行容器的已用资源增加,则存在耗尽资源的风险。因此,机会性执行应用于分配超出节点容量的容器。通过这种方式,我们可以选择机会性容器以回收资源。YARN-5886
)。我们可以采用重新排序策略,动态确定接下来要执行哪个机会性容器,而不是按 FIFO 顺序执行排队容器。例如,我们可以优先考虑预计运行时间短或属于接近完成的应用程序的容器。YARN-5887
)。如上所述,当我们需要释放资源以保证容器开始执行时,我们会按到达顺序反向终止机会容器(首先终止最近启动的容器)。这可能并不总是正确的决定。例如,我们可能希望最大程度地减少终止的容器数量,或避免终止即将完成的作业的容器。YARN-5292
):目前,我们在资源争用时终止机会容器,为保证容器腾出空间。在繁忙的集群中,这会降低有效的集群利用率:每当我们终止一个正在运行的机会容器时,都必须重新启动它,因此会丢失工作。为此,我们可以暂停正在运行的机会容器。请注意,这将需要容器执行程序(例如,所使用的容器技术)和应用程序的支持。YARN-5085
)。在某些情况下,在容器执行期间更改其执行类型可能是有益的。例如,应用程序可能会将容器提交为机会容器,并在其执行开始时,它可以请求将其提升为保证容器,以避免被终止。