13.2. 解决方案
使用这个部分来调查和配置仿真程序线程固定。
13.2.1. qemu-kvm Emulator Threads 复制链接链接已复制到粘贴板!
仿真程序线程处理虚拟机硬件模拟的中断请求和非阻塞进程。未运行 vCPU 的线程是 qemu-kvm 仿真程序线程。请参见以下示例。
[root@overcloud-compute-0 ~]# ps -Tp `pgrep -f instance-00000009`
PID SPID TTY TIME CMD
364936 364936 ? 00:00:02 qemu-kvm
364936 364946 ? 00:00:00 qemu-kvm
364936 364952 ? 00:00:52 CPU 0/KVM
364936 364953 ? 00:00:26 CPU 1/KVM
364936 364954 ? 00:00:30 CPU 2/KVM
364936 364956 ? 00:00:00 vnc_worker
由于 Linux CFS(完全公平调度程序), 模拟程序线程通常会定期从一个 pCPU 移到另一个(在 libvirt 的仿真器中定义)中。
在 NFV 环境中,如果您使用 isolcpus 参数配置仿真程序线程时,您可能会遇到问题,因为这种内核配置会禁用这些 CPU 上的 CFS 调度。如果您不使用 isolcpus 参数,可以在仿真程序线程中断正在处理数据包的 CPU 时遇到数据包丢失。
仿真程序线程示例包括:
- qemu-kvm 线程
- vnc_worker 线程
- vhost-<qemu-kvm PID> 内核线程(使用 virtio-net(虚拟机监控程序上的内核网络)
13.2.2. 默认行为线程线程处理 复制链接链接已复制到粘贴板!
默认情况下,nova 将配置仿真程序线程固定设置,该设置跨越分配给所有 vCPU 的 pCPU。如果您不使用 isolcpus 参数,则可以在任何 pCPU 上调度模拟器线程,并定期从一个 pCPU 移到另一个 CPU。
virsh dumpxml instance-0000001d
(...)
<vcpu placement='static'>4</vcpu>
<cputune>
<shares>4096</shares>
<vcpupin vcpu='0' cpuset='34'/>
<vcpupin vcpu='1' cpuset='14'/>
<vcpupin vcpu='2' cpuset='10'/>
<vcpupin vcpu='3' cpuset='30'/>
<emulatorpin cpuset='10,14,30,34'/>
</cputune>
(...)
[root@overcloud-compute-0 ~]# virsh dumpxml instance-00000009
(...)
<nova:vcpus>3</nova:vcpus>
<vcpu placement='static'>3</vcpu>
<vcpupin vcpu='0' cpuset='1'/>
<vcpupin vcpu='1' cpuset='2'/>
<vcpupin vcpu='2' cpuset='3'/>
(...)
<emulatorpin cpuset='1-3'/>
(...)
因此,这些 CPU 都可以由 qemu 的仿真程序线程来抢占,从而降低数据包丢失的风险。
有关仿真器线程固定新功能的当前进度,请参阅 Bug 1468004 和 OpenStack Change 510897
在编写本文时,草案指定了以下线程策略:
Valid THREAD-POLICY values are:
- ``share``: (default) The emulator threads float across the pCPUs
associated to the guest. To place a workload's emulator threads on
a set of isolated physical CPUs, set ``share``` and
``[compute]/cpu_shared_set`` configuration option to the set of
host CPUs that should be used for best-effort CPU resources.
- ``isolate``: The emulator threads are isolated on a single pCPU.
13.2.3. 关于在 Emulator Thread scheduling 上 isolcpus 的影响 复制链接链接已复制到粘贴板!
使用 isolcpus 时,C CFS 调度程序被禁用,所有仿真程序线程都将在第一个可用、最低索引的 pCPU 上运行。因此,如果没有干预或进一步配置,实例的一个 vCPU 会为仿真器线程的资源争用造成高风险。
更多信息请参阅 Kernel.org Bugzilla - Bug 116701。
使用以下算法来确定仿真程序线程使用哪个 vCPU:
PCPU=MIN([EMULATORPINSET])
VCPU=REVERSE_CPUSET(PCPU)
REVERSE_CPUSET := SELECT pcpu from `virsh dumpxml <instance name> | grep "cpuset=$PCPU"`
例如,在这个实例中,所有仿真程序线程和子项从默认模拟器固定集继承了关联 1-3:
[root@overcloud-compute-0 ~]# taskset -a -c -p `pgrep -f instance-00000009`
pid 364936's current affinity list: 1-3
pid 364946's current affinity list: 1-3
pid 364952's current affinity list: 1
pid 364953's current affinity list: 2
pid 364954's current affinity list: 3
pid 364956's current affinity list: 1-3
[root@overcloud-compute-0 ~]# ps -Tp `pgrep -f instance-00000009`
PID SPID TTY TIME CMD
364936 364936 ? 00:00:02 qemu-kvm
364936 364946 ? 00:00:00 qemu-kvm
364936 364952 ? 00:00:51 CPU 0/KVM
364936 364953 ? 00:00:26 CPU 1/KVM
364936 364954 ? 00:00:30 CPU 2/KVM
364936 364956 ? 00:00:00 vnc_worker
[root@overcloud-compute-0 ~]# pgrep -f vhost- | xargs -I {} taskset -a -c -p {}
pid 364948's current affinity list: 1-3
pid 364949's current affinity list: 1-3
pid 364950's current affinity list: 1-3
[root@overcloud-compute-0 ~]#
与 isolcpus 相结合,所有仿真程序线程和 vhost-* 线程在 pCPU1 上执行,且永远不会重新调度:
cat /proc/sched_debug | sed '/^cpu#/,/^runnable/{//!d}' | grep vhost -C3
(...)
cpu#1, 2099.998 MHz
runnable tasks:
task PID tree-key switches prio wait-time sum-exec sum-sleep
----------------------------------------------------------------------------------------------------------
watchdog/1 11 -2.995579 410285 0 0.000000 5025.887998 0.000000 0 /
migration/1 12 0.000000 79 0 0.000000 3.375060 0.000000 0 /
ksoftirqd/1 13 5172444.259776 54 120 0.000000 0.570500 0.000000 0 /
kworker/1:0 14 5188475.472257 370 120 0.000000 14.707114 0.000000 0 /
kworker/1:0H 15 8360.049510 10 100 0.000000 0.150151 0.000000 0 /
kworker/1:1 2707 5045807.055876 16370 120 0.000000 793.611916 0.000000 0 /
kworker/1:1H 2763 5187682.987749 11755 100 0.000000 191.949725 0.000000 0 /
qemu-kvm 364936 3419.522791 50276 120 0.000000 2476.880384 0.000000 0 /machine.slice/machine-qemu\x2d6\x2dinstance\x2d00000009.scope/emulator
qemu-kvm 364946 1270.815296 102 120 0.000000 23.204111 0.000000 0 /machine.slice/machine-qemu\x2d6\x2dinstance\x2d00000009.scope/emulator
CPU 0/KVM 364952 52703.660314 53709 120 0.000000 52715.105472 0.000000 0 /machine.slice/machine-qemu\x2d6\x2dinstance\x2d00000009.scope/vcpu0
vnc_worker 364956 123.609634 1 120 0.000000 0.016849 0.000000 0 /machine.slice/machine-qemu\x2d6\x2dinstance\x2d00000009.scope/emulator
vhost-364936 364948 3410.527677 1039 120 0.000000 84.254772 0.000000 0 /machine.slice/machine-qemu\x2d6\x2dinstance\x2d00000009.scope/emulator
vhost-364936 364949 3407.341502 55 120 0.000000 2.894394 0.000000 0 /machine.slice/machine-qemu\x2d6\x2dinstance\x2d00000009.scope/emulator
vhost-364936 364950 3410.395220 174 120 0.000000 10.969077 0.000000 0 /machine.slice/machine-qemu\x2d6\x2dinstance\x2d00000009.scope/emulator
cpu#2, 2099.998 MHz
runnable tasks:
task PID tree-key switches prio wait-time sum-exec sum-sleep
----------------------------------------------------------------------------------------------------------
watchdog/2 16 -5.995418 410285 0 0.000000 5197.571153 0.000000 0 /
migration/2 17 0.000000 79 0 0.000000 3.384688 0.000000 0 /
ksoftirqd/2 18 -7.031102 3 120 0.000000 0.019079 0.000000 0 /
kworker/2:0 19 0.119413 39 120 0.000000 0.588589 0.000000 0 /
kworker/2:0H 20 -1.047613 8 100 0.000000 0.086272 0.000000 0 /
kworker/2:1 2734 1475469.236026 11322 120 0.000000 241.388582 0.000000 0 /
CPU 1/KVM 364953 27258.370583 33294 120 0.000000 27269.017017 0.000000 0 /machine.slice/machine-qemu\x2d6\x2dinstance\x2d00000009.scope/vcpu1
cpu#3, 2099.998 MHz
runnable tasks:
task PID tree-key switches prio wait-time sum-exec sum-sleep
----------------------------------------------------------------------------------------------------------
watchdog/3 21 -5.996592 410285 0 0.000000 4970.777439 0.000000 0 /
migration/3 22 0.000000 79 0 0.000000 3.886799 0.000000 0 /
ksoftirqd/3 23 -7.035295 3 120 0.000000 0.014677 0.000000 0 /
kworker/3:0 24 17.758583 38 120 0.000000 0.637152 0.000000 0 /
kworker/3:0H 25 -1.047727 8 100 0.000000 0.077141 0.000000 0 /
kworker/3:1 362530 154177.523420 83 120 0.000000 6.544285 0.000000 0 /
CPU 2/KVM 364954 32456.061889 25966 120 0.000000 32466.719084 0.000000 0 /machine.slice/machine-qemu\x2d6\x2dinstance\x2d00000009.scope/vcpu2
13.2.4. 模拟线程的最佳位置 复制链接链接已复制到粘贴板!
本节提供了将仿真程序线程放在以下网络中的描述:
- 实例的 DPDK 网络和 Open vSwitch 中的 netdev 数据路径
- 实例中 DPDK 网络,位于 Open vSwitch 中的系统数据路径和虚拟机监控程序上的内核空间网络
- 实例中内核和 Open vSwitch 中的内核网络
如果 DPDK 在实例内运行,则为完全在用户空间中进行数据包处理。不要将 PMD 调度到 vCPU0 上运行,因为这应该留给操作系统和中断处理。由于实例中的 PMD CPU 运行活跃循环并且需要 100% 的 CPU,所以不应被抢占。如果其中一个 vCPU 被抢占,则可能会发生数据包丢失。因此,模拟仿真程序需要进行配置,从而使它不会与处理编号为 1 及以上虚拟 CPU 的物理 CPU 重叠。
在实例中使用 DPDK 网络,仿真程序线程的最佳位置是处理 vCPU 0 的 pCPU,也可以是不处理任何虚拟 CPU 的专用物理 CPU。
如果虚拟机监控程序和 DPDK 在实例上使用 OVS-DPDK,请将仿真器线程放在 vCPU 0 上。
13.2.4.2. 使用 DPDK 网络在 Open vSwitch 中的实例和系统数据路径中优化调度线程的放置 复制链接链接已复制到粘贴板!
如果虚拟机监控程序上使用内核空间网络,则在内核内执行对管理程序的数据包处理。
在实例中使用 DPDK 网络,仿真程序线程的最佳位置是处理 vCPU 0 的 pCPU,也可以是不处理任何虚拟 CPU 的专用物理 CPU。
请注意,在这种情况下,vNIC 队列的数据包处理在 hypervisor 的 vhost-<qemu-kvm PID> 内核线程中执行。在高流量下,这些内核线程可生成大量 CPU 负载。需要根据情况确定仿真程序线程的最佳位置。
[root@overcloud-compute-0 ~]# ps aux | grep vhost-
root 364948 0.0 0.0 0 0 ? S 20:32 0:00 [vhost-364936]
root 364949 0.0 0.0 0 0 ? S 20:32 0:00 [vhost-364936]
root 364950 0.0 0.0 0 0 ? S 20:32 0:00 [vhost-364936]
在实例中使用内核联网,有两个选项:
- 优化中断分布,例如:实例的 softirqs。在这种情况下,您不必为仿真程序线程分配额外的 pCPU,并可将仿真程序线程分配给不处理任何网络中断的 pCPU。
- 在同一 NUMA 节点上使用一个专用的 pCPU 用于仿真程序线程。
由于第一个选项的复杂性,建议使用第二个选项。