Rocky 10 中 virsh 列表为空?排查 libvirt URI 连接问题
2026/9/7 3:04:16 网站建设 项目流程

Rocky 10 上打开 virt-manager 或者敲下virsh list --all时,发现原来的虚拟机列表变成空白,第一反应往往是“完了,虚拟机丢了”。我这次排障得到的结论很直接:虚拟机没丢,是你连到了另一套 libvirt。这类问题在 Rocky 10、CentOS Stream 以及 RHEL 系新版本里都有可能遇到,表现形式几乎一模一样——定义文件还在,磁盘镜像还在,甚至 qemu 进程可能还在跑,但 virsh 和 virt-manager 就是看不到虚拟机。所以排查的关键不是重装 libvirt,也不是急着恢复文件,而是先搞清楚当前 shell 到底连接了哪一套 system socket 或 session socket。本文适合在 Rocky 10 上跑 KVM 虚拟化的用户、遇到过 libvirt 列表为空但文件没消失的人,以及刚接手带多套虚拟化环境的运维同学。

1. 先判断问题:是虚拟机丢了,还是连错了 URI

1.1 libvirt 有两套常见连接:system 和 session

libvirt 管理虚拟机时,通过 URI 来决定连到哪一套libvirtd控制接口。最常用的是这两套:

  • qemu:///system:系统级连接,通常由 root 权限发起,socket 在/run/libvirt/libvirt-sock
  • qemu:///session:用户级连接,当前普通用户自己启动的 libvirt 会话,socket 在$XDG_RUNTIME_DIR/libvirt/libvirt-sock

如果你平时用 root 账号或者 virt-manager 管理虚拟机,那么虚拟机大概率被定义在 system 列表里。但当你切到普通用户终端,直接输入virsh时,libvirt 可能优先连到 session socket,session socket 对应的是一套全新的空 libvirt 环境,列表自然会是空的。

这两个 URI 对应的不是同一个 socket,也不是同一个命名空间。虚拟机定义在哪个 socket 下,就只能在哪个 URI 下看到。system 列表里的虚拟机跑到 session 列表里去找,当然找不到。

1.2 第一步不是修复,而是确认当前 URI

很多人看到virsh list --all为空,会直接执行virsh undefine、删除磁盘、重装 libvirt,结果越搞越糟。正确做法是先确认当前连的是哪套 URI。

virsh uri virsh list --all

如果virsh uri返回的是qemu:///session,那么当前确实连到了 session 套接字。此时再对比 root 环境:

sudo virsh uri sudo virsh list --all

如果 root 环境返回的是qemu:///system,并且能看到所有虚拟机,那就说明虚拟机没丢,只是普通用户和 root 用户各自连到了不同的 libvirt 实例。

我在 Rocky 10 上遇到的情况就是这样:用普通用户登录桌面后打开终端,virsh list --all是空的;用sudo virsh list --all一看,所有虚拟机都在。这个问题从现象上特别像“虚拟机丢了”,其实就是观察角度错了。

为了更直观,可以在命令行里强制指定 URI 对比:

virsh -c qemu:///system list --all virsh -c qemu:///session list --all

两条命令结果不一致时,基本可以断定是连接 URI 的问题。

连接类型典型 URIsocket 位置权限虚拟机定义目录
systemqemu:///system/run/libvirt/libvirt-sockroot/etc/libvirt/qemu 或 /var/lib/libvirt/qemu
sessionqemu:///session$XDG_RUNTIME_DIR/libvirt/libvirt-sock当前用户用户 home 下或运行目录中

这里最容易误判的点是:virt-manager默认添加的连接通常是qemu:///system,所以图形界面里能看到虚拟机。而终端里普通用户执行virsh默认可能是 session,两个工具看起来“不一致”,其实都是各自 URI 下的正常结果。

2. 找到“另一套 libvirt”:从 socket 和守护进程入手

2.1 system socket 和 session socket 的真实位置

确认问题方向后,接下来要找“另一套 libvirt”到底在哪。先从 socket 文件下手。

system 级别 socket 通常在:

ls -la /run/libvirt/libvirt-sock

在 Rocky 系系统里,/run/var/run往往是软链关系,所以/var/run/libvirt/libvirt-sock指向的也是同一套。

