虚拟机变慢先查KVM:Rocky 10性能排障指南
2026/9/8 8:24:07 网站建设 项目流程

手里的 Rocky 10 虚拟机最近开始卡得离谱:CPU 在宿主机里动不动就 100%,Guest 系统里打开一个配置界面都要等好几秒,应用更是一顿一顿的。一开始我以为是资源不够,先后加了 vCPU 和内存,结果几乎没有改善。后来抱着试试看的心态,进虚拟机里执行了一个命令,才确认一个让人哭笑不得的事实:这台虚拟机根本没有跑在 KVM 上,而是退回了纯软件模拟。

虚拟机的“慢”有很多种原因,但如果你问我在 Rocky 10 这种 Linux 虚拟化环境里排障,第一件事永远不是调参数,也不是加资源,而是先确认虚拟机到底有没有真正使用 KVM 加速。因为 KVM 和 QEMU 软件模拟之间的性能差距,不是百分之二十三十的问题,而是数量级的问题。本文就围绕这个判断,把确认方法、常见原因、修复路径和长期预防一次说清楚。

1. 虚拟机慢,为什么第一步要先问“它真的是 KVM 吗”

1.1 一个典型的“慢”现场

先还原一个我遇到的场景。

某台测试机起初在物理服务器上跑得很正常,后来因为机房调整,用备份镜像迁移到了另一台机器。迁移之后,虚拟机里的 Rocky 10 开始出现明显的卡顿。打开终端窗口有延迟,编译一个项目的时间比以前长了三倍,查看系统监控时始终能看到一个 QEMU 进程的 CPU 占用很高。

这时常见的排障思路是什么?先看内存够不够,再看磁盘 I/O,然后逐个排查 Guest 内部的进程,最后甚至会把压力归到网络或负载均衡上。但如果你在宿主机上用top看,会发现占用 CPU 的是qemu-system-x86_64进程本身,而这个进程正是虚拟机所有资源和指令的执行者。也就是说,性能瓶颈极有可能发生在虚拟化层面,而不是 Guest 内部。

如果这时候不去确认虚拟化加速是否生效,后面很多优化动作都是无效的。加 CPU、加内存、调节 I/O 调度,可能都解决不了根本问题。因为问题的本质不是“资源不够”,而是“指令执行方式从硬件加速退回到了软件翻译”。

1.2 慢的本质:KVM 和软件模拟差在哪儿

要理解为什么必须先确认这一点,得先搞清楚 KVM 和 QEMU 的关系。

KVM 是 Linux 内核里的虚拟化模块,它利用 CPU 的硬件虚拟化扩展(Intel 的 VT-x 或 AMD 的 SVM),把 Guest 中的大部分特权指令直接交给物理 CPU 执行。QEMU 则负责模拟设备、管理内存、处理 I/O 等,但它本身在指令执行层面有两个模式:

  • /dev/kvm存在并且 QEMU 以-accel kvm方式启动时,Guest CPU 指令会尽可能直接跑在物理 CPU 上,这叫硬件辅助虚拟化。
  • 当没有 KVM 加速时,QEMU 会退回到 TCG(Tiny Code Generator)纯软件模拟,把 Guest 的二进制指令翻译成宿主机指令再执行。这种翻译过程有一个比较直观的表现:CPU 会一直忙,但实际进度很慢。

用一个不太严谨但容易理解的类比:KVM 加速有点像一个掌握同声传译能力的翻译,说话人讲一句,他几乎同步翻译一句;而 TCG 则是先录下整段话,再逐句查字典翻译,过程中还要反复确认是否理解正确。翻译能力的差距直接体现在交流效率上。

具体到虚拟机上,普通工作负载在 TCG 模式下性能下降一两倍很正常,某些 CPU 密集型的计算任务下降得更多。这不是“慢一点”,而是“不可用”。所以虚拟机变慢时,第一步先确认“是不是跑在 KVM 上”,是整个排障流程的地基。

2. 三种确认方式:从宿主到 Guest,从进程到日志

确认一个虚拟机是否真的跑在 KVM 上,比很多人想象中简单。不需要复杂工具,只要按宿主、配置、Guest 三层来验证。

2.1 宿主侧:先看 /dev/kvm 和 QEMU 进程参数

第一种方式是在宿主机上直接看虚拟机的实际运行参数。KVM 加速的前提是宿主机存在/dev/kvm设备节点,并且内核已经加载了对应的 KVM 模块。

