带资源提供程序的 NUMA 拓扑¶
https://blueprints.launchpad.net/nova/+spec/numa-topology-with-rps
现在,嵌套资源提供程序在 Placement API 和 Nova 计算节点中都已存在,我们可以使用资源提供程序树来解释根资源提供程序(root 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 资源或带宽资源,它们的父节点仍将是计算节点。
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 额外规范中现有的表达方式转换为 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 的值将分别是 flavored vCPU 和 flavored 内存除以 hw:numa_nodes 值的结果(实际上,这已在 RequestSpec 对象中计算并作为 NUMATopology 对象信息提供)。
注意
从基于 flavor 的请求到 Placement 查询的转换机制将由调度程序服务处理。
注意
由于内存作为曾孙提供,我们总是需要请求默认的 MEMORY_PAGE_SIZE_SMALL。
请求特定的内存页大小¶
操作员定义一个包含 2 个 vCPU、4GB RAM 和 hw:mem_page_size=2MB,hw:numa_nodes=2 的 flavor 时,会发现 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 extra spec 属性并将其转换为 Placement 编号组查询。
由于将有一个配置选项来指示主机将变为 NUMA 感知型,因此相应的分配也必须更改,因此 virt 驱动程序负责提供重塑机制,该机制最终将在启动计算服务时调用 Placement API /reshaper endpoint。此重塑实现绝对需要考虑快速前滚升级 (FFU) 策略,在该策略中,所有控制平面都已关闭,并且可能需要记录 FFU 所需的任何额外步骤,并在几个版本后最终删除,一旦所有部署人员不再需要此支持。
最后但并非最不重要的一点是,我们将提供一个过渡期(至少在 Ussuri 期间),在此期间操作员可以决定将哪些主机专用于 NUMA 感知工作负载。一个特定的 nova-status pre-upgrade check 命令将警告他们在升级到 Victoria 之前这样做。
实现¶
负责人¶
bauzas
sean-k-mooney
功能联络人¶
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