session 级别的 socket 要看当前用户的运行目录:

echo $XDG_RUNTIME_DIR ls -la $XDG_RUNTIME_DIR/libvirt/libvirt-sock

普通用户登录后,XDG_RUNTIME_DIR一般是/run/user/<uid>。每个用户有自己独立的 session socket,所以不同用户之间看到的 session 虚拟机列表也会不同。

如果virsh uri返回qemu:///session,但$XDG_RUNTIME_DIR/libvirt/libvirt-sock路径下没有对应 socket 文件,说明 session daemon 可能还没有启动,virsh 会在连接时尝试启动一个新的 libvirtd 会话。这也会造成“当前列表为空”的现象。

2.2 系统里是否真的跑了两套守护进程

除了 socket 文件,还要确认守护进程本身是否存在多套。

systemctl status libvirtd systemctl status virtqemud

新版 RHEL 系和 Rocky 10 环境中,libvirt 可能把 qemu 驱动拆分成virtqemud这样的独立守护进程,和传统libvirtd并存。如果系统里同时存在旧版libvirtd和新版virtqemud,连接命令选的 URI 不同,就会进入不同的管理面。

然后看进程:

ps -ef | grep -E 'libvirtd|virtqemud'

正常环境一般只有一个主守护进程。如果看到多个 libvirtd 相关进程,就要逐个确认它们对应的 socket 和配置。出现这种情况的常见原因是:系统升级时旧服务没有被完全停止,或者某个管理平台用独立参数拉起了一套新的 libvirt。

还可以用sslsof确认 socket 被哪个进程占用:

ss -lx | grep libvirt lsof /run/libvirt/libvirt-sock

这样能快速区分出“另一套 libvirt”到底是谁启动的。

3. 虚拟机“看不见”不代表丢掉:进程、定义、存储三条线分开确认

3.1 先看 qemu 进程是否还活着

确认虚拟机是否还存在的第一条线,是看运行中的 qemu 进程。

ps aux | grep qemu

如果 qemu 进程还在运行,说明至少有一台或多个虚拟机正在运行。此时即使virsh list --all为空,也只是连接 URI 错了,而不是虚拟机没了。

qemu 进程的命令行里通常会带-name-drive file=/var/lib/libvirt/images/xxx.qcow2-uuid等参数,可以通过这些信息反推它属于哪台虚拟机。这样不需要先恢复 libvirt 列表,也能判断出具体是哪个 guest。

如果 qemu 进程一个都没有,说明没有正在运行的虚拟机,但不代表定义文件也丢了。许多宿主机长期关机或重启后,虚拟机本来就是停止状态,此时列表为空可能是连接问题,也可能是定义文件被清理。

3.2 用 XML 定义文件把虚拟机恢复到正确 URI

libvirt 的虚拟机定义本质上是一个 XML 文件。只要 XML 还在,虚拟机就没有丢,完全可以恢复到指定 URI 下。

常见定义文件位置:

  • /etc/libvirt/qemu/
  • /var/lib/libvirt/qemu/
  • /etc/libvirt/qemu/
ls -la /etc/libvirt/qemu/

如果能看到类似vm1.xmlvm2.xml的文件,说明定义都还在。此时可以直接用 define 命令把它重新注册到 system 列表:

virsh -c qemu:///system define /etc/libvirt/qemu/vm1.xml virsh -c qemu:///system list --all

注意几点:

  • define 之前先确认目标 URI 下没有同名虚拟机,否则可能提示冲突或覆盖。
  • 如果 XML 文件里有uuid和现有虚拟机重复,也需要先处理。
  • define 不会影响正在运行的 qemu 进程,但如果该虚拟机已经在运行,重新 define 后 libvirt 可能认为它没有被管理,这种情况需要谨慎处理。
  • 如果 XML 文件被误删,可以从备份目录恢复,或者根据 qemu 进程的启动参数手工重新写一个最小 XML,但不建议新手直接手写,风险偏高。

3.3 再确认存储池和网络

第三件事是确认存储和网络,这两项能进一步证明虚拟机资源没有消失。

virsh -c qemu:///system pool-list --all virsh -c qemu:///system vol-list default