# 查看 /dev/kvm 设备是否存在 ls -l /dev/kvm # 查看 KVM 内核模块是否加载 lsmod | grep kvm # 查看 CPU 是否支持虚拟化扩展(物理机或已开启嵌套的虚拟机) grep -E 'vmx|svm' /proc/cpuinfo

正常情况下,/dev/kvm应该是一个字符设备,权限通常属于kvm组。lsmod会看到kvm_intelkvm_amd模块,以及kvm模块本身。

接下来看 QEMU 进程是怎样启动的。如果你用的是 libvirt 管理虚拟机:

# 查看所有虚拟机列表 virsh list --all # 查看正在运行的 QEMU 进程的完整命令行 ps -ef | grep qemu-system

重点关注命令行里有没有这些东西:

  • -machine accel=kvm
  • -cpu host
  • 或者在 QEMU 参数中出现accel=kvm

如果看到的是-accel tcg,或者没有显式的kvm加速参数,那就要高度怀疑这台虚拟机和 KVM 加速没有关系了。

2.2 Guest 侧:systemd-detect-virt 和 CPU 型号的真相

在虚拟机内部验证是更直接的判断方式。Rocky 10 使用 systemd,所以天然有一个命令可以用。

systemd-detect-virt

如果虚拟机确实跑在 KVM 加速上,这条命令通常会输出kvm。如果输出的是qemu,说明它运行在 QEMU 模拟环境里,但不能确定是否硬件加速;如果输出none,那就是物理机或某些特殊虚拟化环境。

再配合看 CPU 信息:

lscpu

在 KVM 加速且 CPU 模式为host-passthroughhost-model的情况下,Guest 里的 CPU 型号会和宿主机比较接近,通常能看到真实 CPU 系列名。如果看到QEMU Virtual CPU version 2.5+这类名字,多半是用了 QEMU 内置的虚拟 CPU 模型,性能会低于真实 CPU。虽然这并不代表一定没有 KVM 加速,但至少说明配置没有把加速能力充分发挥出来。

2.3 配置侧:libvirt XML 里的虚拟化类型

如果你使用virsh管理虚拟机,配置信息保存在 domain XML 里。里面最关键的一行是<domain>标签的type属性。

# 查看虚拟机的 XML 配置 virsh dumpxml <虚拟机的名字>

正常情况下,KVM 加速配置应该是:

<domain type='kvm'>

如果显示是:

<domain type='qemu'>

那就说明 libvirt 会默认使用纯 QEMU 模式启动虚拟机,也就是 TCG 软件模拟。这是最容易识别的信号之一。

同样重要的是 XML 里的 CPU 配置:

<cpu mode='host-passthrough'/>

或:

<cpu mode='host-model'/>

如果看到的是:

<cpu mode='custom' match='exact'> <model fallback='allow'>qemu64</model> </cpu>

那么即使已经用了 KVM 加速,CPU 指令集也会被限制在一个非常保守的兼容模型上,部分对指令集敏感的工作负载照样会变慢。

3. 没有跑在 KVM 上的常见原因和修复路径

确认了“没有真正跑在 KVM 上”之后,接下来就要弄清楚为什么。根据经验,最常见的原因集中在硬件、内核模块、权限和配置这四个层面。

3.1 硬件虚拟化没有打开或不可用

KVM 必须依赖 CPU 硬件虚拟化扩展。如果宿主机是物理机,要先确认 BIOS/UEFI 里是否开启了 Intel VT-x 或 AMD-V。不同厂商的主板菜单不一样,但关键词通常有Intel Virtualization TechnologySVM Mode等。确认方法是在宿主机上执行:

grep -E 'vmx|svm' /proc/cpuinfo

有输出说明 CPU 支持且当前环境可用。没有输出,就需要进 BIOS 开启,开启后重启再检查。

还有一种情况容易被忽略:宿主机本身也是一个虚拟机。比如在这台虚拟机里再跑 Rocky 10 虚拟机,而底层没有开启嵌套虚拟化,那么上层 Guest 自然就用不了 KVM。这种场景下要先去底层宿主确认嵌套虚拟化参数,而不是在 Guest 里反复折腾。

3.2 KVM 模块没加载、被版本升级覆盖

如果 CPU 支持虚拟化,但执行lsmod | grep kvm没有输出,说明内核模块没有加载。可以先手动加载再确认:

# Intel CPU modprobe kvm_intel # AMD CPU modprobe kvm_amd

加载成功后再次执行lsmod | grep kvmls -l /dev/kvm。如果加载时报错,查看内核日志:

dmesg | grep -i kvm

常见报错信息包括 “module is already loaded” 或 “Operation not supported” 等。其中Operation not supported往往说明当前环境没有可用硬件虚拟化能力。在 Rocky 10 这类系统上,内核一般会携带 KVM 模块,但不会保证一定会自动加载。为了避免重启后回到“无加速”状态,可以把对应的模块写入自动加载配置:

# /etc/modules-load.d/kvm.conf kvm kvm_intel

等下次机器重启后,再检查模块是否自动加载。

3.3 权限和嵌套虚拟化配置

有时候/dev/kvm设备存在,但当前运行虚拟机的用户没有访问权限,导致 libvirt 在启动时无法使用 KVM 加速。这种问题通常表现为创建虚拟机失败,或者 libvirt 自动回退到 QEMU 模式。

解决方法是把运行虚拟机的用户加入kvm组:

usermod -aG kvm your-user

新添加的组成员关系需要重新登录才能生效。如果使用 root 运行 libvirtd,则需要确认/etc/libvirt/qemu.conf中配置的usergroup是否有权限访问/dev/kvm

嵌套虚拟化场景还要额外检查:

# 查看是否允许嵌套 cat /sys/module/kvm_intel/parameters/nested

输出为1Yy表示允许。如果为0,需要卸载模块后重新加载,并设置嵌套参数:

modprobe -r kvm_intel modprobe kvm_intel nested=1

不过在正式环境里,我更建议在引导配置中固定这个参数,避免每次手工处理。

3.4 修复后如何验证

修复不能只用一条命令确认。我习惯按这个顺序走一遍:

  1. 宿主机检查grep -E 'vmx|svm' /proc/cpuinfo是否正常。
  2. 检查lsmod | grep kvm是否已经加载。
  3. 检查ls -l /dev/kvm是否存在。
  4. 启动一台测试虚拟机。
  5. 在宿主机上用ps -ef | grep qemu-system查看 QEMU 参数是否包含accel=kvm
  6. 进入 Guest 执行systemd-detect-virt,看输出是否为kvm

这里有一个容易被忽略的建议:不要一次性把所有虚拟机都启动。先挑一台测试机验证,确认加速已经生效后,再批量恢复业务虚拟机。否则如果配置本身有问题,可能所有虚拟机都会以错误的模式运行很长时间。

4. 一套可复用的“KVM 加速自检”流程

前面这些命令每次逐步执行也能用,但排障最怕的是漏掉某一层。把检查步骤固化成一个脚本,可以明显减少人为遗漏。

4.1 快速检查脚本设计思路

脚本只需要做“检查”和“提示”,不需要直接修改系统。原则是尽早暴露出问题,而不是自动修复。下面是一个适合作为参考的脚本结构:

#!/usr/bin/env bash echo "== 1. CPU 硬件虚拟化扩展 ==" if grep -E 'vmx|svm' /proc/cpuinfo > /dev/null; then echo "OK: CPU 支持虚拟化扩展" else echo "FAIL: 未检测到 vmx/svm,请在 BIOS/固件中开启,或确认嵌套虚拟化已开启" fi echo "" echo "== 2. KVM 内核模块 ==" if lsmod | grep -q '^kvm'; then lsmod | grep '^kvm' else echo "WARN: 没有检测到 KVM 模块" fi echo "" echo "== 3. /dev/kvm 设备 ==" if [ -e /dev/kvm ]; then ls -l /dev/kvm else echo "FAIL: /dev/kvm 不存在" fi echo "" echo "== 4. QEMU 进程参数 ==" if pgrep -f qemu-system > /dev/null; then ps -ef | grep qemu-system | grep -v grep || true else echo "INFO: 当前没有正在运行的 QEMU 进程" fi

脚本本身不复杂,但它能在第一时间区分出问题在哪一层。实际使用中,可以根据业务环境补充“检查 libvirt XML”的步骤,比如用virsh dumpxml提取<domain>type属性,再判断是不是kvm

4.2 纳入日常健康检查

单次执行脚本只是排障,长期价值在于把它放进日常巡检或监控系统。

现在很多团队已经有周期监控,但监控位置往往停留在“虚拟机能不能通”、“磁盘空间是否够用”这一层。更建议至少把以下两个指标纳入检查:

  • 宿主机上是否存在应该运行但退化为 TCG 模式的虚拟机。
  • /dev/kvm 设备是否从可用变成不可用。

