Red Hat Training
A Red Hat training course is available for Red Hat Enterprise Linux
8.4. libvirt NUMA 调试
通常,NUMA 系统通过将客机大小限制在单一 NUMA 节点的资源数量实现最佳性能。应当避免 NUMA 节点中不必要的分散资源。
使用
numastat
工具对进程和操作系统的每个 NUMA 节点内存统计进行查看。
在以下示范中,
numastat
工具显示了 NUMA 节点中的四种非优化内存排列的虚拟机:
# numastat -c qemu-kvm
Per-node process memory usage (in MBs)
PID Node 0 Node 1 Node 2 Node 3 Node 4 Node 5 Node 6 Node 7 Total
--------------- ------ ------ ------ ------ ------ ------ ------ ------ -----
51722 (qemu-kvm) 68 16 357 6936 2 3 147 598 8128
51747 (qemu-kvm) 245 11 5 18 5172 2532 1 92 8076
53736 (qemu-kvm) 62 432 1661 506 4851 136 22 445 8116
53773 (qemu-kvm) 1393 3 1 2 12 0 0 6702 8114
--------------- ------ ------ ------ ------ ------ ------ ------ ------ -----
Total 1769 463 2024 7462 10037 2672 169 7837 32434
运行
numad
,使客机 CPU 和内存资源自动对齐。
接下来再次运行
numastat -c qemu-kvm
,以查看 numad
的运行结果。以下输出显示了资源已对齐:
# numastat -c qemu-kvm
Per-node process memory usage (in MBs)
PID Node 0 Node 1 Node 2 Node 3 Node 4 Node 5 Node 6 Node 7 Total
--------------- ------ ------ ------ ------ ------ ------ ------ ------ -----
51747 (qemu-kvm) 0 0 7 0 8072 0 1 0 8080
53736 (qemu-kvm) 0 0 7 0 0 0 8113 0 8120
53773 (qemu-kvm) 0 0 7 0 0 0 1 8110 8118
59065 (qemu-kvm) 0 0 8050 0 0 0 0 0 8051
--------------- ------ ------ ------ ------ ------ ------ ------ ------ -----
Total 0 0 8072 0 8072 0 8114 8110 32368
注意
同时运行
numastat
和 -c
可提供简洁的输出;添加 -m
可将每个节点上系统范围内的内存信息添加到输出。更多信息请参见 numastat
手册页。
8.4.1. NUMA 虚拟 CPU 钉选
虚拟 CPU 钉选为裸机系统中的任务钉选提供了相似的优点。由于虚拟 CPU 在主机操作系统中作为用户空间任务运行,钉选将提高缓存效率。例如,当所有虚拟 CPU 线程都在同一个物理 socket 中运行时,将会共享 L3 缓存域。
合并虚拟 CPU 钉选和
numatune
可以避免 NUMA 缺失。NUMA 缺失会对性能带来显著影响,通常会有至少 10% 或更多的性能损失。虚拟 CPU 钉选和 numatune
应该同时配置。
当虚拟机在执行存储或网络 I/O 任务时,最好将所有的虚拟 CPU 和内存固定至同一个连接到 I/O 适配器的物理 socket。
注意
lstopo 工具可以被用作使 NUMA 拓扑可视化。同时还可以用作验证虚拟 CPU 在同一个物理插槽中的内核绑定。更多信息请参照知识库文章,链接如下:lstopo: https://access.redhat.com/site/solutions/62879。
重要
在虚拟 CPU 的数量远多于物理内核时,钉选将导致复杂性增加。
以下示例 XML 配置具有固定在物理 CPU 0-7 上的域过程。虚拟线程固定在其自身的 cpuset 上。例如,虚拟 CPU0 被固定在 物理 CPU 0 上,虚拟 CPU1 被固定在物理 CPU 1 上等等:
<vcpu cpuset='0-7'>8</vcpu> <cputune> <vcpupin vcpu='0' cpuset='0'/> <vcpupin vcpu='1' cpuset='1'/> <vcpupin vcpu='2' cpuset='2'/> <vcpupin vcpu='3' cpuset='3'/> <vcpupin vcpu='4' cpuset='4'/> <vcpupin vcpu='5' cpuset='5'/> <vcpupin vcpu='6' cpuset='6'/> <vcpupin vcpu='7' cpuset='7'/> </cputune>
虚拟 CPU 与 vcpupin 标签之间有直接的联系。如果 vcpupin 选项不是确定的,那么属性值会被自动指定并会从上一级虚拟 CPU 标签选项中继承。以下配置显示了因为 vcpu 5 丢失的
<vcpupin>
。进而,vCPU5 将会被固定在物理 CPU 0-7 上,正如上一级标签 <vcpu>
中指定的那样:
<vcpu cpuset='0-7'>8</vcpu> <cputune> <vcpupin vcpu='0' cpuset='0'/> <vcpupin vcpu='1' cpuset='1'/> <vcpupin vcpu='2' cpuset='2'/> <vcpupin vcpu='3' cpuset='3'/> <vcpupin vcpu='4' cpuset='4'/> <vcpupin vcpu='6' cpuset='6'/> <vcpupin vcpu='7' cpuset='7'/> </cputune>
重要
<vcpupin>
、<numatune>
以及 <emulatorpin>
应该被一同配置,从而实现优化、确定的性能。更多关于 <numatune>
标签的信息,请查看第 8.4.2 节 “域进程”。更多关于 <emulatorpin>
标签的信息,请参照第 8.4.4 节 “使用 emulatorpin”。
8.4.2. 域进程
如 Red Hat Enterprise Linux 所提供的那样,libvirt 使用 libnuma 来为域进程设定内存绑定政策。这些政策的节点集可以作为 static(在域 XML 中指定),或 auto (通过询问 numad 进行配置)进行配置。关于如何配置这些
<numatune>
标签内部的示例,请参考以下 XML 配置:
<numatune> <memory mode='strict' placement='auto'/> </numatune>
<numatune> <memory mode='strict' nodeset='0,2-3'/> </numatune>
libvirt 使用 sched_setaffinity(2) 来为域进程设定 CPU 绑定政策。cpuset 选项既可以是 static (静态;在域 XML 中指定),也可以是 auto (自动;通过询问 numad 进行配置)。关于如何配置这些
<vcpu>
标签内部的示例,请参考以下 XML 配置:
<vcpu placement='auto'>8</vcpu>
<vcpu placement='static' cpuset='0-10,ˆ5'>8</vcpu>
在
<vcpu>
所使用的放置模式和 <numatune>
之间有隐式的继承规则:
<numatune>
的放置模式默认为与<vcpu>
相同的放置模式,当<nodeset>
被指定时,则被默认为 static。- 同样,
<vcpu>
的放置模式默认为与<numatune>
相同的放置模式,当<cpuset>
被指定时,则被默认为 static。
这意味着为了域进程而进行的 CPU 调试和内存调试可以被单独指定和定义,但是它们也可以在依赖其他放置模式的情况下进行配置。
也有一种可能,即通过 numad 来配置您的系统,可以启动选定数量的虚拟 CPU,并且在启动时不需要固定到所有的虚拟 CPU。
例如,通过 32 个虚拟 CPU 使仅有的 8 个虚拟 CPU 在一个系统中启动,配置 XML 方式与如下方式类似:
<vcpu placement='auto' current='8'>32</vcpu>
注意
更多关于虚拟 CPU 和 numatune 的信息请参照以下网址: http://libvirt.org/formatdomain.html#elementsCPUAllocation 和 http://libvirt.org/formatdomain.html#elementsNUMATuning
8.4.3. 域虚拟 CPU 线程
除了调试域进程,libvirt 还允许 XML 配置中每个虚拟 CPU 线程的钉选策略设置。设置
<cputune>
标签内部每个虚拟 CPU 线程的钉选策略:
<cputune> <vcpupin vcpu="0" cpuset="1-4,ˆ2"/> <vcpupin vcpu="1" cpuset="0,1"/> <vcpupin vcpu="2" cpuset="2,3"/> <vcpupin vcpu="3" cpuset="0,4"/> </cputune>
在这个标签中,libvirt 可以使用 cgroup 或 sched_setaffinity(2),使虚拟 CPU 线程固定在指定的 cpuset 中。
注意
更多关于
<cputune>
的信息,请参照以下网址: http://libvirt.org/formatdomain.html#elementsCPUTuning
8.4.4. 使用 emulatorpin
调试域进程钉选策略的另一个方法是使用
<cputune>
中的 <emulatorpin>
标签。
<emulatorpin>
标签指定了 emulator(一个域的子集,不包括虚拟 CPU)将被固定的主机物理 CPU。<emulatorpin>
标签提供了一个将精确关联设定成仿真线程进程的方法。因此,vhost 线程在同一个物理 CPU 和内存子集中运行,从而可以从缓存位置获益。例如:
<cputune> <emulatorpin cpuset="1-3"/> </cputune>
注意
在 Red Hat Enterprise Linux 7 中,默认启用自动化 NUMA 平衡。随着 vhost-net 仿真线程能够更加可靠地跟随虚拟 CPU 任务,自动化 NUMA 平衡减少了手动调试
<emulatorpin>
的需要。关于自动化 NUMA 平衡的更多信息,请参照第 8.3 节 “自动化 NUMA 平衡”。
8.4.5. 用 virsh 调试 vcpu CPU 钉选
重要
这些只是示例命令。您需要依据环境替换属性值。
以下示例
virsh
命令会将 ID 为 1 的虚拟 CPU 线程(rhel7)固定到物理 CPU 2。
% virsh vcpupin rhel7 1 2
您也可以通过
virsh
命令获得当前虚拟 CPU 的钉选配置。例如:
% virsh vcpupin rhel7
8.4.6. 用 virsh 调试域进程 CPU 钉选
重要
这些只是示例命令。您需要依据环境替换属性值。
emulatorpin
选项将 CPU 关联设置应用到与每个域进程关联的线程。为了完全固定,您必须给每个客机同时使用 virsh vcpupin
(如之前所展示的)和 virsh emulatorpin
。例如:
% virsh emulatorpin rhel7 3-4
8.4.7. 用 virsh 调试域进程内存策略
域进程内存可以动态调试。请参考以下示例指令:
% virsh numatune rhel7 --nodeset 0-10
关于这些指令的更多示例,请参见
virsh
手册页。
8.4.8. 客机 NUMA 拓扑
客机 NUMA 拓扑可以通过使用
<cpu>
标签中的 <numa>
标签在客机虚拟机的 XML 中进行指定。请参照以下示例,并替换相应的属性值:
<cpu> ... <numa> <cell cpus='0-3' memory='512000'/> <cell cpus='4-7' memory='512000'/> </numa> ... </cpu>
每个
<cell>
元素指定一个 NUMA cell 或者 NUMA 节点。cpus
指定了 CPU 或者部分节点的系列 CPU,memory
以千位字节(1,024字节一块)指定了节点内存。从 0 开始,cellid
或 nodeid
以递增的次序被指定到每个 cell 或节点。
8.4.9. 向多个客机 NUMA 节点指定主机大页面
在 Red Hat Enterprise Linux 7.1 中,主机中的大页面可以被分配到多个客机的 NUMA 节点。这一过程可以优化内存性能,因为客机 NUMA 节点可以被移动到主机 NUMA 节点需要的位置上,同时客机可以继续使用主机指定的大页面。
在配置客机 NUMA 节点拓扑后(详情参考第 8.4.8 节 “客机 NUMA 拓扑”),在客机 XML 的
<memoryBacking>
元素中指定大页面的大小和客机 NUMA 节点集。page size
和 unit
代表主机大页面的大小。nodeset
指定了大页面被分配的客机 NUMA 节点(或若干节点)。
在以下示例中,客机 NUMA 节点 0-5(不包括 NUMA 节点 4)将会使用 1 GB 的大页面,客机 NUMA 节点 4 将使用 2 MB 的大页面,无论客机 NUMA 节点在主机的任何位置。为了在客机中使用 1 GB 的大页面,主机必须先启动 1 GB 大页面;启动 1 GB 大页面的方法请参考第 7.3.3 节 “大页面和透明大页面(THP)”
<memoryBacking> <hugepages/> <page size="1" unit="G" nodeset="0-3,5"/> <page size="2" unit="M" nodeset="4"/> </hugepages> </memoryBacking>
当一些客机 NUMA 节点和单独主机 NUMA 节点进行合并起作用时,将允许对大页面控制的增强,但继续使用不同的大页面尺寸。例如,即使客机 NUMA 节点 4 和 5 被移动到了主机的 NUMA 节点 1 上,它们也将继续使用不同大小的大页面。
注意
当使用
strict
内存模式时,在 NUMA 节点上不具有足够可用的大页面的情况下,客机将无法启用。关于 <numatune>
标签中 strict
内存模式选项的配置示例,请参照第 8.4.2 节 “域进程”。
8.4.10. PCI 设备的 NUMA 节点位置
当启动一个新的虚拟机时,了解主机 NUMA 拓扑和 NUMA 节点中的 PCI 设备归属是重要的一点,以便在请求 PCI 传递时,客机可以被固定在正确的 NUMA 节点以优化其内存性能。
例如,如果客机被固定在 NUMA 节点 0-1 上,但是其 PCI 设备中的一个隶属于节点 2,那么节点之间的数据传输将花费一段时间。
在 Red Hat Enterprise Linux 7.1 中,libvirt 在客机 XML 中为 PCI 设备报道了 NUMA 节点位置,使管理应用程序完成更好的性能决策。
该信息在
/sys/devices/pci*/*/numa_node
的 sysfs
文件中可见。使用 lstopo 工具来回报 sysfs
数据,可以作为验证这些设置的一种方法。
# lstopo-no-graphics Machine (126GB) NUMANode L#0 (P#0 63GB) Socket L#0 + L3 L#0 (20MB) L2 L#0 (256KB) + L1d L#0 (32KB) + L1i L#0 (32KB) + Core L#0 + PU L#0 (P#0) L2 L#1 (256KB) + L1d L#1 (32KB) + L1i L#1 (32KB) + Core L#1 + PU L#1 (P#2) L2 L#2 (256KB) + L1d L#2 (32KB) + L1i L#2 (32KB) + Core L#2 + PU L#2 (P#4) L2 L#3 (256KB) + L1d L#3 (32KB) + L1i L#3 (32KB) + Core L#3 + PU L#3 (P#6) L2 L#4 (256KB) + L1d L#4 (32KB) + L1i L#4 (32KB) + Core L#4 + PU L#4 (P#8) L2 L#5 (256KB) + L1d L#5 (32KB) + L1i L#5 (32KB) + Core L#5 + PU L#5 (P#10) L2 L#6 (256KB) + L1d L#6 (32KB) + L1i L#6 (32KB) + Core L#6 + PU L#6 (P#12) L2 L#7 (256KB) + L1d L#7 (32KB) + L1i L#7 (32KB) + Core L#7 + PU L#7 (P#14) HostBridge L#0 PCIBridge PCI 8086:1521 Net L#0 "em1" PCI 8086:1521 Net L#1 "em2" PCI 8086:1521 Net L#2 "em3" PCI 8086:1521 Net L#3 "em4" PCIBridge PCI 1000:005b Block L#4 "sda" Block L#5 "sdb" Block L#6 "sdc" Block L#7 "sdd" PCIBridge PCI 8086:154d Net L#8 "p3p1" PCI 8086:154d Net L#9 "p3p2" PCIBridge PCIBridge PCIBridge PCIBridge PCI 102b:0534 GPU L#10 "card0" GPU L#11 "controlD64" PCI 8086:1d02 NUMANode L#1 (P#1 63GB) Socket L#1 + L3 L#1 (20MB) L2 L#8 (256KB) + L1d L#8 (32KB) + L1i L#8 (32KB) + Core L#8 + PU L#8 (P#1) L2 L#9 (256KB) + L1d L#9 (32KB) + L1i L#9 (32KB) + Core L#9 + PU L#9 (P#3) L2 L#10 (256KB) + L1d L#10 (32KB) + L1i L#10 (32KB) + Core L#10 + PU L#10 (P#5) L2 L#11 (256KB) + L1d L#11 (32KB) + L1i L#11 (32KB) + Core L#11 + PU L#11 (P#7) L2 L#12 (256KB) + L1d L#12 (32KB) + L1i L#12 (32KB) + Core L#12 + PU L#12 (P#9) L2 L#13 (256KB) + L1d L#13 (32KB) + L1i L#13 (32KB) + Core L#13 + PU L#13 (P#11) L2 L#14 (256KB) + L1d L#14 (32KB) + L1i L#14 (32KB) + Core L#14 + PU L#14 (P#13) L2 L#15 (256KB) + L1d L#15 (32KB) + L1i L#15 (32KB) + Core L#15 + PU L#15 (P#15) HostBridge L#8 PCIBridge PCI 1924:0903 Net L#12 "p1p1" PCI 1924:0903 Net L#13 "p1p2" PCIBridge PCI 15b3:1003 Net L#14 "ib0" Net L#15 "ib1" OpenFabrics L#16 "mlx4_0"
此结果表明:
- NIC
em*
与磁盘sd*
是与 NUMA 节点 0 和 cores 0、2、4、6、8、10、12、14 连接的。 - NIC
p1*
与ib*
是与 NUMA 节点 1 和 cores 1、3、5、7、9、11、13、15 连接的。