Kubernetes拓扑管理器移至Beta-对齐!

作者: 凯文·克鲁斯(NVIDIA),维克多·皮卡德(Red Hat),科纳·诺兰(Conor Nolan)(英特尔)

这篇博客文章介绍了 TopologyManager,是1.18版中Kubernetes的Beta版功能。的 TopologyManager 该功能可实现CPU和外围设备(例如SR-IOV VF和GPU)的NUMA对齐,从而使您的工作负载在针对低延迟而优化的环境中运行。

在介绍之前 TopologyManager,CPU和设备管理器将相互独立地做出资源分配决策。这可能会导致在多套接字系统上分配不期望的内容,从而导致延迟关键型应用程序的性能下降。随着介绍 TopologyManager,我们现在有办法避免这种情况。

这篇博客文章涵盖:

  1. NUMA简介及其重要性
  2. 最终用户可以使用的策略,以确保CPU和设备的NUMA对齐
  3. 内部细节如何 TopologyManager 作品
  4. 当前的局限性 TopologyManager
  5. 未来的方向 TopologyManager

那么,什么是NUMA?为什么我要关心?

术语NUMA代表非统一内存访问。它是多CPU系统上可用的一项技术,它允许不同的CPU以不同的速度访问内存的不同部分。直接连接到CPU的任何内存都被认为是该CPU的“本地”内存,可以非常快速地进行访问。任何未直接连接到CPU的内存都被认为是“非本地”的,并且具有不同的访问时间,具体取决于要通过多少个互连才能到达。在现代系统上,具有“本地”和“非本地”内存的想法也可以扩展到诸如NIC或GPU的外围设备。为了获得高性能,应该分配CPU和设备,以便它们可以访问相同的本地内存。

NUMA系统上的所有内存都分为一组“ NUMA节点”,每个节点代表一组CPU或设备的本地内存。我们将单个CPU视为NUMA节点的一部分,如果其本地内存与该NUMA节点相关联。

我们将外围设备视为NUMA节点的一部分,这是基于达到该数量必须经过的最短互连数量。

例如,在图1中,CPU 0-3被称为NUMA节点0的一部分,而CPU 4-7被称为NUMA节点1的一部分。同样,GPU 0和NIC 0被称为NUMA节点0的一部分。被连接到套接字0,套接字的CPU都是NUMA节点0的一部分。对于NUMA节点1上的GPU 1和NIC 1也是如此。

图1: 一个示例系统,其中包含2个NUMA节点,2个插槽(每个插槽具有4个CPU,2个GPU和2个NIC)。套接字0,GPU 0和NIC 0上的CPU都是NUMA节点0的一部分。套接字1,GPU 1和NIC 1上的CPU都是NUMA节点1的一部分。

尽管上面的示例显示了NUMA节点到Socket的1-1映射,但是在通常情况下不一定是正确的。单个NUMA节点上可能有多个插槽,或者单个插槽的各个CPU可能连接到不同的NUMA节点。此外,诸如Sub-NUMA群集(在最新的Intel CPU上可用)允许单个CPU与多个NUMA节点关联,只要它们对两个节点的内存访问时间相同(或差异可以忽略不计)。

TopologyManager 已构建为处理所有这些情况。

对齐!这是TeaM的努力!

如前所述, TopologyManager 允许用户按NUMA节点调整其CPU和外围设备分配。为此有几种策略:

  • none: 此政策不会尝试进行任何资源调整。它的作用与 TopologyManager 根本不存在。这是默认策略。
  • best-effort: 有了这项政策, TopologyManager 将尝试尽最大可能在NUMA节点上对齐分配,但是即使某些分配的资源在同一NUMA节点上未对齐,也会始终允许pod启动。
  • restricted: 此政策与 best-effort 策略,除非如果分配的资源无法正确对齐,它将导致pod接纳失败。不像 single-numa-node 策略,如果不可能,则某些分配可能来自多个NUMA节点 曾经 满足单个NUMA节点上的分配请求(例如,请求2个设备,系统上仅有2个设备在不同的NUMA节点上)。
  • single-numa-node: 此政策是限制性最强的政策,并且仅在以下情况允许豆荚入场 所有 可以仅从一个NUMA节点分配请求的CPU和设备。

重要的是要注意,所选策略将分别应用于Pod规范中的每个容器,而不是将所有容器中的资源对齐在一起。