实现思路不算复杂:可以定期跑脚本,如果检测到“应该使用 KVM 加速,但进程参数没有包含 accel=kvm”的情况,就把警告发到告警群里。这一步真正能避免的是“偷偷回退”的坑,比如某个虚拟机从备份恢复后,配置被改写成了 qemu 模式,业务还在跑,只是慢得离谱。

4.3 边界:哪些情况确认是 KVM 但还是慢

必须说清楚,确认 KVM 加速只是排障的第一步,并不等于性能问题一定解决。现实中还有一种情况:确实跑在 KVM 上了,但虚拟机和宿主机两层配置都不合理。

比如宿主机没有配置 CPU pinning,虚拟机的 vCPU 频繁在不同物理核之间跳动,缓存亲和性变差;比如虚拟机的磁盘设备用的是纯 IDE 模拟,而不是 virtio;比如 Guest 内的 virtio 驱动没有安装完整,导致网络和磁盘吞吐上不去;再比如物理机的 NUMA 拓扑没有被 vCPU 内存亲和性利用好。

这些优化方向和“确认 KVM 加速”并不冲突,恰恰相反,只有先确认加速引擎已经点火,后面的优化才有意义。否则在 TCG 模式下调 I/O 队列长度,效果自然很差,因为瓶颈根本不在 I/O 配置上。

5. 落到长期维护:不要只做一次确认

很多虚拟机性能问题,最麻烦的不是当次排障,而是“今天修好了,下周又坏了”。KVM 加速失效这件事,很容易在几次看似无关的操作后悄悄发生。

5.1 升级后回退陷阱

Rocky 10 作为一个 RHEL 系发行版,内核和虚拟化相关组件都会持续更新。历史上并不缺少这种情况:系统更新后需要重启,重启后发现/dev/kvm没生成,或者 KVM 模块没有加载。

如果你没有配置/etc/modules-load.d/kvm.conf,也没有把检查命令放进运维手册里,那么一次内核升级就可能让所有虚拟机重新进入软件模拟模式。而且如果使用的是 libvirt 默认的type='qemu'配置,可能连报错都不会有,业务照样启动,只是性能崩了。

5.2 迁移/备份恢复后配置漂移

工程项目里最常见的问题是:虚拟机从 VMware 或 Hyper-V 迁移过来,或从备份镜像恢复后,domain XML 发生了变化。这类跨虚拟化平台的迁移很容易把<domain type='kvm'>改成<domain type='qemu'>,或把 CPU 模式改成兼容性最好的 qemu64。从另一个角度看,这其实是迁移工具在“降低兼容性风险”,目的是让镜像能启动。但如果迁移后不加确认,它就会成为性能隐患。

备份恢复也一样。如果你用virsh dumpxml保存过 XML,恢复时要检查保存的 XML 是否被修改过;如果你使用的管理平台自动生成 XML,也要防止平台默认模板覆盖原有配置。

5.3 一个简单的清单

建议把下列检查项保存成运维清单,每次迁移、升级、恢复后跑一遍:

检查项期望结果检查方式
CPU 硬件虚拟化扩展宿主机/proc/cpuinfo含 vmx 或 svmgrep -E 'vmx|svm' /proc/cpuinfo
KVM 内核模块lsmod包含 kvm 和 kvm_intel/kvm_amdlsmod | grep kvm
/dev/kvm 设备文件存在且可访问ls -l /dev/kvm
libvirt 域类型domain type 为 kvmvirsh dumpxml <vm> | grep "<domain"
QEMU 进程参数包含 accel=kvm 或 -machine accel=kvmps -ef | grep qemu-system
Guest 虚拟化检测输出 kvmsystemd-detect-virt
CPU 模型使用 host-passthrough 或 host-modelvirsh dumpxml <vm> | grep -A5 "<cpu"

这个清单看起来很简单,但每一步都在回答“虚拟机的指令到底由谁执行”这个问题。指令执行方式正确,性能优化才有讨论的基础;指令执行方式不对,再复杂的参数调优都是在错误的地基上盖楼。

下次再遇到 Rocky 10 虚拟机变慢,先别急着加 vCPU,也别急着重装系统。花两分钟确认它真的跑在 KVM 上,这个动作成本极低,但往往能帮你省下半天甚至一天的排障时间。虚拟化性能是一条很长的链路,从 CPU 指令执行,到设备模拟,再到 Guest 驱动,每一环都有优化空间。而这条链路的第一环,永远是确认加速引擎已经点火。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询