多数 KVM 故障排查根本不需要进入虚拟机控制台。libguestfs 工具可以在虚拟机关机时读取和编辑其磁盘镜像中的文件,virt-clone 能以更安全的方式复制虚拟机,virt-top 则能在宿主机上显示每个域正在做什么。无需打开一次 virt-manager,你就能提取日志、修复损坏的 sshd_config,并查明哪个虚拟机占满了磁盘。
本文介绍日常最常用的 6 个工具。所有命令均于 2026 年 11 月在 Rocky Linux 10 和 Ubuntu 24.04 宿主机上运行,下面的输出也来自这些测试。每个工具解决不同问题,因此先给出总览:
virt-top:实时查看每个虚拟机的 CPU 和内存占用。virt-cat:打印镜像中的文件。virt-ls:列出镜像中的文件。virt-edit:无需启动虚拟机即可修复文件。virt-df:查看所有虚拟机的磁盘使用量。virt-clone:安全复制虚拟机。
这些工具还有配套命令:qemu-img 和 qemu-nbd 负责镜像转换与挂载,virt-copy-in 把文件复制到镜像中,virt-sysprep 则重置克隆镜像,使其能以独立机器的身份启动。
安装 KVM CLI 工具
这套工具由三个软件包提供。由于依赖相同,两种发行版都可以用一条命令全部安装。virt-top 是独立软件包;文件工具(virt-cat、virt-ls、virt-edit、virt-df)来自 libguestfs 套件;virt-clone 则随 virt-install 一起提供。参照旧版 RHEL 指南时要注意软件包名称变化:RHEL 10 使用 guestfs-tools,Debian 和 Ubuntu 仍使用 libguestfs-tools。名称弄错正是全新 RHEL 10 主机报告“找不到 libguestfs-tools 软件包”的常见原因。
sudo dnf install -y guestfs-tools virt-top virt-install
sudo apt update
sudo apt install -y virt-top libguestfs-tools virtinst libvirt-daemon-system
确认宿主机已安装的组件。测试所用的 Rocky Linux 10 主机版本如下:
virt-top --version
virt-cat --version
virt-install --version
virt-top 和 virt-cat 会打印名称及版本,virt-install 则只打印版本号:
virt-top 1.1.2 ocaml-libvirt 0.6.1.7
virt-cat 1.54.0
5.1.0
Ubuntu 24.04 上同样的检查显示 virt-top 1.1.1、virt-cat 1.52.0 和 virt-install 4.1.0。这里还有一个打包细节:Ubuntu 上的文件工具实际上也位于 guestfs-tools 软件包中,libguestfs-tools 会把它作为依赖引入,因此前面的安装命令两种写法都能正常工作。本文其余命令在两个平台上完全一致。
Rocky Linux 主机上的版本检查结果如下。

Ubuntu 主机使用的是同一批工具中较旧的 Debian 发布版本:

两台主机都从官方软件仓库安装:Rocky 使用 AppStream,Ubuntu 24.04 使用 Ubuntu 归档,均不需要 EPEL 或 PPA。
使用 virt-top 实时监控虚拟机
virt-top 相当于虚拟机管理程序版的 top。它读取 libvirt API,每个域显示一行,包括 CPU、内存、磁盘请求和网络流量,每隔几秒刷新。因此,某个虚拟机变得异常繁忙时,屏幕会在用户提交工单之前就显示出来。针对默认的 qemu:///system 连接运行:
sudo virt-top
用于脚本和告警时,可以以脚本模式运行一次并写出 CSV。--script 完全跳过终端界面,因此在 cron 中运行也很安全。下面的命令采样 8 秒后退出:
sudo virt-top --script --csv /tmp/vt.csv --end-time +8
表头会命名所有列,每个采样行包含宿主机汇总以及运行中域的数据:
Hostname,Time,Arch,Physical CPUs,Count,Running,Blocked,Paused,Shutdown,Shutoff,Crashed,Active,Inactive,%CPU,Total hardware memory (KB),Total memory (KB),Total guest memory (KB),Total CPU time (ns),Domain ID,Domain name,CPU (ns),%CPU,Mem (bytes),%Mem,Block RDRQ,Block WRRQ,Net RXBY,Net TXBY
kvm-host01,00:43:20,x86_64,2,2,1,0,0,0,0,0,1,1,0.0,3737636,1048576,1048576,0,19,db01,0.,0.,0,0,,,,
各列数字与交互界面相同,因此 CSV 模式很适合告警:让 cron 或指标导出器读取文件,并针对关注的字段设置告警,不必整天盯着终端。首次采样时四个网络字节列为空,因为按域统计的计数器会延迟一个采样间隔;空值是正常现象,并非导出损坏。读取数据行时,值得告警的字段包括:
%CPU:该域在本次采样中消耗的宿主机 CPU 比例。%MEM:该域占用的宿主机内存比例。RDRQ与WRRQ:读写请求数,是发现虚拟机之间存储争抢最快的信号。TIME:累计 CPU 时间,连续几天高 CPU 的虚拟机会十分明显。
CSV 示例显示了表头和 db01 的数据行:

