Linux 性能调优系列(二十五) 聊聊VirtIO、TuneD、virtio-mem 调优
1 | 作者:李晓辉 |
- Linux 性能调优系列(一) 建立科学的性能分析思路
- Linux 性能调优系列(二) 掌握基础性能监控命令
- Linux 性能调优系列(三) sysstat实现性能数据持久采集
- Linux 性能调优系列(四) 别只会用 sysstat,PCP 才是 Linux 性能监控的进阶玩法
- Linux 性能调优系列(五) 给你的服务器做一次全面体检
- Linux 性能调优系列(六) 想调内核参数?先搞懂 /proc、sysctl 和 sysfs
- Linux 性能调优系列(七) 还在手动改 sysctl?TuneD 智能性能调优实战来了
- Linux 性能调优系列(八) 进程太能吃资源怎么办?用 ulimit 和 systemd 给它加个"上限"
- Linux 性能调优系列(九) cgroup v2实战:防止一个烂进程干崩整台服务器
- Linux 性能调优系列(十) CPU 100% 到底是谁在忙?用 perf 把"元凶"揪出来
- Linux 性能调优系列(十一) 程序为什么卡住了?strace 和 ltrace 带你找到答案
- Linux 性能调优系列(十二) 服务器又卡了?SystemTap 带你杀到内核看看为啥
- Linux 性能调优系列(十三) 从 SystemTap 到 eBPF,Linux 性能追踪进入新时代
- Linux 性能调优系列(十四) CPU 这么忙,到底是谁决定“让谁先跑”?一文搞懂 Linux 调度器
- Linux 性能调优系列(十五) CPU 到底该先给谁?搞懂 Linux 实时调度
- Linux 性能调优系列(十六) Linux CPU 隔离实战,为什么实时线程绑核后延迟还会抖?
- Linux 性能调优系列(十七) CPU 占用不高却跑的慢?深挖 CPU 缓存性能瓶颈
- Linux 性能调优系列(十八) 程序申请了1GB内存,为什么系统却说它没用?
- Linux 性能调优系列(十九) 服务器硬盘慢得像蜗牛?先别急着换硬盘,这四招先把瓶颈找出来
- Linux 性能调优系列(二十) 磁盘 I/O 调度器怎么选?一文搞懂 mq-deadline、kyber、bfq 和 none
- Linux 性能调优系列(二十一) 磁盘为什么慢?从文件系统到 I/O 调优,一次讲透
- Linux 性能调优系列(二十二) Linux 网络调优别乱改 sysctl!先找瓶颈再调 TCP 缓冲区
- Linux 性能调优系列(二十三) MTU 从 1500 到 9000,巨帧真的能提升性能吗?
- Linux 性能调优系列(二十四) 从 CPU 省电到网卡卸载,聊聊 Linux 网络调优
- Linux 性能调优系列(二十五) 聊聊VirtIO、TuneD、virtio-mem 调优
前面我们聊了不少 Linux 性能调优的方法。这次换个场景:同样的 CPU、同样的内存,为什么虚拟机跑起来,还是感觉没有裸机快?
比如,你在物理机上跑一个 I/O 密集型应用,磁盘读写挺快。把应用搬进虚拟机后,业务代码没变,磁盘也没换,性能却可能下降了。
这时候,先别急着给虚拟机加 CPU、加内存。
问题可能出在虚拟设备上,也可能出在客户机的内核配置、磁盘 I/O 路径,甚至是宿主机本身。
今天就以 RHEL 10 为例,沿着一条比较实用的调优路线走一遍:先检查虚拟设备,再调整 TuneD 和磁盘调度器,最后看看如何使用 virtio-mem 动态调整虚拟机内存。
需要说明的是,本文主要讨论 KVM/libvirt 环境。不同的存储后端、宿主机配置和业务负载,调优结果可能不一样。咱们不追求把参数改得越多越好,而是尽量找到真正影响性能的地方。
虚拟机为什么可能比裸机慢?
先别急着改参数,咱们先看看虚拟机运行时,到底多做了哪些工作。
flowchart LR
A["应用程序"] --> B["RHEL 客户机内核"]
B --> C["VirtIO 虚拟设备"]
C --> D["KVM / QEMU"]
D --> E["宿主机内核"]
E --> F["物理 CPU、内存、磁盘、网卡"]
style A fill:#e8f4ff,stroke:#4385be
style C fill:#fff1d6,stroke:#d99b2b
style D fill:#ffe4e4,stroke:#cf6666
style F fill:#e2f5e8,stroke:#4a9b65应用程序看到的是一套虚拟硬件,但真正提供计算、内存和 I/O 能力的,仍然是宿主机背后的物理资源。
不过,虚拟化并不是把每条指令都重新模拟一遍。现代 CPU 提供了硬件虚拟化支持,很多客户机指令可以直接在物理 CPU 上执行。
真正值得关注的,是那些需要额外处理的路径。
CPU:VM exit 不是每条指令都要经历的
VM exit,也就是虚拟机退出,是虚拟化性能分析中经常出现的一个概念。
例如,客户机执行某些需要由 KVM 处理的操作,或者触发了需要虚拟机监控器介入的事件,就可能发生 VM exit。
大致过程如下:
- 客户机正在执行代码。
- 某个操作触发虚拟机退出。
- CPU 转到宿主机侧的虚拟化处理路径。
- KVM 等组件完成相应处理,再恢复客户机执行。
如果某类操作频繁触发 VM exit,就可能产生额外开销。
但这里有个容易误解的地方:不是所有特权指令都会触发 VM exit,更不是虚拟机执行的每条指令都要经过宿主机。
所以,看到虚拟机 CPU 使用率很高时,不要马上认定是 VM exit 导致的。还要结合宿主机 CPU 使用率、vCPU 调度等待时间以及具体负载进行分析。
内存:多了一层地址转换
裸机环境下,CPU 访问内存时,需要通过页表将虚拟地址转换为物理地址。
到了虚拟机环境,情况就多了一层。
flowchart TD
A["客户机虚拟地址 GVA"] --> B["客户机物理地址 GPA"]
B --> C["宿主机物理地址 HPA"]
A -.- D["客户机页表"]
B -.- E["EPT / NPT 等硬件辅助地址转换"]
style A fill:#e8f4ff,stroke:#4385be
style B fill:#fff1d6,stroke:#d99b2b
style C fill:#e2f5e8,stroke:#4a9b65这三个概念分别是:
- GVA:Guest Virtual Address,客户机虚拟地址。
- GPA:Guest Physical Address,客户机物理地址。
- HPA:Host Physical Address,宿主机物理地址。
客户机内核负责维护自己的页表,CPU 则通过 Intel EPT 或 AMD NPT 等硬件辅助机制完成第二阶段地址转换。
这意味着地址转换可能涉及更多层级的页表遍历。不过,CPU 的 TLB 可以缓存地址转换结果,并不是每次内存访问都要完整遍历所有页表。
因此,虚拟机内存性能不能只看内存容量,还需要关注 TLB 未命中、NUMA 远程访问、宿主机内存压力等因素。
存储:磁盘读写可能经过更多层
虚拟机访问磁盘时,数据通常需要经过客户机文件系统、虚拟块设备驱动、虚拟化后端以及宿主机块层,最后才到达实际存储设备。
如果使用文件形式的虚拟磁盘,还可能经过宿主机文件系统。
flowchart LR
A["应用程序"] --> B["客户机文件系统"]
B --> C["VirtIO 块设备"]
C --> D["QEMU / 存储后端"]
D --> E["宿主机块层"]
E --> F["物理存储设备"]
style C fill:#fff1d6,stroke:#d99b2b
style D fill:#ffe4e4,stroke:#cf6666
style F fill:#e2f5e8,stroke:#4a9b65这条链路涉及多个组件,任何一层出现瓶颈,都可能影响最终的 I/O 性能。
另外,客户机和宿主机都可能缓存文件数据,但不能简单认为所有场景都会产生两份完全相同的数据。是否存在缓存浪费,还得结合虚拟磁盘格式、缓存模式和实际工作负载判断。
网络:虚拟交换也会消耗 CPU
普通虚拟网卡的数据包,可能经过客户机网络协议栈、VirtIO 网卡、宿主机虚拟交换机,再到物理网卡。
网络吞吐量上升后,软中断、数据包处理和虚拟交换等工作,都可能消耗宿主机 CPU。
不过,启用 vhost、硬件卸载、多队列或 SR-IOV 等功能后,实际数据路径可能发生明显变化。
所以,虚拟机性能下降,不一定是客户机内部的问题。如果宿主机本身已经成为瓶颈,只在 RHEL 里调整几个内核参数,效果可能非常有限。
虚拟设备怎么选?优先检查 VirtIO
假设你新建了一台 RHEL 虚拟机,发现网卡使用的是 e1000 模拟设备,磁盘使用传统模拟控制器。
这时候就值得检查一下:有没有必要改成 VirtIO?
常见的虚拟设备可以分为三类。
| 类型 | 工作方式 | 特点 | 常见用途 |
|---|---|---|---|
| 模拟设备 | 模拟传统硬件接口 | 兼容性较好,但可能产生较多模拟开销 | 旧系统兼容、特殊需求 |
| VirtIO 半虚拟化设备 | 客户机通过专用驱动与虚拟化后端协作 | 通常具有较好的性能与兼容性平衡 | 大多数常规虚拟机 |
| PCI 设备直通 | 将物理设备或其虚拟功能分配给客户机 | 可以减少部分虚拟化数据路径开销,但管理更复杂 | 高性能网络、GPU 等场景 |
对于大多数常规业务,优先考虑 VirtIO,只有在明确的性能需求下,才进一步评估设备直通。
在 libvirt XML 里怎么看?
先在 KVM 宿主机上查看虚拟机配置:
1 | virsh dumpxml vm0 |
网卡配置可能类似这样:
1 | <interface type='network'> |
其中,model type='virtio' 表示使用 VirtIO 网卡模型。
如果使用模拟网卡,可能看到:
1 | <model type='e1000e'/> |
e1000e 是一种具体的模拟网卡型号。实际支持哪些型号,还取决于虚拟机机器类型和 QEMU 配置。
磁盘设备也类似:
1 | <disk type='file' device='disk'> |
这里的 bus='virtio' 表示使用 VirtIO 磁盘接口。
如果使用 VirtIO SCSI 控制器,设备配置会有所不同,不能只看磁盘名称就判断完整的数据路径。
设备改成 VirtIO 就一定能提速吗?
也不一定。
VirtIO 只是让客户机和虚拟化后端使用更适合虚拟化的设备接口,并不能解决所有性能问题。
比如:
- 虚拟磁盘所在的物理存储已经达到性能上限。
- 宿主机 CPU 过载,vCPU 长时间等待调度。
- 客户机内存不足,频繁发生换页。
- 网络瓶颈实际出现在宿主机的虚拟交换或物理网卡上。
这些问题都不是单纯更换 VirtIO 就能解决的。
如果准备将现有虚拟机的磁盘或网卡从模拟设备切换为 VirtIO,还要先确认客户机操作系统具备相应驱动。对于启动磁盘,驱动缺失甚至可能导致系统无法正常启动。
新建虚拟机时优先选择合适的 VirtIO 设备,通常比事后修改启动设备更省事。
物理设备直通是不是更快?
PCI 设备直通常借助 IOMMU 等硬件机制,将物理设备或其虚拟功能分配给客户机使用。
对于某些高吞吐、低延迟场景,直通确实可以减少部分软件处理开销。
但直通也有代价:
- 设备分配、IOMMU 分组和驱动绑定需要额外配置。
- 设备通常不能同时被宿主机和多个客户机随意共享。
- 迁移、快照和高可用方案可能受到限制。
- 直通不会自动消除客户机协议栈和业务程序自身的开销。
所以,别一看到虚拟化开销就马上考虑直通。先把 VirtIO 用好,再根据测试结果决定是否需要进一步优化。
TuneD:先给 RHEL 用上合适的配置文件
设备检查完,接下来看看客户机自己的配置。
RHEL 提供了 TuneD,用来按照不同的系统用途应用一组调优设置。相比手工修改一堆内核参数,先选一个合适的配置文件,再针对业务做小范围调整,通常更容易维护。
对于 RHEL 10 虚拟机,可以先从 virtual-guest 配置文件开始。
它基于 throughput-performance,并针对虚拟机环境调整了虚拟内存相关设置。
注意,宿主机和客户机的 TuneD 配置并不是一回事。RHEL 10 宿主机可以评估 virtual-host,而运行在虚拟机中的 RHEL 通常优先考虑 virtual-guest。
查看当前使用的配置文件
先执行:
1 | tuned-adm active |
如果当前没有使用 virtual-guest,并且确认该配置适合当前业务,可以切换:
1 | tuned-adm profile virtual-guest |
再检查一下:
1 | tuned-adm active |
其中,tuned-adm active 用于查看当前配置文件,tuned-adm verify 用于检查当前系统设置与配置文件预期是否一致。
如果想知道它具体调整了哪些参数,可以查看当前系统安装的配置:
1 | cat /usr/lib/tuned/profiles/virtual-guest/tuned.conf |
这里有个细节:TuneD 配置文件会随着软件包版本变化而变化,不要只根据网上的一份示例,就认定自己的系统一定使用相同参数。
swappiness 为什么值得关注?
查看当前值:
1 | sysctl vm.swappiness |
例如:
1 | vm.swappiness = 30 |
vm.swappiness 控制 Linux 内核在内存回收时使用交换空间的倾向。
当客户机内存压力增大时,内核可能将部分匿名内存换出到交换空间,以便回收内存供其他用途使用。
如果虚拟机的交换空间位于性能较差的存储上,频繁换页就可能带来明显延迟。
而且,如果宿主机自身也存在内存压力,虚拟机进程占用的内存还可能受到宿主机回收机制的影响。
不过,降低 swappiness 不等于彻底解决内存压力。
如果把这个值压得过低,内核可能更倾向于回收文件缓存;对于某些工作负载,性能反而可能下降。
因此,可以先使用 virtual-guest 提供的默认策略,再结合内存使用情况和业务表现决定是否需要进一步调整。
dirty_bytes 是不是越大越好?
你可能还会在 TuneD 配置中看到与脏页写回有关的设置。
所谓脏页,就是已经被修改、但尚未写回存储设备的缓存页。
如果应用持续产生脏页,内核需要在合适的时机将数据写回磁盘。如果写回速度跟不上应用写入速度,脏页就可能积累,最终影响业务延迟。
这里需要区分两件事:
vm.dirty_bytes和vm.dirty_ratio是 Linux 内核的脏页控制参数。- TuneD 配置文件中的
dirty_bytes则需要按照当前 TuneD 插件的配置规则解释。
不要把 TuneD 中的百分比写法直接当成普通 sysctl 参数,更不要未经确认就将 dirty_bytes = 30% 改成 vm.dirty_ratio = 30。
对于 RHEL 10,建议先检查当前安装的 virtual-guest 配置文件,以及实际生效的内核参数。如果没有明确的写回瓶颈,不必为了追求性能而手工修改脏页阈值。
先使用系统提供的配置文件,再根据实际数据决定要不要改参数,这样更稳妥。
磁盘调度器:none 就是最优解吗?
上一篇我们聊了 Linux I/O 调度器,知道了 none、mq-deadline、bfq 和 kyber 等调度器各自的特点。
到了虚拟机里,问题又来了:客户机有一层 I/O 调度,宿主机可能还有一层,到底该怎么选?
我的建议是:先别急着改,先看当前用的是什么,再做对比测试。
查看当前调度器
先确认设备名称:
1 | lsblk |
VirtIO 块设备常见的名称是 /dev/vda,VirtIO SCSI 设备则可能显示为 /dev/sda。
假设当前磁盘是 vda,执行:
1 | cat /sys/block/vda/queue/scheduler |
如果输出如下:
1 | [none] mq-deadline |
方括号里的 none 就是当前选中的调度器。
为什么虚拟机值得测试 none?
对于许多现代虚拟化存储场景,none 是值得测试的选择。
它不会在客户机块层使用传统的 I/O 请求排序策略,但这不代表整个存储路径完全没有调度。宿主机块层、存储阵列以及其他后端组件,仍然可能进行请求处理和调度。
因此,客户机使用 none 并不意味着宿主机也应该使用 none。
如果要临时测试,可以执行:
1 | echo none > /sys/block/vda/queue/scheduler |
这会立即影响当前设备的调度行为,但通常不会自动持久化。
测试时可以比较:
- 顺序读写吞吐量。
- 随机读写 IOPS。
- 平均 I/O 延迟。
- 高分位延迟,例如 p95、p99。
- 业务负载下的 CPU 使用率。
如果结果证明 none 更适合当前业务,再考虑通过适合本机 RHEL 和 TuneD 版本的方式持久化。
另外,切换前要确认设备支持目标调度器。不同内核、虚拟设备和块层配置,支持的选项可能不一样。
这一节的重点不是强行把调度器改成 none,而是先检查、再测试、最后持久化。
动态内存:用 virtio-mem 给虚拟机伸缩内存
前面解决的是虚拟机跑得快不快的问题。接下来,我们再解决另一个实际问题:虚拟机分配的内存,能不能随着业务需求动态调整?
假设宿主机上运行着多台虚拟机。
白天业务高峰时,虚拟机 A 需要更多内存;到了晚上,A 的业务量下降,但虚拟机 B 正好需要更多资源。
如果只能给虚拟机固定分配内存,就可能出现一边不够用、另一边闲置的情况。
这时,可以考虑 virtio-mem。
先强调一点:virtio-mem 的主要价值是动态调整内存容量、提高宿主机资源利用率,而不是直接提升虚拟机的运行速度。
内存气球和 virtio-mem 有什么区别?
传统的内存气球(ballooning)通过客户机驱动与宿主机协作,让客户机释放部分内存,供宿主机重新分配。
virtio-mem 则提供了另一种方式:它是一种半虚拟化内存设备,支持按指定粒度动态增加或减少虚拟机可使用的内存。
两种机制的工作方式不同,不能简单地认为气球机制一定造成严重碎片,而 virtio-mem 就完全没有碎片。
实际能否顺利缩容,还取决于客户机内核、内存区域分配、业务使用情况以及宿主机配置。
此外,RHEL 10 的 virtio-mem 有明确的兼容性限制,不能与内存气球膨胀、收缩机制随意混用。部署前应确认相关宿主机、客户机和 libvirt 版本都支持该功能。
先理清虚拟机的内存配置
下面假设:
- 虚拟机名称:
vm0。 - 初始内存:2 GiB。
- virtio-mem 设备最大容量:2 GiB。
- 虚拟机总内存容量上限:4 GiB。
先查看当前配置:
1 | virsh dumpxml vm0 |
对于使用 virtio-mem 的虚拟机,需要区分以下字段:
| XML 字段 | 含义 |
|---|---|
memory | 虚拟机初始内存配置 |
currentMemory | 当前配置的内存容量,运行时还需要结合实际状态判断 |
maxMemory | 虚拟机可使用的最大内存容量,包含初始内存和热插入内存 |
在本例中,初始内存为 2 GiB,最大内存为 4 GiB,可以将配置设计为类似下面的形式:
1 | <memory unit='GiB'>2</memory> |
这只是展示内存容量关系的示意片段,不是可以直接替换现有虚拟机配置的完整 XML。
实际配置时,应结合现有虚拟机的机器类型、NUMA 布局、内存插槽及 libvirt 版本检查,避免覆盖已有配置。
定义 virtio-mem 设备
接下来创建一个设备 XML 文件:
1 | vim virtio-mem.xml |
设备定义可以参考以下示例:
1 | <memory model='virtio-mem'> |
这里各字段的含义如下:
| 字段 | 含义 |
|---|---|
model='virtio-mem' | 声明使用 virtio-mem 设备 |
size | 设备可提供的最大内存容量 |
node | 设备对应的虚拟 NUMA 节点 |
block | 内存调整的基本粒度 |
requested | 请求设备当前提供的内存容量 |
alias | 设备别名,方便后续指定目标设备 |
这里将设备最大容量设置为 2 GiB,内存块粒度设置为 2 MiB,初始请求容量为 0。
为什么使用 2 MiB?
在 x86_64 环境中,2 MiB 是常见的透明大页大小,也是 virtio-mem 常见的块大小选择。不过,实际值仍需要满足平台和设备的约束。
requested 设置为 0,意味着初始时不请求该设备向客户机提供额外内存。
注意,这个示例没有固定 PCI 地址。libvirt 在支持的情况下可以分配设备地址;如果需要手工指定,则应先检查现有 PCI 设备布局,避免地址冲突。
另外,node 也不能随便填写。应先查看虚拟机的 vCPU、基础内存和 NUMA 拓扑,再决定设备布局。
将设备添加到虚拟机
如果虚拟机正在运行,并且希望设备立即生效,同时保存到持久化配置,可以使用:
1 | virsh attach-device vm0 virtio-mem.xml --live --config |
其中:
--live:修改正在运行的虚拟机。--config:修改持久化配置,使变更在后续启动时保留。
如果虚拟机已经关机,可以根据当前环境使用持久化配置方式添加设备。
执行前,应确认 XML 符合当前 libvirt 版本的要求,并且虚拟机满足设备热插拔条件。
设备添加成功,不等于客户机已经获得了额外内存。 客户机内核还需要识别设备,并将新加入的内存上线,才能正常使用。
客户机端:为什么要关注 online_movable?
使用 virtio-mem 前,需要配置客户机内核如何处理热插入内存。
RHEL 默认可以通过 udev 规则管理内存上线,但使用 virtio-mem 时,应按照 RHEL 10 的相关文档,将内存上线策略配置到内核。
常见策略包括:
| 策略 | 特点 |
|---|---|
online_movable | 将热插入内存放入可移动内存区域,更有利于后续热拔 |
online_kernel | 热插入内存可以供内核和其他分配使用,但不保证后续能够全部热拔 |
auto-movable | 根据相关配置,在可移动与普通内存区域之间分配 |
如果希望后续能够比较可靠地缩容,可以优先测试 online_movable。
在客户机中执行:
1 | grubby --update-kernel=ALL \ |
然后重启客户机,使内核启动参数生效。
重启后检查:
1 | cat /proc/cmdline |
重点确认内核启动参数已经包含:
1 | memhp_default_state=online_movable |
并检查系统报告的内存上线策略。
如果实际输出与预期不一致,应进一步检查 RHEL 版本、内核启动参数以及相关 udev 规则,不要仅凭某一个文件的输出就认定配置已经全部生效。
还要注意,online_movable 并不保证任何情况下都能立即热拔。如果内存区域分布、NUMA 布局或客户机内存使用情况不满足要求,缩容仍然可能失败。
动态扩容:请求增加 512 MiB
设备添加完成后,就可以尝试调整 virtio-mem 的请求容量。
先检查命令支持的参数:
1 | virsh update-memory-device --help |
如果虚拟机只有一个 virtio-mem 设备,可以执行:
1 | virsh update-memory-device vm0 --alias ua-virtiomem0 --requested-size 512MiB |
这里将设备请求容量设置为 512 MiB。
如果虚拟机有多个 virtio-mem 设备,应使用对应的设备别名或 NUMA 节点,避免修改错设备。
另外,请求容量必须满足设备的块大小要求,不能超过设备的最大容量。
执行后,先在宿主机查看虚拟机的实际配置:
1 | virsh dumpxml vm0 |
重点关注 virtio-mem 设备中的 requested 和 current 字段。
其中:
requested表示请求提供的内存容量。current表示设备当前实际提供的内存容量。
这两个值可能暂时不同。
然后进入客户机检查:
1 | free -h |
free -h 可以查看客户机看到的内存总量、已用内存和可用内存;lsmem 则可以帮助观察内存块范围和在线状态。
如果请求容量已经增加,但客户机没有看到相应的可用内存,就需要进一步检查设备状态和内存上线配置。
动态缩容:把请求容量调回 0
当业务不再需要额外内存时,可以将请求容量调回零:
1 | virsh update-memory-device vm0 \ |
这表示请求 virtio-mem 不再向客户机提供额外内存。
但这里有个关键点:请求缩容不等于缩容一定成功。
宿主机和客户机需要完成相应的内存热拔流程。只有当客户机能够释放相应内存块,设备才能真正减少当前提供的内存容量。
如果客户机中的内存无法移动,或者系统配置不满足要求,就可能无法完成缩容。
因此,执行命令后不要只看命令是否返回成功,还应该再次检查:
1 | virsh dumpxml vm0 |
以及客户机中的:
1 | free -h |
确认设备的 current 容量是否已经下降,客户机看到的内存是否发生相应变化。
如果缩容失败,应先排查内存上线策略、内存使用情况和设备状态,而不是反复执行缩容命令。
最后还有一点:如果要从正在运行的虚拟机中完全移除 virtio-mem 设备,通常需要先将该设备的请求容量调为零,再根据当前环境支持的方式执行设备移除操作。减少内存容量与移除设备,是两件不同的事。
最后做一次体检
前面讲了设备、TuneD、磁盘调度器和动态内存。真正落地时,可以先从下面这组命令开始。
客户机中执行
1 | # 查看当前 TuneD 配置 |
如果你的虚拟磁盘是 /dev/sda,请将上面的 /sys/block/vda/ 路径替换为实际设备路径。
其中,lsmem 和内存上线策略检查主要用于配置了热插入内存或 virtio-mem 的场景,不是所有虚拟机都必须执行。
宿主机中执行
1 | # 查看虚拟机状态 |
如果配置了设备别名,也可以直接在 XML 中搜索对应的别名。
这些命令可以帮助你确认配置,但还不能代替性能测试。
调优前后怎么比较?
如果要判断调优有没有效果,最好在修改前后使用相同的测试条件。
| 调优方向 | 建议观察的指标 |
|---|---|
| CPU | CPU 使用率、vCPU 调度等待、业务响应时间 |
| 内存 | 可用内存、换页活动、NUMA 访问情况 |
| 磁盘 | IOPS、吞吐量、平均延迟、p95/p99 延迟 |
| 网络 | 吞吐量、丢包、重传、软中断和宿主机 CPU |
| virtio-mem | 请求容量、实际容量、客户机可用内存、缩容是否完成 |
如果测试磁盘性能,可以使用 fio 构造符合业务特点的测试负载;如果测试网络,可以使用 iperf3。
需要注意,测试参数必须与实际业务场景匹配。顺序读写性能好,不代表随机读写一定好;平均延迟下降,也不代表高分位延迟一定下降。
涉及生产环境时,应先在测试虚拟机上验证,避免为了测试性能影响正在运行的业务。
总结
虚拟机性能优化,并不是找到一个参数,把它改成某个值就结束了。
如果设备使用了不合适的模拟模型,可以优先评估 VirtIO;如果客户机采用了不合适的系统配置,可以先尝试 virtual-guest;如果怀疑磁盘 I/O 调度带来了额外开销,可以测试 none,但不要忽略宿主机后端。
如果业务对内存容量的需求变化很大,则可以进一步评估 virtio-mem,让虚拟机在满足自身内存需求的同时,更灵活地利用宿主机资源。
最后再强调一句:先确认瓶颈,再改配置,最后用数据判断是否真的有效。
这套思路不仅适用于 RHEL 虚拟机,也适用于日常的 Linux 性能排查。