此外,单一政策适用于 所有 通过全局在节点上的豆荚 kubelet 标记,而不是允许用户逐个容器(或逐个容器)选择不同的策略。我们希望将来放宽此限制。

kubelet 设置这些策略之一的标志如下所示:

--topology-manager-policy=
    [none | best-effort | restricted | single-numa-node]

此外, TopologyManager 受功能门保护。自Kubernetes 1.16起,此功能门已可用,但自1.18起默认仅启用此功能。

可以按以下方式启用或禁用功能闸(如更详细描述) 这里):

--feature-gates="...,TopologyManager=<true|false>"

为了根据所选策略触发对齐,用户必须根据一组特定要求在其pod规范中请求CPU和外围设备。

对于外围设备,这意味着从设备插件提供的可用资源中请求设备(例如 intel.com/sriov, nvidia.com/gpu等)。仅当设备插件已扩展以与 TopologyManager。目前,唯一已知具有此扩展名的插件是 Nvidia GPU设备插件英特尔SRIOV网络设备插件。有关如何扩展设备插件以与 TopologyManager 可以被找寻到 这里.

对于CPU,这要求 CPUManager 已经配置了 --static 策略已启用,并且Pod正在保证的QoS类中运行(即所有CPU和内存 limits 等于它们各自的CPU和内存 requests)。还必须要求CPU提供整数值(例如, 1, 2, 1000m等)。有关如何设置 CPUManager 可以找到政策 这里.

例如,假设 CPUManager 正在运行 --static 启用了策略,并且设备插件 gpu-vendor.comnic-vendor.com 已扩展以与 TopologyManager 正确地,以下广告连播规范足以触发 TopologyManager 运行其选定的策略:

spec:
   containers:
   - name: numa-aligned-container
     image: alpine
     resources:
         limits:
             cpu: 2
             memory: 200Mi
             gpu-vendor.com/gpu: 1
             nic-vendor.com/nic: 1

遵循上一节的图1,这将导致以下对齐分配之一:

{cpu: {0, 1}, gpu: 0, nic: 0}
{cpu: {0, 2}, gpu: 0, nic: 0}
{cpu: {0, 3}, gpu: 0, nic: 0}
{cpu: {1, 2}, gpu: 0, nic: 0}
{cpu: {1, 3}, gpu: 0, nic: 0}
{cpu: {2, 3}, gpu: 0, nic: 0}

{cpu: {4, 5}, gpu: 1, nic: 1}
{cpu: {4, 6}, gpu: 1, nic: 1}
{cpu: {4, 7}, gpu: 1, nic: 1}
{cpu: {5, 6}, gpu: 1, nic: 1}
{cpu: {5, 7}, gpu: 1, nic: 1}
{cpu: {6, 7}, gpu: 1, nic: 1}

就是这样!只需遵循此模式即可 TopologyManager 确保在请求拓扑感知设备和专用CPU的容器之间实现NUMA对齐。

**NOTE:** if a pod 是 rejected by one of the **TopologyManager** policies, it will be placed in a **Terminated** state with a pod admission error 和 a reason of "TopologyAffinityError“。一旦Pod处于这种状态,Kubernetes调度程序将不会尝试重新调度它。因此,建议使用 Deployment 带有副本的服务器,以在发生此类故障时触发Pod的重新部署。一个 外部控制回路 也可以实施以触发具有 TopologyAffinityError.

这太好了,那么它如何在引擎盖下工作?

伪代码用于由 TopologyManager 可以在下面看到:

对于 container := range append(InitContainers, Containers...) {
    对于 provider := range 提示提供者 {
        hints += provider.GetTopologyHints(container)
    }

    bestHint := policy.Merge(hints)

    对于 provider := range 提示提供者 {
        provider.Allocate(container, bestHint)
    }
}

下图总结了此循环期间采取的步骤:

步骤本身是:

  1. 遍历容器中的所有容器。
  2. 对于每个容器,收集“拓扑提示“来自一组”提示提供者”,以针对容器请求的每种可感知拓扑的资源类型(例如 gpu-vendor.com/gpu, nic-vendor.com/nic, cpu等)。
  3. 使用选定的策略,合并收集的 拓扑提示 查找“最佳”提示,以使所有资源类型之间的资源分配保持一致。
  4. 循环返回提示提供者集合,指示他们使用合并的提示作为指导来分配他们控制的资源。
  5. 此循环在pod接纳时间运行,如果这些步骤中的任何一个失败或根据所选策略无法满足对齐要求,将无法接纳pod。发生故障之前分配的所有资源都会相应清理。

