
这次我们看一个典型的 Rocky 10 虚拟化排障场景登录服务器后执行virsh list --all发现虚拟机列表是空的。第一反应通常很重——虚拟机是不是丢了数据是不是没了但更合理的排查顺序是先确认两件事磁盘镜像文件还在不在XML 定义文件还在不在。/var/lib/libvirt/images/下面的 qcow2 文件安静地躺在那里/etc/libvirt/qemu/下面的 XML 也在服务状态也是正常的。问题基本可以下结论了虚拟机没有丢只是当前命令行连到了另一套 libvirt 实例而那一套实例里什么都没定义。这类故障在 KVM/libvirt 环境里并不少见。libvirt 并不是只有唯一一个守护进程系统级qemu:///system和用户级qemu:///session对应完全不同的数据目录和 socket在 Rocky 10 这类较新的发行版上还可能出现libvirtd与virtqemud模块化守护进程并存的情况。URI 指向哪一端决定你看到的虚拟机列表长什么样。只要连接 URI 被环境变量、用户切换或服务启动方式改变就很容易出现“虚拟机消失”的错觉。这篇文章按现场排查的顺序走先确认虚拟机没有丢再定位当前连接的是哪一套 libvirt然后通过指定 URI、重新加载 XML 的方式把虚拟机收回管理最后给出防止误判的配置建议和排障命令速查表。如果你一直在用virsh或virt-manager管理 KVM 虚拟机这篇排障记录可以直接收藏备用。1. 故障现象与影响范围先描述一下典型现象方便你对号入座。1.1 典型症状virsh list --all输出为空不仅没有运行中的虚拟机连已定义但未启动的虚拟机也看不到。virt-manager打开后左侧列表为空刷新多次也没有变化。手动执行systemctl status libvirtd发现服务是 activerunning状态没有明显的异常报错。QEMU 进程可能仍然在跑比如ps aux | grep qemu能看到残留的虚拟机进程。磁盘镜像文件还在/var/lib/libvirt/images/下XML 定义文件也还在/etc/libvirt/qemu/下。最关键的是最后一条。只要磁盘镜像和 XML 定义都还在虚拟机的“丢失”就不是物理层面的数据丢失而是管理层面的连接错位。此时最忌讳的操作是立刻用virt-install重建一台同名虚拟机因为这样做可能会覆盖原有定义或者因为磁盘被占用而创建出重复的 XML。1.2 为什么第一反应容易误判很多习惯用 VMware Workstation 的管理员会把“虚拟机列表”理解为软件内部维护的一份注册信息列表空了就以为注册信息丢了。但 libvirt 的工作方式不太一样它把虚拟机定义当作 XML 文件保存在文件系统里由守护进程负责加载和维护。virsh list读到的内容完全取决于它连接到哪一个守护进程。一旦环境变量、用户身份或者 socket 路径发生变化你看到的列表就可能是另一套 daemon 的数据。所以排障的第一步不是重建而是搞清楚两个问题文件在不在当前连接的是谁。2. libvirt 多实例机制system 与 session 的区别在排查之前建议先把 libvirt 的多实例机制理清楚。这是整个故障的根源。2.1 qemu:///system 和 qemu:///sessionlibvirt 最常用的连接 URI 有两个qemu:///system和qemu:///session。这两个 URI 看起来差别不大实际上对应两套完全独立的运行环境对比项qemu:///systemqemu:///session守护进程系统级 libvirtd 或 virtqemud用户级 libvirtd 或 virtqemud默认数据目录/etc/libvirt/qemu~/.config/libvirt/qemu默认 socket/var/run/libvirt/libvirt-sock/run/user/ /libvirt/libvirt-sock权限要求通常需要 root 或 libvirt 组权限当前用户即可典型用途系统虚拟机、开机自启、生产环境用户沙箱、开发测试如果你之前一直用 root 执行virsh连接的是qemu:///system管理的是/etc/libvirt/qemu/下的 XML。后来某个脚本、某个工具或者一次su切换到普通用户后执行virsh默认连接可能就变成了qemu:///session。普通用户的~/.config/libvirt/qemu目录是空的列表自然也是空的。2.2 模块化守护进程libvirtd 与 virtqemud在新版 RHEL/Rocky 系发行版里libvirt 引入了模块化守护进程modular daemons。原来的单一大libvirtd被拆成了多个专用守护进程包括virtqemudQEMU 驱动专用守护进程。virtnetworkd网络管理守护进程。virtstoraged存储管理守护进程。virtnodedevd宿主机设备管理守护进程。virtsecretd密钥管理守护进程。virtproxyd统一接入代理。这些守护进程默认通过 systemd socket 激活也就是说socket 文件存在但进程可能没有常驻收到连接请求后才由 systemd 拉起。在这个架构下可能出现多个 libvirt 相关守护进程同时存在的情况传统libvirtd还在跑新的virtqemud也在跑两者监听的 socket 路径不同。如果某个管理工具默认去连旧的libvirtdsocket而虚拟机的实际定义由virtqemud加载就会出现列表不一致。2.3 “另一套 libvirt”从哪里来“另一套 libvirt”并不是玄学常见来源有以下几种同一台机器上同时存在 system 和 session 两个实例用户切换导致连接 URI 变化。同一种 URI 下同时存在旧版libvirtd和新版virtqemud不同命令解析到的 socket 不同。管理脚本里写了LIBVIRT_DEFAULT_URIqemu:///session覆盖了系统默认。通过qemussh://连接远程宿主机时主机名写错或连到了另一台机器。容器环境里宿主机命名空间外的 libvirtd 与容器内的 libvirtd 是两套独立实例。搞清楚这些来源排障时就不会只盯着文件系统看而是会主动去查连接 URI。3. 现场排查确认虚拟机没有丢下面是一套可以照抄的排障流程。建议按顺序执行不要跳步。3.1 第一步确认镜像文件还在ls -lh /var/lib/libvirt/images/预期输出里应该能看到一个或多个 qcow2 文件大小和你印象中的虚拟机磁盘容量一致。如果这里为空再检查自定义存储池路径virsh -c qemu:///system pool-list --all virsh -c qemu:///system vol-list --pool default如果存储池和卷都看不到说明问题可能升级为存储层面的故障需要先看存储池状态。3.2 第二步确认 XML 定义还在ls -l /etc/libvirt/qemu/*.xml正常情况下每台虚拟机对应一个 XML 文件。文件名通常是虚拟机名称例如rocky10-test.xml。如果这一步能看到文件说明定义没有被物理删除。3.3 第三步确认守护进程在运行systemctl status libvirtd virtqemud virtproxyd在 Rocky 10 环境里可能只有libvirtd也可能只有virtqemud也可能两者都在。重点看状态是不是active (running)以及有没有报 socket 相关的错误。3.4 第四步检查当前连接 URIvirsh uri这条命令会直接输出当前实际连接的 URI。如果输出是qemu:///session而你的虚拟机定义在/etc/libvirt/qemu/那已经可以确定问题出在连接通道上。如果输出是qemu:///system但列表仍然为空则要继续看 daemon 是否加载了 XML。3.5 第五步显式连接 system 实例virsh -c qemu:///system list --all如果这样能看到虚拟机说明核心结论成立文件没丢服务没挂只是默认连接错了。4. 定位“另一套 libvirt”并恢复管理找到问题方向后需要进一步确认是谁把 URI 指到了别处。4.1 检查环境变量echo $LIBVIRT_DEFAULT_URI很多开发机或自动化脚本会在/etc/profile、~/.bashrc、~/.zshrc里设置这个变量。一旦它被设置成qemu:///session所有不显式带-c参数的virsh命令都会走 session 实例。检查到之后可以直接在当前 shell 里修正unset LIBVIRT_DEFAULT_URI export LIBVIRT_DEFAULT_URIqemu:///system4.2 检查当前用户身份whoami id如果当前登录用户已经从 root 切换成了普通用户而且普通用户没有配置连接 system 实例的权限virsh默认就会落到 session 实例。建议回到 root 再执行virsh或者把用户加入libvirt组usermod -aG libvirt username注意组变更需要重新登录会话才会生效。4.3 检查 socket 监听情况ss -lx | grep -E libvirt|virtqemud预期会看到类似/run/libvirt/libvirt-sock、/run/libvirt/virtqemud-sock、/run/user/uid/libvirt/libvirt-sock这样的监听路径。如果你发现系统同时监听多个 socket并且不同virsh命令走的是不同 socket那就证实了多实例并存。4.4 检查是否有远程连接干扰virsh -c qemussh://root127.0.0.1/system list --all如果你是习惯用qemussh连接远程宿主机的人还要检查~/.ssh/config里是否有 Host 别名避免“以为连的是宿主机 A实际跳到了宿主机 B”。5. 恢复管理的完整命令流程确认虚拟机没有丢之后恢复管理就很直接了。关键是让正确的 daemon 加载正确的 XML。5.1 使用正确的 URI 重新列出虚拟机virsh -c qemu:///system list --all如果虚拟机出现在列表里说明 daemon 已经通过扫描/etc/libvirt/qemu/自动加载了定义不需要额外操作。5.2 手动重新加载 XML 定义如果列表仍然为空可能是 daemon 没有自动扫描到 XML此时用define手动加载virsh -c qemu:///system define /etc/libvirt/qemu/rocky10-test.xml执行成功后会出现Domain rocky10-test defined from /etc/libvirt/qemu/rocky10-test.xml的提示。define操作只是加载定义不会启动虚拟机也不会覆盖同名磁盘数据。5.3 设置开机自启并启动虚拟机virsh -c qemu:///system autostart rocky10-test virsh -c qemu:///system start rocky10-testautostart的作用是让 libvirtd 在开机时自动加载这台虚拟机的定义避免下次重启后又出现“找不到虚拟机”的错觉。5.4 查看虚拟机配置确认无误virsh -c qemu:///system dumpxml rocky10-test检查source file/var/lib/libvirt/images/rocky10-test.qcow2/这一行确认磁盘路径没有指错。5.5 如果 XML 定义真的丢失了如果/etc/libvirt/qemu/下没有 XML 文件但磁盘镜像还在可以使用virt-install --import基于现有磁盘重新创建定义virt-install \ --name rocky10-test \ --memory 4096 \ --vcpus 2 \ --disk path/var/lib/libvirt/images/rocky10-test.qcow2,formatqcow2 \ --import \ --osinfo detecton,requireoff注意这里的--memory、--vcpus、磁盘路径都要按实际环境修改。--osinfo detecton,requireoff表示尝试从磁盘探测系统类型探测不到也不报错。执行后可以用virsh -c qemu:///system list --all验证。6. 排障命令速查表把上面用到的命令汇总成一张表方便现场复制。排查目标命令判断标准查看当前连接 URIvirsh uri是否指向 system查看默认 URI 环境变量echo $LIBVIRT_DEFAULT_URI是否被设置为 session列出 system 实例虚拟机virsh -c qemu:///system list --all能否看到虚拟机列出 session 实例虚拟机virsh -c qemu:///session list --all通常为空查看 libvirt 守护进程systemctl status libvirtd virtqemud virtproxyd是否 active查看 socket 监听ss -lx | grep -E libvirt|virtqemud哪些 socket 在监听加载 XML 定义virsh -c qemu:///system define /path/to/xxx.xml提示 defined设置开机自启virsh -c qemu:///system autostart vm无报错查看虚拟机 XMLvirsh -c qemu:///system dumpxml vm磁盘路径正确备份定义文件tar czf backup.tar.gz /etc/libvirt/qemu生成备份包建议把命令里的rocky10-test、/etc/libvirt/qemu/rocky10-test.xml、/var/lib/libvirt/images/rocky10-test.qcow2替换成你自己的虚拟机名避免误操作。7. 与其他虚拟化平台的误判对比很多管理员是从 VMware Workstation 迁移到 KVM/libvirt 的遇到“虚拟机消失”时会沿用 VMware 的排查思路这里需要做一个区分。7.1 VMware Workstation 的常见情况在 VMware Workstation 里“虚拟机找不到”通常是因为.vmx文件被移动、虚拟机目录被重命名、快照描述文件.vmsd损坏或者 Workstation 的 inventory 注册信息丢失。处理方式一般是去磁盘上找.vmx然后通过Open重新打开虚拟机。7.2 libvirt/KVM 的常见情况libvirt 环境里虚拟机的核心数据同样分为“元数据”和“磁盘数据”。元数据是 XML 定义文件磁盘数据是 qcow2 镜像。只要 XML 和镜像都在理论上就可以恢复。问题通常出在连接层URI 不对、daemon 不对、用户不对。这与 VMware 的注册信息丢失有本质区别。7.3 通用排障思路无论是哪种平台排障顺序都建议保持一致先确认磁盘镜像/虚拟磁盘文件存在再确认元数据/配置文件存在最后确认管理工具连接到哪个实例。在 KVM 环境里virsh -c qemu:///system list --all是成本最低的验证命令建议第一时间执行。8. 防止再次发生配置与备份建议问题已经解决了接下来要考虑怎么不让它再次发生。8.1 固定默认 URI最直接的方法是在管理员用户的 shell 配置里固定LIBVIRT_DEFAULT_URI# 编辑 /etc/profile.d/libvirt.sh 或 ~/.bashrc export LIBVIRT_DEFAULT_URIqemu:///system这样每次打开终端virsh都会默认连接系统级实例不会因为用户切换而落到 session 实例。8.2 脚本里显式指定连接参数所有自动化脚本都建议显式加-c qemu:///system不要依赖默认值virsh -c qemu:///system list --all virsh -c qemu:///system start rocky10-test这样即使目标机器上有人改了环境变量脚本行为也不会被影响。8.3 定期备份 XML 定义XML 定义文件很小但价值很高。建议定期打包备份mkdir -p /var/backups/libvirt tar czf /var/backups/libvirt/libvirt-defs-$(date %F).tar.gz /etc/libvirt/qemu可以配合 crontab 每周执行一次。磁盘镜像的备份则需要结合实际的备份窗口和存储空间至少要做到“删除前先确认”。8.4 合规与安全提醒操作生产环境虚拟机之前请确认你拥有相应机器的管理和数据访问授权。涉及他人虚拟机、业务数据、快照文件时不要在没有授权的情况下执行恢复、启动、迁移或删除操作。如果机器承载重要业务恢复前先完整备份磁盘镜像和 XML 定义避免二次破坏。9. 总结这次排障的核心结论不复杂虚拟机没丢是命令行走错了门。KVM/libvirt 的“虚拟机列表”不像 VMware 那样由单一 inventory 维护而是由多个 daemon 实例分别管理。URI、用户身份、socket 路径、模块化守护进程任何一个环节出现偏差都会导致virsh list --all输出为空。遇到这类问题最先应该验证的是文件层镜像是否还在XML 是否还在。然后执行virsh -c qemu:///system list --all大概率能把虚拟机“找回来”。最容易踩的坑是过早重建虚拟机以及忽略LIBVIRT_DEFAULT_URI环境变量。后续建议把默认 URI 固定下来把 XML 备份纳入日常运维再花一点时间了解 Rocky 10 环境下模块化守护进程的 socket 激活机制这类故障基本就不会再造成困扰了。