常见默认存储池指向/var/lib/libvirt/images/。如果该目录下磁盘镜像文件还在,说明 guest 的磁盘数据是完整的。

ls -la /var/lib/libvirt/images/

网络定义同样值得检查:

virsh -c qemu:///system net-list --all

如果之前创建过隔离网络或 NAT 网络,网络定义也会出现在 system 列表里。如果连网络都还在,更说明不是“虚拟机丢了”,只是你当前视角进入了另一套 libvirt。

这三条线分别是:进程、定义、存储。三者都确认后,就可以放心下结论:虚拟机没有丢。

4. 为什么会连到另一套 libvirt:常见来源和触发场景

4.1 普通用户 session 和 sudo 后的 URI 变化

最常见触发场景就是普通用户和 root 用户切换。

普通用户终端里直接执行virsh,libvirt 会根据调用者的权限自动选择 URI。非 root 用户没有权限连接 system socket 时,通常会回落到 session。而 session 是独立环境,不会自动加载 root 在 system 里管理的虚拟机。

sudo 之后情况更复杂。执行sudo virsh时,系统环境变量可能没有完整继承XDG_RUNTIME_DIR,某些版本会尝试连 system socket,并提示权限错误;某些配置下则可能继续使用 session socket,导致 root 环境下看到的还是空列表。

所以我会建议每次执行时显式指定 URI,不要依赖默认行为:

sudo virsh -c qemu:///system list --all

如果是图形界面,问题更隐蔽。虚拟化管理工具可能记住了一个旧的 session 连接,重新打开时自动选中它,看起来就像虚拟机列表清空了。

4.2 守护进程拆分带来的 socket 变化

Rocky 10 这类较新版本,如果沿用了 RHEL 9 之后的思路,libvirt 的模块化趋势会比较明显。传统libvirtd可能被拆成virtqemudvirtnetworkdvirtnodedevd等多个守护进程。

此时需要确认你安装的版本是否启动的是拆分的服务:

systemctl list-units | grep virt systemctl status virtqemud

如果系统里同时存在老的服务文件和新服务文件,或者升级过程中旧 socket 没有被清理,就会出现“明明 libvirtd 在运行,但 virsh 连到另一套 socket”的情况。

这种情况下,运行中的 qemu 进程不一定直接归属于当前 virsh 连接的管理面。有些虚拟化平台自带的脚本会把LIBVIRT_DEFAULT_URI=qemu:///system写在环境变量里,而你的终端没有加载,自然看到两套结果。

4.3 其他容易混的来源

除了用户切换和守护进程拆分,还有几个比较容易踩的来源:

  • 宿主机上跑着嵌套虚拟化,内部又安装了一套 libvirt,内部列表和外部列表是两套。
  • 容器里启动的 libvirtd,宿主机的 virsh 不一定能看到。
  • 运维脚本里写死了qemu:///session,后续直接复用脚本,导致误认为虚拟机消失。
  • 多用户登录环境里,另一个用户在同一台机器上启动了独立的 session daemon,虚拟机归他管理,你当然看不到。

遇到这些情况,重点不是把 libvirt 卸载重装,而是把所有 URI 枚举出来逐一确认。可以先用virsh uri看当前,再用不同的-c参数切到其他 URI 看列表差异。

5. 恢复正常管理:修正连接而不是重装虚拟化

5.1 固定使用 system URI 拿回虚拟机列表

如果确认虚拟机定义本来就在 system 列表里,后续管理尽量统一使用 system URI。

命令行里可以这样用:

virsh -c qemu:///system list --all virsh -c qemu:///system start vm1 virsh -c qemu:///system shutdown vm1

如果觉得每次加-c qemu:///system太啰嗦,可以临时设置默认 URI:

export LIBVIRT_DEFAULT_URI=qemu:///system virsh list --all

这个环境变量只在当前终端生效,不会影响其他用户和其他 session。生产环境里如果希望所有命令默认走 system,可以在用户级配置里维护,但我不建议修改系统级全局配置,因为可能影响其他管理工具。

如果只是排查一次,直接加-c参数最稳妥,命令一目了然,脚本里也不容易被环境变量坑到。

5.2 XML 不在当前连接列表时,重新 define 的注意事项