以下各节将更详细地介绍 拓扑提示提示提供者,以及有关每个策略使用的合并策略的一些详细信息。

拓扑提示

A TopologyHint 对一组约束进行编码,从中可以满足给定的资源请求。目前,我们考虑的唯一约束是NUMA对齐。定义如下:

type TopologyHint struct {
    NUMANodeAffinity bitmask.BitMask
    Preferred bool
}

NUMANodeAffinity 字段包含可满足资源请求的NUMA个节点的位掩码。例如,在具有2个NUMA节点的系统上,可能的掩码包括:

{00}, {01}, {10}, {11}

Preferred 字段包含一个布尔值,该布尔值编码给定的提示是否为“首选”。随着 best-effort 策略,当生成“最佳”提示时,优先提示将优先于非优先提示。随着 restrictedsingle-numa-node 政策中,非首选提示将被拒绝。

一般来说, 提示提供者 生成 拓扑提示 通过查看可以满足资源请求的一组当前可用资源。更具体地说,它们产生一个 TopologyHint 可以满足资源请求的NUMA个节点的每个可能的掩码。如果掩码不能满足要求,则将其省略。例如,一个 HintProvider 当被要求分配2个资源时,可能会在具有2个NUMA节点的系统上提供以下提示。这些提示编码为两种资源都可以来自单个NUMA节点(0或1),也可以各自来自不同的NUMA节点(但我们更希望它们仅来自一个)。

{01: True}, {10: True}, {11: False}

目前,所有 提示提供者 设置 PreferredTrue 当且仅当 NUMANodeAffinity 编码一个 最小的 可以满足资源请求的NUMA节点集。通常,这只会是 True 对于 拓扑提示 在其位掩码中设置了一个NUMA节点。但是,也可能是 True 如果唯一的方法 曾经 满足资源请求是跨越多个NUMA节点(例如,请求2个设备,并且系统上仅有2个设备位于不同的NUMA节点上):

{0011: True}, {0111: False}, {1011: False}, {1111: False}

注意: 的设置 Preferred 这样的领域是 基于当前可用资源集。它基于在NUMA最小节点集上物理分配请求资源数量的能力。

这样,有可能 HintProvider 返回带有提示的列表 所有 Preferred 字段设置为 False 如果在其他容器释放其资源之前无法满足实际的首选分配。例如,考虑图1中系统的以下情形:

  1. 当前,除2个CPU外的所有CPU均已分配给容器
  2. 剩余的2个CPU在不同的NUMA节点上
  3. 一个新的容器问来2个CPU

在这种情况下,唯一生成的提示是 {11: False} 并不是 {11: True}。发生这种情况是因为 可以从该系统上的同一NUMA节点分配2个CPU(鉴于当前的分配状态,现在暂时不可以)。这样做的想法是,在可以满足最小对齐方式时,使pod进入失败并重试部署比允许以次优对齐方式调度pod更好。

提示提供者

A HintProvider 是内部的组件 kubelet 将协调的资源分配与 TopologyManager。目前,唯一 提示提供者 在Kubernetes中 CPUManagerDeviceManager。我们计划增加对 HugePages 不久。

如前所述, TopologyManager 都聚集 拓扑提示提示提供者 并使用合并的“最佳”提示在其上触发对齐的资源分配。因此, 提示提供者 实现以下接口:

type HintProvider interface {
    GetTopologyHints(*v1.Pod, *v1.Container) map[string][]TopologyHint
    Allocate(*v1.Pod, *v1.Container) error
}

请注意, GetTopologyHints() 返回一个 map[string][]TopologyHint。这允许一个 HintProvider 提供多种资源类型的提示,而不仅仅是一种。例如, DeviceManager 要求这样做,以便针对其插件注册的每种资源类型传递回提示。

提示提供者 生成提示,他们只考虑如何满足对齐要求 目前 系统上的可用资源。不考虑已经分配给其他容器的任何资源。

例如,考虑图1中的系统,其中以下两个容器从中请求资源:

Container0Container1
spec:
    containers:
    - name: numa-aligned-container0
      image: alpine
      resources:
          limits:
              cpu: 2
              memory: 200Mi
              gpu-vendor.com/gpu: 1
              nic-vendor.com/nic: 1
spec:
    containers:
    - name: numa-aligned-container1
      image: alpine
      resources:
          limits:
              cpu: 2
              memory: 200Mi
              gpu-vendor.com/gpu: 1
              nic-vendor.com/nic: 1