把 -c 指向远程 URI 就能监控另一台主机,例如 sudo virt-top -c qemu+ssh://user@remote-host/system。数据会通过 SSH 流式传回终端,无需登录目标机器或打开第二个窗口,远端也只需要 libvirt。它要求以密钥方式通过 SSH 登录该主机;如果你已能从终端管理这台机器,相关配置通常已经具备。本文其他工具也支持同样的 URI 形式。
使用 virt-cat 读取虚拟机中的文件
virt-cat 可以打印虚拟机磁盘镜像中的任意文件。虚拟机无需运行,镜像中的内容也不会改变。既可以用 -d 按 libvirt 域名引用虚拟机,也可以用 -a 直接指定镜像文件:
sudo virt-cat -d db01 /etc/os-release
测试虚拟机是 Debian 12 镜像,下面是从离线磁盘直接读取的前几行:
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
日志和配置文件也使用相同方式读取。以下两个读取命令几乎每天都会用到:
sudo virt-cat -d db01 /etc/fstab
挂载表会完全按照虚拟机看到的内容打印:
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# systemd generates mount units based on this file, see systemd.mount(5).
# Please run 'systemctl daemon-reload' after making changes here.
#
# <file system> <mount point> <type> <options> <dump> <pass>
# / was on /dev/vda1 during installation
UUID=ddf91058-656c-4606-806a-8a4a9d31676c / ext4 errors=remount-ro 0 1
测试主机执行这次读取时,虚拟机处于关机状态:

由于输出写到标准输出,普通的 Shell 管道同样适用。可以用管道过滤,也可以把镜像中的文件导出,用于工单排查:
sudo virt-cat -d db01 /etc/passwd | tail -3
sudo virt-cat -d db01 /var/log/auth.log > auth-today.log
第二行在凌晨三点尤其有用:只需一条命令,就能在宿主机上取得无法启动虚拟机的认证日志。有一种失败模式几乎所有人都会遇到,因此值得单独说明。
virt-cat 会拒绝运行中虚拟机的镜像
如果把 virt-cat 指向仍处于开机状态的域磁盘,libguestfs 会在镜像锁处停止:
libguestfs: error: could not create appliance through libvirt. Original error from libvirt: internal error: process exited while connecting to monitor: qemu-kvm: -blockdev {"node-name":"libvirt-4-format","read-only":true,"cache":{"direct":false,"no-flush":true},"driver":"qcow2","file":"libvirt-4-storage","backing":null}: Failed to get shared "write" lock
Is another process using the image [/var/lib/libvirt/images/db01.qcow2]? [code=1 int1=-1]
运行中的 qemu 进程会对 qcow2 文件持有写锁,而 libguestfs 拒绝与之争抢,因为两个写入方修改同一镜像可能严重破坏虚拟机。先执行 virsh shutdown db01 关机,等待 virsh list 中的状态显示为“shut off”,再重新运行命令。我们第一次针对运行中的虚拟机执行时就遇到了这个问题,因此最好尽早养成习惯:libguestfs 文件工具只读取离线镜像或已关机虚拟机的镜像,没有任何参数能让它强行取得锁。
使用 virt-ls 列出虚拟机文件
virt-ls 遍历虚拟机文件系统的方式与 ls 相同。普通输出只显示名称,-l 则增加权限、大小等长格式信息:
sudo virt-ls -l -d db01 /etc/ssh
列表中可以看到宿主机密钥文件和配置,以及它们在虚拟机中的真实所有者:
total 588
drwxr-xr-x 4 root root 4096 Jun 2 2023 .
drwxr-xr-x 67 root root 4096 Nov 9 23:31 ..
-rw-r--r-- 1 root root 573928 Feb 8 2023 moduli
-rw-r--r-- 1 root root 1650 Feb 8 2023 ssh_config
drwxr-xr-x 2 root root 4096 Feb 8 2023 ssh_config.d
-rw-r--r-- 1 root root 3223 Feb 8 2023 sshd_config
drwxr-xr-x 2 root root 4096 Feb 8 2023 sshd_config.d
-R 会递归列出,适合在记不清配置文件位于哪个目录时进行搜索:
sudo virt-ls -R -d db01 /etc/apt
递归输出每行一个路径,可以直接交给 grep 或 while 循环:
/apt.conf
/apt.conf.d
/apt.conf.d/00trustcdrom
/apt.conf.d/01autoremove
/apt.conf.d/20listchanges
/apt.conf.d/70debconf
/auth.conf.d
/keyrings
/listchanges.conf
/listchanges.conf.d
测试虚拟机的递归结果如下,可直接送入循环处理:

当关注大小而不是精确字节数时,把 -h 与 -l 一起使用;排查文件增长问题时,人类可读的大小更容易比较。借助 virt-ls 查找文件、再用 virt-cat 读取文件,大部分“文件在哪里、内容是什么”的问题都能从宿主机解决,也不会为了读取一个配置而反复启动故障虚拟机三次。
使用 virt-edit 编辑虚拟机中的文件
virt-edit 会直接在镜像中原地修改文件。除文件路径外不传其他参数时,它会打开编辑器,行为与 vim 类似。处理故障虚拟机时,我们最常用的是非交互模式:-e 接受 Perl 替换表达式,因此一行修复也能写成脚本化命令。下面的命令从宿主机侧关闭 SSH 密码认证:
sudo virt-edit -d db01 -e 's/^#PasswordAuthentication yes/PasswordAuthentication no/' -b .bak /etc/ssh/sshd_config
-b 接受一个扩展名,并在镜像内保留原文件为 sshd_config.bak。它曾多次把我们从不正确的替换表达式里救回来,而且添加它的成本很低。文件路径必须放在最后,因为 -b 会把下一个单词当作扩展名;按照命令语法说明,把文件路径保留为最后一个参数。之后用 virt-cat 回读文件,确认修改已经生效:
sudo virt-cat -d db01 /etc/ssh/sshd_config | grep -i passwordauth
替换成功,注释掉的原始内容保持不变:
PasswordAuthentication no
# PasswordAuthentication. Depending on your PAM configuration,
# PAM authentication, then enable this but set PasswordAuthentication
修改和验证都针对离线镜像执行:

需要精准的小范围修复时应使用 virt-edit,因为能在命令历史中审阅的脚本化替换,比必须全程盯着的救援启动更可靠。对于结构性变更,例如无论怎样修补都无法启动的虚拟机网络配置,从 PXE 或 ISO 源重新构建虚拟机通常更干净。
使用 virt-df 检查虚拟机磁盘使用量
virt-df 是宿主机上所有虚拟机的 df。不带参数运行时会遍历全部已定义域,并打印每个虚拟机的文件系统表:
sudo virt-df -h
每行对应虚拟机内的一个文件系统,前面带有虚拟机名称,因此磁盘满时能立刻看出属于哪个虚拟机:
Filesystem Size Used Available Use%
db01:/dev/sda1 5.8G 1.2G 4.3G 21%
关注 inode 而不是数据块时,把 -h 换成 -i。邮件或容器主机可能在 df 仍显示大量空闲空间时耗尽 inode,而虚拟机自身的日志不会说明原因,因此从宿主机同时检查两者可以省下一次启动。如果只检查一个镜像文件而不是所有虚拟机,可以用 -a 传入路径:
sudo virt-df -h -a /var/lib/libvirt/images/db01.qcow2
两次针对测试虚拟机的运行如下:先显示完整表格,再检查单个镜像:

从故障主机取出的镜像也能使用这种方式,因此可以把 qcow2 复制到正常机器上直接得到答案,而不必复活原主机。这是回答“它宕机时磁盘是否已满”最快的方法,而几乎所有事后分析都会首先问这个问题。
使用 virt-clone 克隆虚拟机
virt-clone 复制已定义的虚拟机,并修复复制后可能冲突的宿主机侧标识:域名、MAC 地址和 NVRAM 文件。它只能复制关机状态的虚拟机,因此请先关闭源虚拟机:
sudo virsh shutdown db01
sudo virt-clone --original db01 --name db02 --auto-clone
使用 --auto-clone 时,它会在原磁盘旁自动生成新磁盘名称,并在复制过程中打印路径:
Allocating 'db02.qcow2' | 6.0 GB 00:30
Clone 'db02' created successfully.
如果希望把副本放到指定位置,就省略 --auto-clone 并自行传入路径:
sudo virt-clone --original db01 --name db02 --file /var/lib/libvirt/images/db02.qcow2
克隆出来的虚拟机能启动,但仍然沿用源虚拟机的主机名、machine ID 和 SSH 主机密钥。这些冲突造成的问题往往很难追踪:DHCP 可能给两台机器相同的保留地址,SSH 会警告主机密钥改变,任何依赖 machine ID 的功能都可能异常。请在首次启动前,趁镜像仍关机时对克隆执行 virt-sysprep 清除这些标识。默认操作集合范围很广,因此这里明确指定三个身份重置项;注意 user-account 会从克隆中删除所有非 root 用户及其主目录。如果要做模板镜像,这正是需要的效果;如果克隆应保留登录账户,就跳过该项:
sudo virt-sysprep --domain db02 --hostname db02 --operation machine-id,ssh-hostkeys,user-account
它会在执行每个重置操作时打印信息:
[ 0.0] Examining the guest ...
[ 157.0] Performing "machine-id" ...
[ 157.4] Performing "ssh-hostkeys" ...
[ 157.7] Performing "user-account" ...
完成之后,克隆就是一台真正独立的机器。用 virsh list --all 列出两台虚拟机,应看到这一对:
Id Name State
-----------------------
- db01 shut off
- db02 shut off
我们的克隆操作从磁盘复制开始,经过 sysprep 重置,最终得到 virsh list 的输出:

对于不是通过克隆创建、而是需要从零构建的虚拟机,virt-builder 可以从在线模板库生成全新镜像,带 Kickstart 文件的 virt-install 则能自动化完整安装。
哪个工具处理哪种任务
这 6 个工具会一起安装,并读取同一份 libvirt 清单,因此唯一要判断的是当前问题适合哪个工具:
| 工具 | 任务 | 所需虚拟机状态 |
|---|---|---|
virt-top |
实时查看每个域的 CPU、内存、磁盘和网络 | 任意状态,运行中最佳 |
virt-cat |
从镜像打印文件 | 关机 |
virt-ls |
浏览镜像目录树 | 关机 |
virt-edit |
修复或脚本化修改单个文件 | 关机 |
virt-df |
查看所有虚拟机的文件系统使用量 | 关机 |
virt-clone |
复制虚拟机并生成新的身份字段 | 源虚拟机关机 |
把 libguestfs 文件工具用于离线操作,把 virt-top 用于实时观察,大多数虚拟机问题都能只在宿主机上解决;当问题虚拟机无法启动时,这正是最需要的能力。virsh 命令速查表涵盖与这些工具配合使用的生命周期命令,而当虚拟机本身需要运行 KVM 时,还需要处理嵌套虚拟化。
libguestfs 官方文档列出了完整工具集,包括用于交互会话的 guestfish 和用于救援 Shell 的 virt-rescue。熟练掌握上述 6 个工具后,这两个也值得学习,因为它们能处理文件系统级清理、启动顺序修复等单一用途工具无法覆盖的罕见任务。