有些情况下,XML 文件存在,但 define 后没有出现在qemu:///system列表里。可能是 UUID 冲突,也可能是 libvirt 配置里的动态拥有者策略把新定义归到了其他用户下。

重新 define 的推荐顺序:

  1. 备份原 XML。
  2. 查看文件里的nameuuid和磁盘路径,确认没有冲突。
  3. 执行virsh -c qemu:///system define /path/to/xxx.xml
  4. 执行virsh -c qemu:///system list --all验证。
  5. 确认自动启动配置:virsh -c qemu:///system autostart vm1

不要把 define 看成“导入数据”,它只是把 XML 告诉 libvirt。如果 XML 指向的磁盘路径已经不存在,虚拟机虽然能在列表里出现,但无法正常启动,启动时会报找不到存储卷。

5.3 virt-manager 添加连接时怎么避免再次选错

图形界面里,打开 virt-manager 后,在连接列表里检查一下是不是存在多个 QEMU/KVM 连接。

添加连接时注意 URI:

  • Hypervisor 选择 QEMU/KVM。
  • URI 手动输入qemu:///system
  • 不要勾选或者输入 session URI,除非你确实需要管理普通用户的虚拟机。

如果连接列表里既有QEMU/KVM又有QEMU/KVM user session,最好删除不需要的那个,避免每次打开都自动选中 session。

我一般会在测试机里把误加的 session 连接删除,只保留 system 连接。这样 virt-manager 的显示结果和 root 下virsh -c qemu:///system list --all保持一致,排障时不用来回切换。

6. 最后留一个排障清单,避免下次再慌

6.1 从现象到结论的固定检查顺序

以后再遇到“虚拟机列表空了”,不要急着重装,按这个顺序来:

  1. 执行virsh uri,确认当前连接的是哪套 URI。
  2. 执行virsh list --allvirsh -c qemu:///system list --all,对比结果。
  3. 执行ps aux | grep qemu,看是否还有运行中的 qemu 进程。
  4. 检查/etc/libvirt/qemu/下的 XML 文件是否还在。
  5. 检查/var/lib/libvirt/images/下的磁盘镜像是否还在。
  6. 检查virsh -c qemu:///system net-list --all是否能看到网络定义。
  7. 查看日志:journalctl -u libvirtd或者journalctl -u virtqemud,关注 socket 启动和连接失败记录。
  8. 全部确认后,再决定是切换到 system URI 继续管理,还是重新 define XML。

这里最关键的是第 1 步。很多时候第 2 步里两台命令结果不一致,就已经知道问题出在哪了,根本不用碰任何文件。

6.2 两种“虚拟机真丢了”的情况,别和连错 URI 混淆

虽然本文核心是“虚拟机没丢”,但还是要说清楚什么情况下虚拟机可能是真丢了,避免把两种情况混为一谈。

第一种是文件系统损坏。如果宿主机非正常断电,或者磁盘出现坏道,/etc/libvirt/qemu/下的 XML 文件可能损坏,虚拟机定义会丢失。这种丢失不是 URI 导致,即使切换到正确的 system URI,列表里依然没有。

第二种是人为误删。有人清理空间时直接删除/var/lib/libvirt/images/下的 qcow2 文件,或者virsh undefine --remove-all-storage一条命令把定义和磁盘一起删掉。这种情况下,再从另一个 URI 切回来也看不到虚拟机。

区分方法很简单:看 XML 文件和磁盘镜像是否还在。如果这两个都在,只是列表为空,基本就是 URI 或 socket 问题;如果文件都不在了,说明是更严重的存储或误操作问题,需要走数据恢复方向,而不是继续折腾 libvirt。

我在 Rocky 10 上处理完这次排障后,特意在终端里加了一个 alias,把常用的管理命令固定到 system URI 上:

alias virsh-system='virsh -c qemu:///system' alias vlist='virsh -c qemu:///system list --all'

这样日常查看时不会因为默认 URI 变化而看错列表。对于刚接触 Rocky 10 或者刚接手这类环境的读者,我建议先把 URI 概念理清,再考虑复杂的虚拟化编排。绝大多数“虚拟机突然不见了”的故障,最后都是连接视角不对,而不是资源真的消失了。

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

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

立即咨询