如果 Container0 是考虑在系统上分配的第一个容器,将为规范中的三种拓扑感知资源类型生成以下提示集。

               cpu: {{01: True}, {10: True}, {11: False}}
gpu-vendor.com/gpu: {{01: True}, {10: True}}
nic-vendor.com/nic: {{01: True}, {10: True}}

结果是:

{cpu: {0, 1}, gpu: 0, nic: 0}

考虑时 Container1 然后假定这些资源不可用,因此将仅生成以下提示集:

               cpu: {{01: True}, {10: True}, {11: False}}
gpu-vendor.com/gpu: {{10: True}}
nic-vendor.com/nic: {{10: True}}

结果是:

{cpu: {4, 5}, gpu: 1, nic: 1}

注意: 与本节开头提供的伪代码不同,对 Allocate() 实际上并没有直接为合并的“最佳”提示采用参数。相反, TopologyManager 实现以下 Store 接口 提示提供者 可以查询,以获取为特定容器生成的提示:

type Store interface {
    GetAffinity(podUID string, containerName string) TopologyHint
}

将其分离成自己的API调用,就可以在pod接纳循环之外访问此提示。这对于调试以及在诸如以下工具中报告生成的提示非常有用 kubectl(尚不可用)。

政策合并

给定策略定义的合并策略决定了如何合并一组 拓扑提示 由所有人产生 提示提供者 变成一个 TopologyHint 可用于通知已对齐的资源分配。

所有受支持策略的常规合并策略均以相同的方式开始:

  1. 取的乘积 拓扑提示 为每种资源类型生成
  2. 对于叉积中的每个条目, bitwise-and 每个的NUMA相似性 TopologyHint 一起。在结果“合并”提示中将此设置为NUMA关联。
  3. 如果条目中的所有提示都包含 Preferred 设置 True ,设定 PreferredTrue 在产生的“合并”提示中。
  4. 如果条目中的提示之一甚至具有 Preferred 设置 False ,设定 PreferredFalse 在产生的“合并”提示中。也设置 PreferredFalse 如果其NUMA相似性包含全0,则在“合并”提示中输入。

遵循上一节中的示例并提供了有关 Container0 生成为:

               cpu: {{01: True}, {10: True}, {11: False}}
gpu-vendor.com/gpu: {{01: True}, {10: True}}
nic-vendor.com/nic: {{01: True}, {10: True}}

上面的算法将产生以下一组交叉产品条目和“合并”提示:

跨产品输入

{cpu, gpu-vendor.com/gpu, nic-vendor.com/nic}

“合并”提示
{{01: True}, {01: True}, {01: True}}{01: True}
{{01: True}, {01: True}, {10: True}}{00: False}
{{01: True}, {10: True}, {01: True}}{00: False}
{{01: True}, {10: True}, {10: True}}{00: False}
{{10: True}, {01: True}, {01: True}}{00: False}
{{10: True}, {01: True}, {10: True}}{00: False}
{{10: True}, {10: True}, {01: True}}{00: False}
{{10: True}, {10: True}, {10: True}}{01: True}
{{11: False}, {01: True}, {01: True}}{01: False}
{{11: False}, {01: True}, {10: True}}{00: False}
{{11: False}, {10: True}, {01: True}}{00: False}
{{11: False}, {10: True}, {10: True}}{10: False}

生成此“合并”提示列表后,这就是特定任务的工作。 TopologyManager 用来决定将哪一个视为“最佳”提示的政策。

通常,这涉及:

  1. 按合并提示的“狭窄程度”对其进行排序。狭窄度定义为提示的NUMA相似性掩码中设置的位数。设置的位数越少,提示越窄。对于在NUMA关联掩码中设置了相同位数的提示,设置为最低位的提示被认为是较窄的。
  2. 按它们对合并的提示进行排序 Preferred 领域。具有的提示 Preferred 设置 True 被认为比带有提示的可能性更高 Preferred 设置 False.
  3. 选择最佳设置的最窄提示 Preferred.

在这种情况下 best-effort 该算法将始终导致 一些 提示被选为“最佳”提示,并接受广告连播。然后,此“最佳”提示可用于 提示提供者 因此他们可以根据此资源进行资源分配。

但是,对于 restrictedsingle-numa-node 政策,任何选择的提示 Preferred 设置 False 将立即被拒绝,从而导致Pod接纳失败,并且无法分配资源。而且, single-numa-node 还将拒绝在其亲和力掩码中设置了多个NUMA节点的选定提示。

