虚拟机变慢,这是每个用 KVM 的人都会遇到的经典问题。前几天刚帮一个朋友排查 Rocky Linux 10 的虚拟机性能问题,现象很典型:系统装好之后操作明显卡顿,在虚拟机里执行dnf makecache都要等十几秒,编译一个小工具时负载直接拉满。朋友的第一反应是“配置不够”,准备加 CPU、加内存。我上机看了一眼,问了一句:“你这虚拟机,确定是用 KVM 加速跑的吗?”
这个问题的答案,比加多少核都重要。
这篇文章就从“虚拟机很慢”这个最常见不过的排障场景说起。我们会先弄清 KVM、QEMU、libvirt 这几个概念的边界,再给出 6 个可以在宿主机和客户机两侧执行的确认命令,最后聊一聊如果确实没有跑在 KVM 上,要怎么修复,以及日常运维时怎么避免踩进同一个坑。
1. 这篇文章真正要解决的问题:虚拟机慢,加配置为什么没用
很多人遇到虚拟机慢,第一反应就是加 CPU、加内存、换 SSD。这些操作在某些情况下确实有效,但有一个前提:虚拟机必须已经运行在硬件虚拟化加速之上。
如果加速层没有生效,再加配置也只是让一台“软件模拟出来的 CPU”跑得快一点点,本质上还是在用宿主机的 CPU 去逐条解释、翻译客户机的指令。这种模式的专业叫法是 TCG(Tiny Code Generator),也就是 QEMU 的纯软件模拟模式。它和 KVM 硬件虚拟化之间的性能差距不是百分之二三十,而是几倍甚至十几倍。
所以,排障的顺序应该反过来:先确认虚拟化加速是否真正生效,再去考虑调整资源规格。确认“虚拟机真的跑在 KVM 上”,是整个排障链路里的第一块地基。
这篇文章适合这样几类读者:
- 在服务器上用 KVM/QEMU/libvirt 管理 Linux 虚拟机,尤其是 Rocky Linux、CentOS Stream 这类 RHEL 系系统的运维和开发者;
- 遇到虚拟机性能异常,怀疑是配置不足,但调整后效果不明显的人;
- 刚开始接触 KVM,想搞清楚“虚拟机”和“模拟器”之间区别的新手。
读完这篇文章,你会得到一套可以直接执行的确认命令清单,以及一份对应的修复思路。
2. KVM、QEMU、libvirt:排障前需要弄清的三层关系
在排查之前,建议先把三个经常混着说的词分开。
KVM(Kernel-based Virtual Machine)是 Linux 内核里的一个虚拟化模块。它把 CPU 的硬件虚拟化能力(Intel VT-x 或 AMD-V)暴露给用户态程序使用。KVM 本身不是一套完整的“虚拟机软件”,它更像是一个驱动、一个能力接口。你打开/dev/kvm,就拿到了利用 CPU 硬件虚拟化指令的入口。
QEMU(Quick Emulator)是一个功能非常完整的模拟器。它既能做纯软件模拟,也能配合 KVM 做硬件加速。当它配合 KVM 时,CPU 指令不再需要软件翻译,而是直接由物理 CPU 的虚拟化扩展来执行,性能开销会被压缩到很小。当它单独运行时,就只能走 TCG 软件模拟,这也是性能瓶颈最常出现的地方。
libvirt是管理虚拟机的 API 和守护进程。virsh、virt-manager、virt-install这些工具都建立在 libvirt 之上。它做的事是把 QEMU/KVM 的启动参数、磁盘、网络、VM 定义文件统一管理起来,给运维人员一个更友好的操作层。
这三个东西的分工可以用下面这张表来看:
| 组件 | 角色 | 启动方式 | 排障中关注什么 |
|---|---|---|---|
| KVM | 内核模块,提供硬件加速能力 | 由内核加载,模块名为 kvm_intel / kvm_amd | 模块是否加载,/dev/kvm 是否存在 |
| QEMU | 用户态模拟器,虚拟机的“进程本体” | 手动 qemu 命令,或由 libvirt 拉起 | 进程参数里有没有启用 accel=kvm |
| libvirt | 管理服务,统一管理 VM 生命周期 | systemd 服务 libvirtd | 服务是否正常,VM XML 定义里的 domain type |
如果你是通过virsh或virt-manager管理虚拟机,那么背后真正跑起来的进程仍然是 QEMU,但它的加速后端可能是 KVM,也可能是 TCG。这两种情况下的表现和定位方式完全不同。
理解这一层关系之后,很多排障思路就清晰了:你以为是虚拟机配置问题,实际上可能是虚拟化实现方式的问题。
3. 提前判断:宿主机是否具备 KVM 硬件虚拟化条件
在虚拟机确实很慢的众多原因里,有一类特别隐蔽:宿主机本身不支持或者没有开启硬件虚拟化,虚拟机被迫走了纯软件模拟。
判断宿主机是否具备条件,一般分三步。
第一步:检查 CPU 是否支持虚拟化技术。
在 x86 架构下,需要确认 CPU 标志里有vmx或svm:
grep -Eo '(vmx|svm)' /proc/cpuinfo | sort -u如果输出包含vmx,说明 Intel CPU 支持 VT-x;如果输出包含svm,说明 AMD CPU 支持 AMD-V。如果没有任何输出,说明 CPU 要么不支持硬件虚拟化,要么在 BIOS/UEFI 中被关闭了。
第二步:检查 KVM 内核模块是否已加载。
lsmod | grep kvm正常加载时应看到类似输出:
kvm_intel 262144 7 kvm 786432 1 kvm_intel如果是 AMD 机器,第一行会是kvm_amd。如果你看到模块已加载,但引用计数为 0,说明当前没有虚拟机正在使用这个模块,可以继续检查后面的环节。
第三步:检查 /dev/kvm 设备节点是否存在且可访问。
ls -l /dev/kvm正常情况下会看到:
crw-rw-rw- 1 root kvm 10, 232 12月 5 14:22 /dev/kvm如果文件不存在,说明 KVM 驱动没有被正确加载,或者内核缺少对应支持模块。如果文件存在但当前用户无权限,则需要把用户加入kvm组。
这三个前置条件都满足之后,才能继续讨论“虚拟机的加速器是否生效”这个更深层的问题。很多人一开始就跳到了 QEMU 启动参数,却没有注意到宿主机本身就缺少vmx/svm标志,这种情况后面怎么调优都没有意义。
4. 核心排障:确认虚拟机是否真的跑在 KVM 上
下面进入正题。假设你已经有一台运行 Rocky Linux 10 的虚拟机,并且感觉很慢,现在要判断它到底跑在 KVM 硬件加速上,还是 QEMU 纯软件模拟上。
推荐按下面 6 个命令依次确认。
命令 1:从宿主机侧看 QEMU 进程的实际参数。
如果虚拟机是手动用 qemu 命令启动的,直接看进程参数最直观:
ps -ef | grep qemu如果命令行里包含-accel kvm或-machine accel=kvm,说明启动时指定了 KVM。如果命令行里没有这两个参数,甚至同时出现了-accel tcg,那就可以基本判定它没有跑在 KVM 上。
如果虚拟机是通过 libvirt 管理的,进程命令行动辄几百个字符,比较难一眼看清,可以改用virsh dumpxml来确认。
命令 2:通过 libvirt 查看虚拟机的 XML 定义。
virsh dumpxml <虚拟机名称> | head -n 3重点看 root 标签里的type属性。如果第一行是<domain type='kvm'>,说明 libvirt 在创建这台虚拟机时使用了 KVM 域。如果第一行是<domain type='qemu'>,说明它走的是纯软件模拟。
命令 3:在 libvirt 管理的虚拟机上,直接观察 QEMU 进程参数。
ps -ef | grep qemu | grep <虚拟机名称>一条条看命令行里的-accel参数即可。现代 libvirt 版本通常会在 QEMU 命令行里明确带出-accel kvm或-machine accel=kvm。这个信息比 XML 更贴近真实运行状态。
命令 4:从客户机内部查看虚拟化类型。
如果宿主机侧没法完全确定,直接进虚拟机系统内部看。Rocky Linux 10 这类使用 systemd 的系统,自带一个很实用的命令:
systemd-detect-virt- 输出
kvm,说明客户机识别到自己运行在 KVM 之上; - 输出
qemu,说明它可能跑在纯软件 QEMU 模拟下; - 输出类似
microsoft或vmware,说明它其实跑在另一层虚拟化里,这种情况需要重新向上排查。
这个命令的原理是检查 CPUID 和 DMI 信息,排障时很顺手,不需要额外安装依赖。
命令 5:用 lscpu 检查客户机里的虚拟化标识。
lscpu | grep -i -E 'hypervisor|virtualization'如果看到类似Hypervisor vendor: KVM的输出,基本可以确认客户机是 KVM 客户机。如果看到Hypervisor vendor: QEMU,则需要进一步分辨是 TCG 还是 KVM,因为某些配置下 QEMU 也会设置这个标识。
命令 6:在客户机里执行 virt-what。
virt-what是一个专门用来识别虚拟化环境的小工具,比systemd-detect-virt判断得更细。在客户机里执行:
dnf install -y virt-what virt-what输出kvm表示运行在 KVM 之上。如果同时有多行输出,比如kvm加qemu,说明是 QEMU 仿真出的 KVM 兼容环境,这种环境通常也是 TCG 或嵌套虚拟化,性能仍需要进一步观察,并不能和物理机上的原生 KVM 划等号。
这 6 个命令可以分成两层:前 3 个在宿主机侧查,后 3 个在客户机侧查。建议排障时先从宿主机侧开始,因为宿主机上的进程参数和 XML 定义是可信度最高的一手信息。
5. 完整示例:从零创建 Rocky 10 虚拟机并确认它跑在 KVM 上
前面说了不少判断方法,这一节把它串成完整流程。我们用 libvirt 的virt-install工具创建一台 Rocky Linux 10 虚拟机,然后在宿主机和客户机两侧分别验证它确实跑在 KVM 上。
5.1 安装虚拟化相关软件包
在 Rocky Linux 10 上,安装 KVM 相关组件最直接的方式是安装虚拟化分组包:
dnf install -y @virtualization也可以按需安装最小集合:
dnf install -y qemu-kvm libvirt virt-install virt-manager安装完成后启动 libvirtd:
systemctl enable --now libvirtd systemctl status libvirtd如果当前用户不在 kvm 组中,建议追加进去,避免后续无权限访问 /dev/kvm:
usermod -aG kvm $USER newgrp kvm5.2 准备 Rocky Linux 10 安装镜像
这里以 Rocky Linux 10 的 ISO 为例,把镜像放到一个固定目录:
mkdir -p /data/iso cd /data/iso下载链接建议从 Rocky Linux 官网或者离你最近的镜像站获取,下面命令中的Rocky-10-*.iso需要替换成你实际下载的文件名。要注意的是,目录下只能有一个匹配该通配符的 ISO 文件,否则virt-install会因参数歧义而报错。
5.3 创建 KVM 虚拟机
使用virt-install创建虚拟机,参数里的关键点是--virt-type kvm:
virt-install \ --name rocky10-test \ --memory 4096 \ --vcpus 4 \ --disk path=/var/lib/libvirt/images/rocky10-test.qcow2,size=40,format=qcow2 \ --cdrom /data/iso/Rocky-10-*.iso \ --os-variant rocky10 \ --network network=default \ --graphics vnc \ --virt-type kvm这里有一个很容易被忽略的细节:如果宿主机满足 KVM 条件,--virt-type kvm会让virt-install创建<domain type='kvm'>的虚拟机;但如果宿主机不满足 KVM 条件,安装过程可能会回退到--virt-type qemu,导致你的虚拟机最终跑在软件模拟上。这个现象非常隐蔽,因为安装过程并不会明显报错,你只会觉得后续使用很慢。
另外,--os-variant rocky10这个参数并不是所有 libvirt 版本都能识别。如果你的环境提示不支持该变体名,可以先执行下面命令查询本机支持的 osinfo 列表:
osinfo-query os | grep -i rock选择最接近的版本名称即可。如果完全没有匹配项,也可以去掉--os-variant参数,安装到系统类型选择步骤时手工指定。
5.4 宿主机侧验证
虚拟机创建并完成系统安装后,回到宿主机执行:
virsh list --all virsh dumpxml rocky10-test | grep '<domain type='预期输出是:
<domain type='kvm'>如果看到的是<domain type='qemu'>,说明这台虚拟机没有跑在 KVM 上,需要回到第 6 节修复。
再检查 QEMU 进程参数:
ps -ef | grep qemu | grep rocky10-test命令行里如果包含-accel kvm,说明虚拟机确实启用了 KVM 加速。
5.5 客户机侧验证
进入 Rocky Linux 10 虚拟机内部,执行:
systemd-detect-virt预期输出是kvm。如果想更精确地识别,再执行:
dnf install -y virt-what virt-what预期输出同样包含kvm。
到这里,一台虚拟机从创建到验证的完整流程就走完了。你会发现,整个流程里最重要的事情,就是“创建时显式指定加速器”和“创建后主动验证”。
6. 如果确实没跑在 KVM 上,怎么修复
如果经过前面的检查,确认虚拟机真的跑在 TCG 或纯 QEMU 模拟模式,要根据不同场景选择修复方式。
场景一:手动用 qemu 命令启动的虚拟机,启动参数里少了 KVM。
这种情况最简单,把启动命令里的-machine accel=tcg改成-machine accel=kvm,或者加上-enable-kvm,然后重启虚拟机。注意,前提是宿主机确实具备 KVM 条件,否则加参数会直接报错,无法启动。
场景二:libvirt 管理的虚拟机,XML 里 domain type 是 qemu。
可以用virsh edit修改虚拟机定义,把 root 标签里的type='qemu'改成type='kvm':
virsh edit <虚拟机名称>保存后重启虚拟机。这里要注意,只改type属性还不够,建议同时检查 XML 里是否有 CPU 相关的合理配置,比如是否保留了原生的 host-passthrough 或 host-model,避免客户机里的 CPU 特性发生变化,导致应用兼容问题。修改 XML 属于生产环境变更,操作前建议先备份原文件:
virsh dumpxml <虚拟机名称> > /path/to/backup/rocky10-test.xml场景三:宿主机本身是一台虚拟机,属于嵌套虚拟化。
如果你的“宿主机”是云服务器或者 VMware 里的虚拟机,那么 KVM 能否使用,取决于上层虚拟化是否透传了虚拟化指令。比如 VMware 里需要开启“向客户机操作系统公开硬件辅助的虚拟化”,云厂商则要看实例类型是否支持嵌套虚拟化。这种情况不建议硬开 KVM,更稳妥的做法是更换支持嵌套虚拟化的云主机,或者改用物理机。
无论哪种场景,修复后都要回到第 4 节里的验证命令重新确认,尤其是virsh dumpxml里的 domain type,以及客户机内部的systemd-detect-virt。已经安装好的客户机系统从 TCG 切到 KVM,一般不需要重装,Rocky Linux 10 在 KVM 场景下默认使用的 virtio 驱动通常可以无缝工作,但生产环境操作仍然要准备好回滚方案。
7. 常见问题:虚拟机很慢的典型原因排查表
把“虚拟机很慢”当成一个综合症状来看,KVM 加速缺失只是其中一个原因。这里整理一份常见的排查表,方便你在现场快速对照。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 虚拟机整体反应迟钝,客户机里 CPU 持续高负载 | 没跑在 KVM 上,走了 TCG 软件模拟 | 宿主机执行 ps 查看 qemu 参数;客户机执行 systemd-detect-virt | 创建时指定 --virt-type kvm,或修复 XML domain type |
| 安装系统时就非常慢,进度条几乎不动 | 创建 VM 时回退到 qemu 类型,或宿主机缺少 /dev/kvm | ls -l /dev/kvm;virt-install 加 --virt-type kvm | 确保宿主机硬件虚拟化开启,重新创建或编辑 VM 定义 |
| 磁盘 IO 慢,客户机磁盘显示为 /dev/sdX 而不是 /dev/vdX | 磁盘总线用的是 IDE 或 SATA,而不是 virtio | 客户机执行 lsblk -d -o name,tran | 磁盘总线改用 virtio,必要时做磁盘离线迁移 |
| 网络吞吐低,延迟抖动明显 | 虚拟网卡模型为 e1000 或 rtl8139,而非 virtio-net | 客户机执行 ethtool -i 网卡名 | 把网卡模型改为 virtio |
| virsh dumpxml 显示 type='qemu',但不想重装系统 | 创建时未显式指定 --virt-type kvm | 检查宿主机 CPU 是否支持 vmx/svm | 通过 virsh edit 修改 domain type,并确认 /dev/kvm 权限 |
| CPU 频率明显低于标称频率,单核跑分偏低 | 宿主机 CPU 节能策略或客户机 CPU governor 为 powersave | 客户机查看 cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor | 使用 performance governor,或调整宿主机 BIOS 电源策略 |
| 多核性能上不去,单核却正常 | vCPU 被限制在同一个物理核心上,或者拓扑不匹配 | virsh vcpuinfo 查看 vCPU 绑定情况 | 合理配置 vCPU 和 NUMA,必要时做 vCPU 绑定分散到不同核心 |
这张表覆盖了比较常见的几种“慢”的表现,它们在症状上很相似,但定位路径完全不同。遇到问题先不要急着加资源,先按表格里的排查方式走一遍,往往能省下大量时间。
8. 最佳实践:让 KVM 虚拟机保持高效和稳定
这一节整理几条对 Rocky Linux + KVM 场景最实用的长期经验。
8.1 创建虚拟机时养成显式指定类型的习惯
无论使用virt-install还是virt-manager,都要在命令或界面上确认虚拟化类型是 KVM。不要让系统自动判断,自动判断在条件不满足时会悄悄回退到 QEMU,这正是很多性能问题的来源。
建议在安装脚本里加一条宿主机预检命令:
#!/bin/bash # 文件路径:/usr/local/bin/check-kvm-host.sh if [ ! -e /dev/kvm ]; then echo "警告:/dev/kvm 不存在,无法使用 KVM 加速" exit 1 fi grep -Eo '(vmx|svm)' /proc/cpuinfo | sort -u | grep -q . || { echo "警告:CPU 未开启硬件虚拟化" exit 1 } echo "KVM 环境检查通过"8.2 磁盘和网络设备尽量使用 virtio
KVM 场景下,virtio 半虚拟化设备的性能通常明显优于 IDE 和 e1000。创建虚拟机时,磁盘总线指定bus=virtio,网络模型指定model=virtio。Rocky Linux 10 的内核自带了 virtio 驱动,默认安装后就能识别,不需要额外加载模块。
排障时如果发现客户机内部磁盘是/dev/sda而不是/dev/vda,通常说明磁盘总线用的是 IDE 或 SATA。这种情况在系统已装好的情况下迁移比较麻烦,需要离线操作,评估好业务影响再动。
8.3 一定要给客户机配置时间同步
虚拟机的性能问题往往和时钟漂移混在一起。KVM 客户机默认的时钟源通常是 kvm-clock,不需要额外配置,但要确保系统时间同步服务开启。在 Rocky Linux 10 中,建议使用 chrony:
dnf install -y chrony systemctl enable --now chronyd timedatectl set-ntp true时间不同步在负载高时会带来系统调用层面的异常等待,现象上很像性能问题,但本质上是时钟问题。
8.4 用监控数据代替感觉
遇到“虚拟机很慢”的时候,不要凭经验猜。先采集数据:
- 宿主机侧:
virsh vcpuinfo、virt-top、sar。 - 客户机侧:
top、iostat、vmstat。
用数据区分是 CPU 慢、磁盘慢还是网络慢。KVM 排障有个特点:问题往往隔离在某一层。CPU 慢大概率是加速器问题,磁盘慢大概率是驱动或存储池问题,网络慢大概率是网卡模型性能问题。只有先锁定层级,才能避免做无用功。
8.5 维护一份最小性能基准
在日常运维中,建议给每类虚拟机保存一份“创建时关键参数”的文档或者脚本,特别是--virt-type、CPU 型号、磁盘总线、网卡模型这几个参数。这样下次再遇到慢的问题,可以快速对照,“是不是本来就没跑在 KVM 上”这个问题只需要 10 秒就能回答。
9. 总结与进一步排查方向
回到文章开头那个场景。朋友的 Rocky Linux 10 虚拟机最后确认是在创建时没有显式指定--virt-type kvm,系统在缺少检测的情况下回退到了 QEMU 纯软件模拟。调整成 KVM 类型后,编译任务耗时从十几分钟降到两三分钟,这比单纯加核加内存有效得多。
所以,当你的虚拟机再次变慢时,先问自己三个问题:
- 宿主机支持硬件虚拟化吗?
/dev/kvm存在吗?- QEMU 进程真的用了 KVM 加速吗?
这三个问题确认完之后,才轮到加配置、调优驱动、优化存储这些后续动作。
进一步可以研究的方向包括:KVM 的 CPU 拓扑与 NUMA 绑定、virtio-blk 与 virtio-scsi 的选择、libvirt 的 cgroup 资源限制,以及嵌套虚拟化场景下的性能损耗评估。对于 Rocky Linux 10 这类基于较新内核的系统,KVM 虚拟化的开箱体验已经足够好,但真正理解加速器的原理,才能在排障时不走弯路。
建议把上面的确认命令和排查表收藏备用,下次遇到“虚拟机很慢”的问题,直接从 KVM 确认开始。