带资源提供程序的 NUMA 拓扑¶
https://blueprints.launchpad.net/nova/+spec/numa-topology-with-rps
既然 嵌套资源提供程序 在 Placement API 和 Nova 计算节点中都已实现,我们可以使用资源提供程序树来解释根资源提供程序(根 RP),即计算节点,与一个或多个非统一内存访问 (NUMA) 节点(也称为单元)之间的关系,每个 NUMA 节点都有独立的资源,例如内存或 PCI 设备。
注意
本规范仅旨在以通用且相当抽象的方式对 NUMA 节点的资源能力进行建模。我们不会在本规范中讨论如何对 NUMA 亲和硬件(如 PCI 设备或 GPU)进行建模,而将在后续规范中讨论这些关系。
问题描述¶
NUMATopologyFilter 检查许多资源,包括模拟器线程策略、CPU 绑定实例和内存页大小。此外,它还执行两个不同的验证:
某个 主机是否能满足查询,因为它有足够的容量
哪个 资源(或哪些资源)应该用于此查询(例如,哪个 pCPU 或 NUMA 节点)
将 NUMA 拓扑建模为 Placement 资源后,这两个问题可以由 Placement 服务作为潜在分配候选者来回答,而过滤器将仅负责在某些非常具体的情况下(例如,PCI 设备 NUMA 亲和性、CPU 绑定和 NUMA 反亲和性)在它们之间进行选择。
因此,我们可以将主机内存和 CPU 拓扑建模为一组以树状排列的资源提供程序,并直接从代表 NUMA 节点及其资源的资源提供程序子树中为特定实例分配资源。
话虽如此,与资源无关的功能(如为 vCPU 在 NUMA 节点内选择特定的 CPU 绑定)仍将仅由 virt 驱动程序完成,并且不在本规范的讨论范围内。
用例¶
考虑以下针对“2 个 NUMA 节点,4 核”主机的 NUMA 拓扑,没有超线程:
+--------------------------------------+
| CN1 |
+-+---------------+--+---------------+-+
| NUMA1 | | NUMA2 |
+-+----+-+----+-+ +-+----+-+----+-+
|CPU1| |CPU2| |CPU3| |CPU4|
+----+ +----+ +----+ +----+
在这里,CPU1 和 CPU2 将通过一个公共内存控制器共享相同的内存,而 CPU3 和 CPU4 将共享它们自己的内存。
理想情况下,对于同一实例上需要多个 vCPU 低延迟内存访问的应用程序(出于并行计算的原因),它们会希望确保这些 CPU 资源由相同的 NUMA 节点提供,否则可能会出现性能损失(当然,如果您的应用程序是 CPU 密集型或 I/O 密集型)。目前,如果您是操作员,您可以使用 flavor extra specs 来指示您的实例所需的客户机 NUMA 拓扑,例如:
$ openstack flavor set FLAVOR-NAME \
--property hw:numa_nodes=FLAVOR-NODES \
--property hw:numa_cpus.N=FLAVOR-CORES \
--property hw:numa_mem.N=FLAVOR-MEMORY
请参阅 flavor 的所有 NUMA 可能的额外规范。
注意
当然,上面的示例仅在您不希望平均分配 NUMA 节点之间的虚拟 CPU 和内存时才需要。
提议的变更¶
鉴于 NUMA 有很多考虑因素,让我们对我们同意的模型采用迭代方法。
NUMA 节点作为嵌套资源提供程序¶
鉴于 virt 驱动程序可以修改计算节点 ResourceTracker 提供的提供程序树,libvirt 驱动程序可以为代表独立 NUMA 节点的两个套接字中的每一个创建子提供程序。
由于 CPU 资源与特定的 NUMA 节点绑定,因此将相应的资源类建模为子 NUMA 资源提供程序的一部分是有意义的。为了方便查询 NUMA 资源,我们建议用一个名为 HW_NUMA_ROOT 的特定特性来装饰 NUMA 子资源提供程序,该特性将存在于每个 NUMA 节点上。这将有助于了解哪些主机是 NUMA 感知的,哪些不是。
内存表示起来有点困难。NUMA 节点具有一定数量的附加内存的粒度在某种程度上是一种初步方法,但我们忽略了 Nova 可以分配的最小可分配单元实际上是页大小这一事实。因此,我们应该用代表您可以分配的最小内存单元(即页大小)的子资源提供程序来建模我们的 NUMA 子树。由于页大小不是一个可消耗的数量,而是一种帮助我们分配 MEMORY_MB 资源的定性信息,我们建议使用三个特性:
MEMORY_PAGE_SIZE_SMALL和MEMORY_PAGE_SIZE_LARGE将允许我们知道内存页大小是默认的还是可选配置的。CUSTOM_MEMORY_PAGE_SIZE_<X>,其中 <X> 是一个整数,将允许我们知道页的大小(以 KB 为单位)。为了清楚起见,即使该特性是自定义的,为其制定命名约定也很重要,以便调度程序可以在不知道所有特性的情况下查询页大小。
+-------------------------------+
| <CN_NAME> |
| DISK_GB: 5 |
+-------------------------------+
| (no specific traits) |
+--+---------------------------++
| |
| |
+-------------------------+ +--------------------------+
| <NUMA_NODE_O> | | <NUMA_NODE_1> |
| VCPU: 8 | | VCPU: 8 |
| PCPU: 16 | | PCPU: 8 |
+-------------------------+ +--------------------------+
| HW_NUMA_ROOT | | HW_NUMA_ROOT |
+-------------------+-----+ +--------------------------+
/ | \ /+\
+ | \_____________________________ .......
| | \
+-------------+-----------+ +-+--------------------------+ +-------------------------------+
| <RP_UUID> | | <RP_UUID> | | <RP_UUID> |
| MEMORY_MB: 1024 | | MEMORY_MB: 1024 | |MEMORY_MB: 10240 |
| step_size=1 | | step_size=2 | |step_size=1024 |
+-------------------------+ +----------------------------+ +-------------------------------+
|MEMORY_PAGE_SIZE_SMALL | |MEMORY_PAGE_SIZE_LARGE | |MEMORY_PAGE_SIZE_LARGE |
|CUSTOM_MEMORY_PAGE_SIZE_4| |CUSTOM_MEMORY_PAGE_SIZE_2048| |CUSTOM_MEMORY_PAGE_SIZE_1048576|
+-------------------------+ +----------------------------+ +-------------------------------+
注意
如上所述,我们目前不希望支持 Ussuri 的子 PCI 设备。根计算节点上的其他现有子 RP,例如用于 VGPU 资源或带宽资源的 RP,仍将以计算节点作为其父节点。
NUMA RP¶
NUMA 节点的资源提供程序名称应遵循 nodename_NUMA# 的约定,其中 nodename 是管理程序主机名(由 virt 驱动程序提供),NUMA# 将是字面上的字符串“NUMA”加上 virt 驱动程序提供的 NUMA 单元 ID。
每个 NUMA 节点都将是一个子资源提供程序,具有两个资源类:
VCPU:表示 NUMA 节点拥有的虚拟核心数(无法绑定)。PCPU:表示 NUMA 节点拥有的可能绑定的核心数。
正如我们所解释的,一个特定的特性应该装饰它:HW_NUMA_ROOT。
内存页大小 RP¶
每个 NUMA RP 都应为其每个可能的主机内存页大小拥有子 RP,并且只有一个资源类:
MEMORY_MB:表示 NUMA 节点在该特定页大小下有多少内存。
此 RP 将由两个特性装饰:
要么是
MEMORY_PAGE_SIZE_SMALL(如果未配置,则为默认值)或MEMORY_PAGE_SIZE_LARGE(如果配置了大页)页大小:CUSTOM_MEMORY_PAGE_SIZE_#(其中 # 为大小,单位为 KB - 默认为 4,因为内核默认为 4KB 页大小)
计算节点 RP¶
根资源提供程序(即计算节点)将只提供与 NUMA 无关的资源类。现有的用于 vGPU 或带宽感知资源的子 RP 仍应以其为父级(直到我们讨论 PCI 设备的 NUMA 亲和性)。
可选配置的 NUMA 资源¶
鉴于存在 NUMA 工作负载和非 NUMA 工作负载,对于运营商来说,让计算节点只接受后者也很重要。话虽如此,如果计算节点资源在多个 NUMA 节点之间划分,对于那些非 NUMA 工作负载来说可能是一个问题,如果它们想要保持现有行为的话。
例如,假设一个实例有 2 个 vCPU,一个主机有 2 个 NUMA 节点,但每个节点只接受一个 VCPU,那么 Placement API 将不接受该主机(因为每个嵌套的 RP 只接受一个 VCPU)。因此,我们需要一个配置来指示哪些资源应该被嵌套。为了加强上述观点,这意味着一个主机将要么是 NUMA 的,要么是非 NUMA 的,因此,如果主机设置为如此,非 NUMA 工作负载将设置在一个特定的 NUMA 节点上。我们在这里提出的建议将是:
[compute]
enable_numa_reporting_to_placement = <bool> (default None for Ussuri)
下文我们将把拥有此选项为 True 的主机称为“NUMA 感知”主机。将此选项设置为 False 的主机则被明确要求具有传统行为,并被称为“非 NUMA 感知”主机。
根据选项的值,Placement 将接受或不接受主机以进行相应的请求。结果矩阵可能如下:
+----------------------------------------+----------+-----------+----------+
| ``enable_numa_reporting_to_placement`` | ``None`` | ``False`` | ``True`` |
+========================================+==========+===========+==========+
| NUMA-aware flavors | Yes | No | Yes |
+----------------------------------------+----------+-----------+----------+
| NUMA-agnostic flavors | Yes | Yes | No |
+----------------------------------------+----------+-----------+----------+
其中 Yes 表示该主机可能有分配候选者,而 No 表示不会返回任何分配候选者。
为了区分值为 False 而非 None 的计算节点,我们将以前者装饰一个特定的特性名称 HW_NON_NUMA。因此,我们将通过添加这个禁止的特性来查询 Placement,以便不获取操作员明确不希望它们支持 NUMA 感知 flavor 的节点。
注意
默认情况下,为了升级的原因,该配置选项的值将为 None。在 Ussuri 发布时,操作员将必须决定哪些主机要支持 NUMA 感知实例,哪些应该专用于“非 NUMA 感知”实例。我们将提供一个 nova-status pre-upgrade check 命令,该命令将警告他们在升级到 Victoria 之前做出决定,如果默认值在本周期后期可能改变。一旦我们停止支持 None(在 Victoria 或更晚版本),HW_NON_NUMA 特性将不再需要,因此我们可以停止查询它。
注意
由于我们允许一个过渡期来帮助操作员做出决定,我们还将明确指出这是一个单向更改,并且我们不会提供将 NUMA 感知主机转换为非 NUMA 感知主机的向后支持。
有关更多详细信息,请参阅升级影响部分。
注意
由于 NUMA 拓扑的发现是由 virt 驱动程序完成的,因此这些嵌套资源提供程序的填充必然由每个 virt 驱动程序完成。因此,虽然上述配置选项被认为是通用的,但使用此选项填充资源提供程序树将仅由 virt 驱动程序完成。当然,为了驱动程序之间的一致性,可以设想一个共享模块,但这是一个实现细节。
最简单的情况:我不关心 NUMA 感知实例¶
对于只请求 vCPU 和内存,而不要求它们是 NUMA 感知的 flavor,我们将进行一次 Placement 调用,要求不将它们放置在 NUMA 感知主机上:
resources=VCPU:<X>,MEMORY_MB=<Y>
&required=!HW_NUMA_ROOT
在这种情况下,即使 NUMA 感知主机有足够的资源满足此查询,Placement API 也不会提供它们,而只会提供非 NUMA 感知主机(考虑到禁止的 HW_NUMA_ROOT 特性)。我们让操作员可以选择将他们的云在 NUMA 感知主机和非 NUMA 感知主机之间进行分片,但这实际上并没有改变目前的操作行为,因为操作员会创建聚合以确保非 NUMA 感知实例无法在 NUMA 感知主机上运行。
有关部分升级到 Ussuri 且只有少数节点被重塑的滚动升级情况,请参阅 Upgrade impact 会话。
请求 NUMA 感知 vCPU¶
由于 NUMA 感知主机具有特定拓扑,其中内存位于曾孙 RP 中,因此我们基本上需要确保可以将 flavor extra specs 中现有的表达能力转换为 Placement 分配候选者查询,该查询要求包含 VCPU 资源的 NUMA RP 与包含 MEMORY_MB 资源的内存页大小 RP 之间存在父子关系。
因此,以下是一些示例:
对于一个 8 个 VCPU、8GB RAM 且
hw:numa_nodes=2的 flavor:resources_MEM1=MEMORY_MB:4096 &required_MEM1=MEMORY_PAGE_SIZE_SMALL &resources_PROC1=VCPU:4 &required_NUMA1=HW_NUMA_ROOT &same_subtree=_MEM1,_PROC1,_NUMA1 &resources_MEM2=MEMORY_MB:4096 &required_MEM2=MEMORY_PAGE_SIZE_SMALL &resources_PROC2=VCPU:4 &required_NUMA2=HW_NUMA_ROOT &same_subtree=_MEM2,_PROC2,_NUMA2 &group_policy=none
注意
我们将 none 作为 group_policy 的值,这意味着在此示例中,分配候选者都可以来自 PROC1 组,这意味着我们破坏了将资源分离到不同 NUMA 节点的目的(这是 hw:numa_nodes=2 的目的)。这没问题,因为我们还将修改 NUMATopologyFilter,使其只接受属于不同 NUMA 节点的宿主机分配候选者。它可能在 nova.virt.hardware 模块中实现,但那是一个实现细节。
对于一个 8 个 VCPU、8GB RAM 且
hw:numa_nodes=1的 flavor:resources_MEM1=MEMORY_MB:8192 &required_MEM1=MEMORY_PAGE_SIZE_SMALL &resources_PROC1=VCPU:8 &required_NUMA1=HW_NUMA_ROOT &same_subtree=_MEM1,_PROC1,_NUMA1
对于一个 8 个 VCPU、8GB RAM 且
hw:numa_nodes=2&hw:numa_cpus.0=0,1&hw:numa_cpus.1=2,3,4,5,6,7的 flavor:resources_MEM1=MEMORY_MB:4096 &required_MEM1=MEMORY_PAGE_SIZE_SMALL &resources_PROC1=VCPU:2 &required_NUMA1=HW_NUMA_ROOT &same_subtree=_MEM1,_PROC1,_NUMA1 &resources_MEM2=MEMORY_MB:4096 &required_MEM2=MEMORY_PAGE_SIZE_SMALL &resources_PROC2=VCPU:6 &required_NUMA2=HW_NUMA_ROOT &same_subtree=_MEM2,_PROC2,_NUMA2 &group_policy=none
对于一个 8 个 VCPU、8GB RAM 且
hw:numa_nodes=2&hw:numa_cpus.0=0,1&hw:numa_mem.0=1024 &hw:numa_cpus.1=2,3,4,5,6,7&hw:numa_mem.1=7168的 flavor:resources_MEM1=MEMORY_MB:1024 &required_MEM1=MEMORY_PAGE_SIZE_SMALL &resources_PROC1=VCPU:2 &required_NUMA1=HW_NUMA_ROOT &same_subtree=_MEM1,_PROC1,_NUMA1 &resources_MEM2=MEMORY_MB:7168 &required_MEM2=MEMORY_PAGE_SIZE_SMALL &resources_PROC2=VCPU:6 &required_NUMA2=HW_NUMA_ROOT &same_subtree=_MEM2,_PROC2,_NUMA2 &group_policy=none
正如您所理解的,VCPU 和 MEMORY_MB 的值将分别是 flavor 中 vCPU 和 flavor 中内存除以 hw:numa_nodes 值的结果(这实际上已经计算并作为 NUMATopology 对象信息提供在 RequestSpec 对象中)。
注意
从基于 flavor 的请求到 Placement 查询的转换机制将由调度服务处理。
注意
由于内存是作为曾孙提供的,我们总是需要请求 MEMORY_PAGE_SIZE_SMALL,这是默认值。
请求特定内存页大小¶
操作员定义的 flavor,具有 2 个 vCPU、4GB RAM 和 hw:mem_page_size=2MB,hw:numa_nodes=2,将看到 Placement 查询变为:
resources_PROC1=VCPU:1
&resources_MEM1=MEMORY_MB:2048
&required_MEM1=CUSTOM_MEMORY_PAGE_SIZE_2048
&required_NUMA1=HW_NUMA_ROOT
&same_subtree=_PROC1,_MEM1,_NUMA1
&resources_PROC2=VCPU:1
&resources_MEM2=MEMORY_MB:2048
&required_MEM2=CUSTOM_MEMORY_PAGE_SIZE_2048
&required_NUMA2=HW_NUMA_ROOT
&same_subtree=_PROC2,_MEM2,_NUMA2
&group_policy=none
如果您只想要大页支持,而不实际指定大小(例如,通过指定 hw:mem_page_size=large 而不是,比如说,2MB),那么上述相同的大页请求将转换为:
resources_PROC1=VCPU:1
&resources_MEM1=MEMORY_MB:2048
&required_MEM1=MEMORY_PAGE_SIZE_LARGE
&required_NUMA1=HW_NUMA_ROOT
&same_subtree=_PROC1,_MEM1,_NUMA1
&resources_PROC2=VCPU:1
&resources_MEM2=MEMORY_MB:2048
&required_MEM2=MEMORY_PAGE_SIZE_LARGE
&required_NUMA2=HW_NUMA_ROOT
&same_subtree=_PROC2,_MEM2,_NUMA2
&group_policy=none
如果使用 hw:mem_page_size=small 请求,则会转换为:
resources_PROC1=VCPU:1
&resources_MEM1=MEMORY_MB:2048
&required_MEM1=MEMORY_PAGE_SIZE_SMALL
&required_NUMA1=HW_NUMA_ROOT
&same_subtree=_PROC1,_MEM1,_NUMA1
&resources_PROC2=VCPU:1
&resources_MEM2=MEMORY_MB:2048
&required_MEM2=MEMORY_PAGE_SIZE_SMALL
&required_NUMA2=HW_NUMA_ROOT
&same_subtree=_PROC2,_MEM2,_NUMA2
&group_policy=none
最终,如果使用 hw:mem_page_size=any 请求,则表示:
resources_PROC1=VCPU:1
&resources_MEM1=MEMORY_MB:2048
&required_NUMA1=HW_NUMA_ROOT
&same_subtree=_PROC1,_MEM1,_NUMA1
&resources_PROC2=VCPU:1
&resources_MEM2=MEMORY_MB:2048
&required_NUMA2=HW_NUMA_ROOT
&same_subtree=_PROC2,_MEM2,_NUMA2
&group_policy=none
注意
正如我们为 vCPU 所说的,鉴于我们使用 group_policy=none 进行查询,分配候选者将位于同一个 NUMA 节点内,但这没问题,因为我们也说过调度程序过滤器将不会同意它们,如果存在 hw:numa_nodes=X。
NUMA 感知 flavor 的回退情况¶
在可选配置的 NUMA 资源部分,我们说过我们希望接受 NUMA 感知 flavor 降落在 enable_numa_reporting_to_placement 选项设置为 None 的主机上。由于我们尚无法为分配候选者构建 OR 查询,我们建议对 Placement 进行另一次调用。在此特定调用(我们将其命名为回退调用)中,我们希望获取所有未重塑的节点,这些节点未明确声明不支持 NUMA。在这种情况下,请求非常简单,因为我们用 HW_NON_NUMA 特性装饰了它们:
resources=VCPU:<X>,MEMORY_MB=<Y>
&required=!HW_NON_NUMA,!HW_NUMA_ROOT
然后我们将获得所有值为 None 的计算节点(包括在滚动升级中仍在运行 Train 版本的节点)。
当然,我们会得到可能不接受 NUMA 感知 flavor 的节点,但我们依赖于 NUMATopologyFilter 不选择它们,这与我们在 Train 中所做的完全相同。
关于我们是否应该仅在 NUMA 特定调用未能获取候选者时才进行回退调用,或者无论如何都应该生成两次调用并合并结果,存在一些悬而未决的问题。前者出于性能原因更好,因为它避免了潜在的不必要调用,但可能会产生一些潜在的散布/打包亲和性问题。在此,我们都同意暂时搁置这个问题,并将解决方案推迟到实施阶段。
备选方案¶
NUMA 资源的建模可以通过使用特定的 NUMA 资源类来完成,例如 NUMA_VCPU 或 NUMA_MEMORY_MB,这些资源类只为子 NUMA 资源提供程序设置,而 VCPU 和 MEMORY_MB 资源类只在根资源提供程序(此处为计算节点)上设置。
如果 Placement 分配候选者 API 也能提供一种方法来表示“您可以在资源提供程序之间拆分资源”,那么我们就不需要长期保留特定的配置选项。所有主机都将重塑为 NUMA 感知,但非 NUMA 感知实例可能降落在这些主机上。这并不会改变为了最佳容量,操作员需要将他们的云在 NUMA 工作负载和非 NUMA 工作负载之间进行分片的事实,但从 Placement 的角度来看,所有主机都是平等的。这个替代方案已经在规范中进行了广泛讨论,但最终的共识是它非常难以实现,并且可能不值得付出这种努力。
数据模型影响¶
无
REST API 影响¶
无
安全影响¶
无
通知影响¶
无
其他最终用户影响¶
无,flavor 不需要修改,因为我们将提供一个翻译机制。话虽如此,我们将在文档中明确说明我们不支持 flavor 中的任何类似 placement 的额外规范。
性能影响¶
仅当将配置选项更改为 True 时,才会执行重塑操作。
其他部署者影响¶
操作员可能希望在明确启用或禁用节点上的 NUMA 感知之前,将一些实例从主机迁移到其他主机,因为他们将不得不相应地考虑容量使用情况,因为他们将不得不对他们的云进行分片。话虽如此,这仅适用于尚未通过聚合在主机之间划分 NUMA 感知和非 NUMA 感知工作负载的云。
开发人员影响¶
无,除了 virt 驱动程序维护者。
升级影响¶
如上所述,为了防止在升级期间更新 flavor,我们将提供一个翻译机制,将现有的 flavor 额外规范属性转换为 Placement 编号组查询。
由于将有一个配置选项来表明主机将变为 NUMA 感知,因此相应的分配也必须随之改变,因此 virt 驱动程序负责提供一种重塑机制,该机制最终将在启动计算服务时调用 Placement API /reshaper endpoint。此重塑实现绝对需要考虑快速前向升级 (FFU) 策略,在该策略中所有控制平面都已关闭,并且可能需要记录 FFU 所需的任何额外步骤,并在几个版本后最终删除,一旦所有部署者不再需要此支持。
最后但同样重要的是,我们将提供一个过渡期(至少在 Ussuri 期间),操作员可以在此期间决定哪些主机专用于 NUMA 感知工作负载。一个特定的 nova-status pre-upgrade check 命令将警告他们在升级到 Victoria 之前这样做。
实现¶
负责人¶
sean-k-mooney
bauzas
功能联络人¶
bauzas
工作项¶
libvirt 驱动程序通过
update_provider_tree()API 传递 NUMA 拓扑Hyper-V 驱动程序通过
update_provider_tree()API 传递 NUMA 拓扑NUMATopologyFilter 可能需要进行工作以查看候选者
调度程序将 NUMA 属性的 flavor 额外规范转换为 Placement 查询
nova-status pre-upgrade check命令
依赖项¶
无。
测试¶
功能测试和单元测试。
文档影响¶
无。
参考资料¶
为 vCPU 在 NUMA 节点内选择特定的 CPU 绑定: https://docs.openstack.org/nova/latest/admin/cpu-topologies.html#customizing-instance-cpu-pinning-policies
NUMA 可能的额外规范: https://docs.openstack.org/nova/latest/admin/flavors.html#extra-specs-numa-topology
大页: https://docs.openstack.org/nova/latest/admin/huge-pages.html
Placement API /reshaper 端点: https://developer.openstack.org/api-ref/placement/?expanded=id84-detail#reshaper
Placement can_split: https://review.opendev.org/#/c/658510/
物理 CPU 资源: https://specs.openstack.org/openstack/nova-specs/specs/train/approved/cpu-resources.html
历史¶
发布名称 |
描述 |
|---|---|
Ussuri |
引入 |
Victoria |
重新提出 |