在上面的示例中,所有策略都会将Pod接纳为提示 {01: True}.

即将推出的增强功能

虽然1.18发行版和Beta的升级带来了一些重大的增强和修复,但仍然存在许多限制, 这里。我们已经在努力解决这些限制以及更多限制。

本节将逐步介绍我们计划针对以下方面实施的一系列增强功能: TopologyManager 在不远的将来。该列表并不详尽,但是可以很好地说明我们的发展方向。它是根据我们希望看到每个增强功能完成的时间范围排序的。

如果您想参与其中的任何增强功能,请 参加Kubernetes SIG节点每周会议 了解更多信息并成为社区努力的一部分!

支持特定于设备的约束

目前,NUMA亲和力是 TopologyManager 用于资源调整。而且,唯一可以扩展到 TopologyHint 涉及 节点级 约束,例如跨设备类型的PCIe总线对齐。尝试添加任何东西将很棘手 特定于设备 对该结构的约束(例如,一组GPU设备之间的内部NVLINK拓扑)。

因此,我们提出了对设备插件接口的扩展,该扩展将允许插件陈述其拓扑感知的分配首选项,而不必向kubelet公开任何特定于设备的拓扑信息。这样, TopologyManager 可以仅将其限制为仅处理常见的节点级拓扑约束,而仍然可以将特定于设备的拓扑约束合并到其分配决策中。

有关该提案的详细信息,请参见 这里,并且应该在Kubernetes 1.19上立即可用。

大页面的NUMA对齐

如前所述,只有两个 提示提供者 目前可供 TopologyManagerCPUManagerDeviceManager。但是,目前也正在努力增加对大页面的支持。随着这项工作的完成, TopologyManager 最终将能够在同一NUMA节点上分配内存,大页面,CPU和PCI设备。

A 韩国环保局 这项工作目前正在审核中,并且正在进行原型开发以很快实现此功能。

调度意识

目前, TopologyManager 充当Pod Admission控制器。它不会直接参与放置容器的位置的调度决策中。相反,当kubernetes调度程序(或部署中正在运行的任何调度程序)将pod放置在要运行的节点上时, TopologyManager 将决定是否应该“允许”或“拒绝”该广告连播。如果由于缺乏可用的NUMA对齐资源而拒绝吊舱,则事情可能会变得有些有趣。这个kubernetes 问题 很好地强调并讨论了这种情况。

那么我们如何解决这个限制呢?我们有 Kubernetes调度框架 营救!该框架提供了一组新的插件API,这些API与现有的Kubernetes Scheduler集成在一起,并允许实现诸如NUMA对齐之类的调度功能,而不必诉诸其他可能吸引力较小的替代方案,包括编写自己的调度程序,甚至更糟。 ,创建一个叉子来添加您自己的调度程序秘密调味料。

有关如何实现这些扩展以与以下产品集成的详细信息: TopologyManager 尚未解决。我们仍然需要回答以下问题:

  • 我们是否需要重复的逻辑来确定 TopologyManager 和调度程序?
  • 我们需要一个新的API来获取 拓扑提示 来自 TopologyManager 调度程序插件?

此功能的工作应在接下来的几个月内开始,敬请期待!

每人结盟政策

如前所述,单一政策适用于 所有 通过全局在节点上的豆荚 kubelet 标记,而不是允许用户逐个容器(或逐个容器)选择不同的策略。

尽管我们同意这将是一个很棒的功能,但在实现之前有很多障碍需要克服。最大的障碍是,此增强功能将要求更改API,以便能够在Pod规范或其关联规范中表达所需的对齐策略。 RuntimeClass.

我们现在才开始就此功能进行认真的讨论,并且距最佳状态尚有几个版本尚待发布。

结论

随着推广 TopologyManager 到1.18中的Beta版本,我们鼓励所有人尝试一下,并期待您的任何反馈。在过去的几个发行版中已进行了许多修复和增强,极大地提高了该功能和可靠性。 TopologyManager 及其 提示提供者。尽管仍然有很多限制,但我们计划为解决这些问题制定一系列增强功能,并期待在即将发布的版本中为您提供许多新功能。

如果您有其他增强功能的想法或对某些功能的需求,请随时与我们联系。团队始终乐于接受建议,以增强和改进 TopologyManager.

我们希望您发现此博客有用且有用!如果您有任何疑问或意见,请告诉我们。而且,部署愉快.....对齐!