ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Rocky Linux 10虚拟机无故消失?教你三步定位libvirt连接URI问题

Rocky Linux 10虚拟机无故消失?教你三步定位libvirt连接URI问题 有朋友在 Rocky Linux 10 上排查虚拟机问题时遇到过一个非常迷惑的现象virsh list --all输出为空virsh pool-list也是空磁盘里的 qcow2 镜像明明还在甚至/etc/libvirt/qemu/下面还能看到之前的 domain XML。第一反应几乎都一样虚拟机丢了硬盘坏了或者系统升级把数据清了。我的判断是虚拟机大概率没丢你只是连到了另一套 libvirt。Rocky Linux 10 里的 libvirt 早就不是“装一个 daemon、连一个 socket”的简单结构而是 system 级与 session 级并存、多 daemon 按需启动的模块化架构。你执行virsh时可能默认连到了用户态 session daemon而真正的虚拟机还安静地挂在 system 级 daemon 下面。这篇文章会把这个“排障事故”从头拆一遍先讲 libvirt 的连接模型与 modular daemon 架构再给出定位“到底是连接问题还是数据问题”的检查步骤然后落到怎么恢复访问、怎么把它变成预防手段。读完你至少能独立回答三个问题当前virsh到底连到了哪套 daemon那批虚拟机是否还在下次怎么避免再次连错。1. 这篇文章真正要解决的问题不是所有“虚拟机消失”都是数据丢失至少可以切成两类连接层问题和数据层问题。连接层问题是指 libvirt daemon、socket、URI 配置不一致导致客户端看到了错误的虚拟机列表数据层问题是指 domain XML、磁盘镜像、存储池真的被删除或损坏。这两类问题的表象完全一样但处理方式完全不同前者只需要改连接配置后者才需要谈备份恢复。本文主要聚焦连接层问题因为它非常常见而且恢复成本几乎为零。很多管理员在 Rocky 10 这类较新系统上遇到virsh list为空时第一反应是重装虚拟机或恢复快照这其实很危险。如果在情况不明时执行删除 pool、删除 volume、重置存储等操作反而可能把原本还在的数据搞丢。先分清“看不到”和“不存在”是 Linux 虚拟化排障的基本功。这篇内容适合几类读者使用 KVM/QEMU 做服务器虚拟化又升级过 Rocky、RHEL、AlmaLinux 的运维人员被virsh、virt-manager、virt-install等工具搞得头晕的 libvirt 初学者以及需要在团队里统一 libvirt 管理方式的架构师或 SRE。你不需要提前很懂 libvirt但最好有一台能用virsh的 Linux 机器可以边看边验证。读完你会得到一条完整的排查路径先看当前 URI再看两套 daemon 里的虚拟机列表然后对比 socket 与数据目录最后用环境变量、配置文件和巡检脚本把默认连接钉死。这样以后执行任何virsh命令看到的都是你真正想管理的那一套资源。2. libvirt 基础概念与核心原理2.1 libvirt 的“连接 URI”才是真正入口libvirt 是一个虚拟化管理平台目标是统一管理 QEMU/KVM、Xen、VirtualBox、VMware 等多种虚拟化后端。我们平时说的“用 virsh 管理虚拟机”实际上并不是让 virsh 直接去读磁盘上的 XML 文件而是让 virsh 作为一个客户端先连接到一个 libvirt daemon由这个 daemon 再和具体的 hypervisor 打交道。换句话说libvirt 采用了一种经典的客户端/服务端模型。客户端工具包括virsh、virt-manager、virt-install甚至 OpenStack、oVirt 这类云管理平台服务端就是libvirtd或virtqemud这样的后台进程。客户端要连接服务端必须给出一串连接 URI也就是连接地址。URI 不一样连到的 daemon 就不一样能看到的世界也不一样。这里可以打一个通俗的比方连接 libvirt 就像连接数据库。你以为自己在连线上数据库结果连接串写的是测试库查询结果当然对不上。virsh list --all输出为空可能不是没有虚拟机而是你“查错了库”。2.2 system 与 session两套完全不同的“世界”libvirt 的连接方式里最常见也是最重要的两种是qemu:///system和qemu:///session。二者虽然名字相近但背后是两套几乎完全隔离的运行环境。qemu:///system是系统级连接对应的 daemon 通常以 root 身份运行管理整个物理机上的虚拟化资源。它的配置文件、磁盘镜像、日志都放在系统目录下开机自启适合生产环境里跑正式虚拟机。qemu:///session是用户态连接对应的 daemon 以当前登录用户身份运行管理的是该用户自己的虚拟机配置文件放在用户主目录下随用户会话启动更适合桌面开发、个人测试。为了更直观地看出差异我整理了一张对比表对比项qemu:///systemqemu:///session连接 URIqemu:///systemqemu:///session常见 systemd 单元libvirtd.service / virtqemud.servicesystemctl --user 管理的 virtqemud 等Socket 常见位置/run/libvirt/ 目录下/run/user/UID/libvirt/ 目录下Domain XML 目录/etc/libvirt/qemu/~/.config/libvirt/qemu/磁盘镜像默认目录/var/lib/libvirt/images/~/.local/share/libvirt/images/ 等权限模型root 或 libvirt 组当前登录用户生命周期开机自启服务级随用户登录会话启动典型场景服务器生产虚拟机桌面开发、用户体验测试从这张表就能看明白两套环境连数据目录都不重叠。你通过 session daemon 去查虚拟机当然不可能看到 system daemon 管理的 domain。更麻烦的是普通用户如果没配置任何 URI 相关环境变量某些情况下客户端可能会默认落到 session daemon于是你明明管理着几十台生产虚拟机virsh list --all却返回空列表。2.3 modular daemonRocky 10 为什么会出现“两套 libvirt”在老一代 Linux 发行版里libvirt 的架构比较单一一个libvirtd进程负责所有功能包括 QEMU 驱动、网络、存储、节点设备、密钥等。虽然用起来简单但问题也很明显模块耦合重、安全隔离不足、出问题时一个进程挂掉全部不可用。从 RHEL 9 开始libvirt 官方开始引入 modular daemon 架构把原本集中在一个libvirtd里的功能拆成多个独立的守护进程。常见的有virtqemud负责 QEMU 驱动virtnetworkd负责网络virtstoraged负责存储virtnwfilterd负责网络过滤virtnodedevd负责节点设备virtsecretd负责密钥管理virtproxyd负责请求转发。到了 Rocky Linux 10 这一代这个方向得到了延续系统里并存多套 daemon 的情况越来越多。modular daemon 配合 systemd 的 socket activation 机制可以做到按需启动客户端访问某个 socket 时systemd 才把对应 daemon 拉起来。这样做的好处是资源占用更少、故障隔离更好、权限模型更清晰但副作用是系统中的 socket 和 daemon 数量变多了默认连接变得更加不可控。你可以用下面这条命令先看一下自己系统里到底注册了哪些 libvirt 相关单元systemctl list-unit-files | grep -iE libvirt|virtqemud|virt.*d输出可能会让你惊讶同一台机器上既可能有 system 级的libvirtd.service、virtqemud.service也可能有用户级 systemd 管理的 session daemon。它们各自监听各自的 socket各自维护各自的虚拟机列表。正是这种“多 daemon 并存”的现实导致了很多人升级系统后突然发现虚拟机不见了。并不是软件把数据删了而是升级后启动的默认 daemon 变了或者你的登录方式变了virsh 默认连到了另一套堆栈。2.4 virsh 默认连接 URI 并不是固定的很多初学者会默认以为执行virsh命令就一定能连到系统的 libvirtd。这个假设在新版本系统上越来越不可靠。virsh决定默认连接 URI 的顺序大致是这样的首先看环境变量LIBVIRT_DEFAULT_URI如果设置了就直接使用这个 URI然后看~/.config/libvirt/libvirt.conf里的uri_default配置如果都没有客户端再根据当前用户身份和编译参数选择一个默认值。也就是说同一个命令在不同用户、不同 shell、不同机器上执行可能连到完全不同的 daemon。这就解释了为什么会出现“管理员 A 能看到虚拟机管理员 B 看不到”的诡异现象。比如 A 的 shell 里设置了LIBVIRT_DEFAULT_URIqemu:///systemB 没有设置那么 B 执行virsh list --all时可能就落到了 session daemon看到的世界自然不同。所以第一原则是不要猜先验证当前 URI。排查任何 libvirt 虚拟机问题时第一步都应该先执行virsh uri看看自己到底连到了哪里。3. Rocky 10 中“虚拟机不见了”是怎么发生的3.1 典型事故链一个典型的“虚拟机全部消失”事故往往是这样发生的管理员原本在旧系统上创建了几台 KVM 虚拟机每天通过virsh list --all查看状态一切正常。系统升级到 Rocky Linux 10 之后因为某些包重装、配置重置或用户 shell 环境变化virsh的默认连接 URI 发生了漂移。管理员再执行virsh list --all输出变成空的。接下来常见的操作是打开ps -ef检查 qemu 进程结果发现没有 qemu-system 进程查看镜像目录发现 qcow2 文件还在再想用virsh vol-list default查看存储池也是空的。从表面看证据链非常完整虚拟机没了、进程没了、存储池也没了只剩下一堆孤零零的磁盘文件。但实际上qemu 进程很大概率不是真的消失了而是运行在另一个 daemon 的“视角”下。如果你用virsh -c qemu:///system list --all去查 system daemon那批虚拟机可能好端端地列在那里状态是 running 或 shut off。这也解释了另一个迷惑现象为什么系统里还能看到 domain XML 文件但 virsh 不认。因为 session daemon 根本不会去扫描/etc/libvirt/qemu/目录它只认自己用户目录下的数据。你拿着 system daemon 的配置文件去问 session daemon“虚拟机在哪”自然什么也问不到。3.2 连接层出问题时的典型特征这里总结一下“连接层问题”而非“数据层问题”的几个典型特征方便快速判断第一domain XML 文件还在。系统级目录/etc/libvirt/qemu/下能看到之前的 XML说明这些 domain 曾经被正常 define 过metadata 并没有丢失。第二磁盘镜像文件还在。默认存储池/var/lib/libvirt/images/下的 qcow2 文件、raw 文件都还在大小也正常没有被清空或损坏。第三virsh list --all是空但换个 URI 就能看到完整列表。比如执行virsh -c qemu:///system list --all后虚拟机全部出现。这种情况几乎可以断定不是虚拟机的错而是你连错了 daemon。第四系统日志里没有大量 block 错误、磁盘错误或 libvirt 启动失败记录。如果 daemon 真的遇到过文件系统损坏journalctl里通常会留下明显的 I/O error 或 libvirt 错误。如果满足以上特征你应该停下来先完成 URI 排查而不是立刻去重建虚拟机。3.3 什么情况下才需要怀疑真正的数据丢失反过来说什么情况才更接近“虚拟机真的丢了”主要有这么几类有人误删了/etc/libvirt/qemu/下的 XML镜像文件所在目录被清理或格式化快照回滚时选错了目标文件系统损坏导致目录结构不完整以及某些自动化脚本误执行了virsh undefine或virsh vol-delete。这些情况下你不仅会遇到“virsh 看不到虚拟机”还会发现 XML 文件和镜像文件本身就没了或者文件虽然叫这个名字但已经变成损坏文件。此时才需要进入数据恢复流程比如从备份中恢复 XML 和镜像。但这篇文章想强调的是在处理“数据丢失”之前先花两分钟排除“连接错误”。因为连接错误的处理成本几乎为零而你一旦在连接错误的状态下执行破坏性修复命令反而可能制造出真正的数据丢失。4. 排查与定位流程4.1 先确认当前连接的是哪个 URI排查的第一步不是看文件而是看当前virsh到底连到了哪个 URI。执行下面这条命令virsh uri它会直接输出当前连接使用的 URI。比如输出qemu:///session说明你现在在用户态 session daemon 的世界里如果输出qemu:///system说明你已经连到了系统级 daemon。同样的逻辑也可以给每个 virsh 命令显式指定 URI。推荐先做一次“全家桶”对比virsh list --all virsh -c qemu:///system list --all virsh -c qemu:///session list --all如果默认命令显示为空但qemu:///system下有虚拟机那问题基本就定位了——你的默认连接不对但虚拟机没有丢。4.2 对比两套世界的虚拟机与存储池确认 URI 之后要习惯把 system 和 session 两个世界的资源都列一遍。主机名、URI、虚拟机列表、存储池列表、网络列表这几样信息在排障时非常关键。virsh -c qemu:///system list --all virsh -c qemu:///session list --all virsh -c qemu:///system pool-list --all virsh -c qemu:///session pool-list --all virsh -c qemu:///system net-list --all virsh -c qemu:///session net-list --all如果 system 下列出了虚拟机只是 session 下是空的那就说明 system daemon 工作正常虚拟机的元数据没有丢。接下来只需要做一件事让默认连接指向 system daemon。如果两套 URI 下都是空那就说明问题可能不只是连接层。这时候再去检查数据和服务状态。4.3 检查 socket 与 daemon 运行状态libvirt daemon 是通过 socket 对外提供服务的。socket 存在daemon 才能被连接socket 不存在或权限不对客户端自然连接失败。可以同时检查 system 级和 session 级的 socket。systemctl status libvirtd virtqemud 2/dev/null || true systemctl --user status virtqemud 2/dev/null || true ls -l /run/libvirt/ /run/user/$(id -u)/libvirt/ 2/dev/null || true ps -ef | grep -E qemu-system|libvirtd|virtqemud正常情况下/run/libvirt/下会看到 system daemon 的 socket 文件/run/user/UID/libvirt/下会看到当前用户的 session socket。如果你发现libvirtd或virtqemud没有启动还要进一步看为什么没有启动是服务被 disabled还是启动时崩溃了。当系统里同时存在 system 级和 session 级两套 daemon 时你可能会看到两个 QEMU 进程分别挂在不同的 daemon 下各管各的虚拟机。这时候必须用明确的 URI 才能区分开。4.4 确认 domain XML 与镜像到底在哪如果需要进一步确认数据完整性可以比较两个目录ls -l /etc/libvirt/qemu/ ls -l ~/.config/libvirt/qemu/ 2/dev/null || true ls -l /var/lib/libvirt/images/ ls -l ~/.local/share/libvirt/images/ 2/dev/null || true/etc/libvirt/qemu/对应 system daemon 的 domain 配置~/.config/libvirt/qemu/对应 session daemon 的 domain 配置。镜像目录同理系统级在/var/lib/libvirt/images/用户级通常在用户主目录下。这一步的目的是建立“文件—daemon—URI”之间的对应关系。如果你在/etc/libvirt/qemu/下看到了 XML那么这些虚拟机应该通过virsh -c qemu:///system来访问。如果你在用户目录下看到了 XML那就是 session daemon 的领域。5. 恢复访问让 virsh 正确连接 system 级 libvirt5.1 临时指定 URI先确认能管理排查发现问题后最稳妥的临时做法是每次执行命令时显式指定 URI。这样不需要修改任何系统配置也能立刻验证“换个 URI 就能看到虚拟机”的判断。假设你的虚拟机在 system daemon 下可以先执行virsh -c qemu:///system list --all virsh -c qemu:///system start my-vm virsh -c qemu:///system console my-vm如果这台虚拟机确实在运行还能看到它的状态virsh -c qemu:///system list如果确认一切正常再继续做永久化配置。临时指定 URI 的好处是不会因为改配置而影响其他管理员的操作习惯缺点是你每次都要手敲-c qemu:///system容易忘。5.2 设置 LIBVIRT_DEFAULT_URI 环境变量最简单、最直接的永久化方案是在 shell 环境中设置LIBVIRT_DEFAULT_URI。修改~/.bashrc或~/.bash_profileexport LIBVIRT_DEFAULT_URIqemu:///system然后让配置马上生效source ~/.bashrc再执行virsh list --all应该就能看到 system daemon 下的虚拟机了。这种配置只对当前用户生效适合个人使用。如果想让整个团队的所有管理员都统一使用 system daemon可以把配置放到/etc/profile.d/下比如新建一个文件/etc/profile.d/libvirt-uri.sh# 文件路径/etc/profile.d/libvirt-uri.sh export LIBVIRT_DEFAULT_URIqemu:///system但这里要特别注意权限问题。普通用户如果没有加入 libvirt 组直接连接 system daemon 会报 Permission denied。所以全局配置要配合用户组权限一起解决。关于权限问题后面小节会单独讲。5.3 通过 libvirt.conf 配置默认 URI环境变量虽然简单但它只影响当前 shell 环境。如果某些 GUI 工具或脚本没有继承你的环境变量还是会走自己的默认 URI。更底层的做法是修改~/.config/libvirt/libvirt.conf让 libvirt 客户端库本身记住默认 URI。配置文件示例# 文件路径~/.config/libvirt/libvirt.conf uri_default qemu:///system配置完成后建议把配置文件权限设置为 600避免其他用户读取你的连接信息chmod 600 ~/.config/libvirt/libvirt.conf这样设置之后多数 libvirt 客户端默认都会连接 system daemon。如果以后你确实要临时测试 session daemon仍然可以用virsh -c qemu:///session显式指定不会互相影响。5.4 团队统一 alias 与权限配置除了环境变量和配置文件很多团队还喜欢用 alias 来固定virsh行为。在~/.bashrc中追加alias virshvirsh -c qemu:///system这样做的好处是非常直观每次敲virsh时相当于自动带上-c qemu:///system。坏处是 alias 会影响所有参数如果你某天真的需要连接别的 URI必须使用\virsh来绕过 alias。所以个人更推荐环境变量或配置文件方案。权限方面需要注意普通用户访问 system daemon 的 socket通常需要加入libvirt组。不同发行版的组名可能略有差异但大多数使用libvirt。可以执行sudo usermod -a -G libvirt $USER修改组之后需要重新登录一次才能生效。如果你发现自己以 root 身份才能连接 system daemon但普通用户连不上第一优先级就是检查用户组和 socket 文件权限。5.5 如果 XML 真的不在了怎么重新定义有一种稍微复杂的情况URI 对了list 是空的但/etc/libvirt/qemu/下的 XML 文件还在。这种情况多半是 daemon 启动时扫描目录失败或者你手动放入了 XML 文件但没有执行 define。解决办法是用virsh define把它加载进去virsh -c qemu:///system define /etc/libvirt/qemu/my-vm.xml执行成功后再virsh list --all就能看到这台虚拟机。如果镜像文件还在但 XML 已经彻底丢失那就需要基于镜像重新创建 domain。这里非常不建议在原镜像上重新安装系统正确做法是用virt-install的 import 模式重建virt-install --name my-vm --memory 2048 --vcpus 2 \ --disk path/var/lib/libvirt/images/my-vm.qcow2,formatqcow2 \ --os-variant detecton,requireoff \ --import --network networkdefault需要提醒的是这种重建方式会生成一份新的 XML之前配置的硬件细节、网卡 MAC、启动顺序等可能都变了只适合“应急恢复”。如果你有备份优先从备份恢复 XML。6. 完整示例双 URI 巡检脚本6.1 脚本要解决什么既然手动检查容易漏而且很多事故都是升级、环境变化后被默认连接坑到的不如直接写一个巡检脚本。它的作用是一次性列出 system 和 session 两套 URI 下的 domain、存储池和网络列表并把结果写入带时间戳的日志文件。出了问题打开日志就能一眼看到“两套世界”的对比。脚本设计目标有三个输出结果清晰失败的时候能明确知道是哪个 URI 连不上可复制到生产环境配合 cron 做日常巡检。6.2 脚本代码下面是一份可以直接复制使用的 bash 脚本文件命名为/usr/local/sbin/libvirt-inventory.sh#!/usr/bin/env bash # 文件路径/usr/local/sbin/libvirt-inventory.sh # 作用巡检 libvirt 两套 URI 下的虚拟机、存储池和网络输出到日志文件 set -uo pipefail URIS(qemu:///system qemu:///session) LOG_DIR${LOG_DIR:-/var/log/libvirt-inventory} STAMP$(date %Y%m%d-%H%M%S) OUT$LOG_DIR/inventory-$STAMP.log mkdir -p $LOG_DIR for uri in ${URIS[]}; do echo $uri $OUT virsh -c $uri uri $OUT 21 || { echo [ERROR] 无法连接 $uri $OUT continue } echo ---- domains ---- $OUT virsh -c $uri list --all $OUT 21 || true echo ---- storage pools ---- $OUT virsh -c $uri pool-list --all $OUT 21 || true echo ---- networks ---- $OUT virsh -c $uri net-list --all $OUT 21 || true done echo 巡检结果已写入: $OUT cat $OUT脚本的核心逻辑很简单遍历两个 URI分别输出 uri、domain、存储池、网络四段信息。如果某个 URI 连不上就明确打印[ERROR] 无法连接并跳过后续检查避免脚本因为一个 daemon 挂了而中断。6.3 给脚本加执行权限并运行拿到脚本后先加上执行权限sudo chmod x /usr/local/sbin/libvirt-inventory.sh sudo /usr/local/sbin/libvirt-inventory.sh运行后日志会写到/var/log/libvirt-inventory/目录下文件名带时间戳。如果系统提示没有写日志目录的权限可以先手动创建并调整属主sudo mkdir -p /var/log/libvirt-inventory sudo chown root:root /var/log/libvirt-inventory sudo chmod 755 /var/log/libvirt-inventory6.4 用 cron 做定期巡检如果希望每天自动巡检一次可以加入 crontab。在多数 Rocky Linux 10 系统上推荐把巡检任务放到/etc/cron.d/libvirt-inventory中# 文件路径/etc/cron.d/libvirt-inventory 0 8 * * * root /usr/local/sbin/libvirt-inventory.sh /var/log/libvirt-inventory/cron.log 21这里指定了用户为 root每天 8 点执行一次。如果某个 URI 连接失败你可以再写一个简单的告警脚本grep [ERROR]日志后发送通知。这个动作留给读者根据自己的监控体系去扩展。6.5 脚本能输出什么脚本运行后的日志大致长这样具体内容会随机器环境不同而不同 qemu:///system com.qemu.uri qemu:///system ---- domains ---- Id Name State ------------------------------ 3 my-vm running - old-vm shut off ---- storage pools ---- Name State Autostart --------------------------------- default active yes ---- networks ---- Name State Autostart --------------------------------- default active yes qemu:///session com.qemu.uri qemu:///session ---- domains ---- Id Name State ------------------------------ ---- storage pools ---- Name State Autostart --------------------------------- ---- networks ---- Name State Autostart ---------------------------------看到这样的输出基本就可以得出结论qemu:///system下有一批生产虚拟机qemu:///session下是空的管理员之前看不到虚拟机只是默认连接落到了 session。7. 运行结果与效果验证7.1 如何判断恢复成功配置好LIBVIRT_DEFAULT_URI或uri_default之后验证成功非常简单执行virsh list --all如果能看到原来那批虚拟机就说明默认连接已经正确。进一步确认虚拟机可以正常管理可以执行virsh uri virsh list --all virsh domiflist my-vmvirsh uri显示的是qemu:///systemlist --all能看到域名domiflist能列出网卡接口说明 domain 对象可被正常访问。7.2 启动一台虚拟机做最终验证如果原来的虚拟机是关机状态可以挑选一台不影响业务的测试虚拟机做启动验证virsh start my-vm virsh list看到状态从空白变为 running基本可以放心。需要注意的是如果虚拟机之前就是在运行中但virsh list显示为空那说明 session daemon 和 system daemon 对同一台宿主机的资源可见性不一致virsh start并不需要通过“重新定义”来恢复。7.3 如果还是失败按顺序检查哪里如果修改默认 URI 后依然看不到虚拟机建议按这个顺序排查先看 libvirtd 或 virtqemud 服务状态systemctl status libvirtd virtqemud journalctl -u libvirtd -n 50 --no-pager journalctl -u virtqemud -n 50 --no-pager再看 socket 文件是否存在ls -l /run/libvirt/如果/run/libvirt/下没有 socket说明 daemon 根本没起来。再检查普通用户是否能访问 socketls -l /run/libvirt/libvirt-sock groups $USER如果权限不对会出现 Permission denied。最后如果系统开启了 SELinux 且处于 enforcing 模式还要检查是否有 AVC 拒绝记录getenforce ausearch -m avc -ts recent | tail -50SELinux 导致的连接失败比较隐蔽日志里可能只显示 generic error。看到大量 AVC 记录时要根据具体 type 去调整文件上下文或布尔值不建议直接关闭 SELinux。8. 常见问题与排查思路下面整理一份我在实际运维中觉得比较高频的 libvirt 问题排查表问题现象可能原因排查方式解决方案virsh list --all显示为空默认连接到了 session daemonvirsh uri显式指定-c qemu:///system或配置默认 URI连接报 Permission denied用户不在 libvirt 组或 socket 权限不足groups $USER、ls -l /run/libvirt/将用户加入 libvirt 组后重新登录无法连接 socket文件不存在libvirtd/virtqemud 未启动systemctl status libvirtd virtqemudsystemctl enable --now libvirtd服务启动失败端口冲突或配置文件错误journalctl -u libvirtd -n 50根据日志修改配置或切换 modular daemonsystem 级可看到虚拟机但启动失败磁盘文件权限或 SELinux 上下文错误journalctl -u libvirtd、getenforce调整文件权限或恢复 SELinux 文件上下文qemu 进程找不到ps 命令 grep 条件过窄pgrep -a qemu用ps -ef配合更宽泛的关键词检查升级后多个 daemon 同时存在modular daemon 架构导致 socket 增多systemctl list-unit-files | grep -iE libvirt|virt统一 URI根据实际需要启用或禁用对应 daemonvirsh undefine误删 XML操作失误检查备份、dumpxml 记录从备份恢复 XML或基于镜像重建 domain连接超时网络问题或 daemon 响应慢ping、journalctl检查网络连通性、服务负载和日志这里展开讲一下最容易踩的两个点。第一个是权限问题。普通用户连接qemu:///system常常会遇到类似Permission denied或Failed to connect socket的报错。解决办法是让用户加入libvirt组然后重新登录。但要注意某些发行版还要求 polkit 规则允许该用户访问 libvirt 服务。如果在桌面环境用virt-manager通常弹窗会要求认证在纯命令行环境就需要提前把组和 polkit 配好。第二个是“升级后 daemon 冲突”。在 RHEL 9 之后的版本中系统可以同时存在传统libvirtd和 modular daemon 的 socket。默认情况下某些命令会连到libvirtd管辖的 socket某些可能连到virtqemud如果两者各自加载了不同的配置就会出现“刚才还能看到虚拟机换一个命令又看不到了”的诡异现象。遇到这种情况不要急着把某个 daemon 禁用先确认你的 URI 是否明确再考虑是否禁用 session daemon 或只保留其中一套。9. 最佳实践与工程建议9.1 把 URI 写进运维文档团队协作时最容易出现的问题就是“每个人的环境变量不一样”。建议在服务器初始化文档、README、runbook 里明确写清楚管理本机虚拟机统一使用qemu:///system并给出环境变量配置样例。这样无论是谁接手都不会因为默认连接问题产生误判。同时建议在脚本开头固定 URI。不要依赖调用者的环境变量# 脚本开头固定 URI URIqemu:///system virsh -c $URI list --all这样即使有人在 shell 里设置了LIBVIRT_DEFAULT_URIqemu:///session你的脚本依然能连到正确的位置。9.2 定期备份 domain XMLvirsh dumpxml是最容易被忽视的备份手段。很多管理员会备份虚拟机的磁盘镜像却忘了备份 XML。一旦 XML 丢失恢复成本会高很多。建议每次修改虚拟机配置后执行一次mkdir -p /backup/libvirt/domains virsh -c qemu:///system dumpxml my-vm /backup/libvirt/domains/my-vm.xml也可以写一个定时任务每天把所有 domain 的 XML 都导出一次。这样即使真的发生误删也能快速恢复元数据。9.3 生产环境只用 system daemon在服务器上跑生产虚拟机强烈建议只使用qemu:///system并关闭或明确隔离 session daemon。session daemon 更适合桌面开发环境放在服务器上不仅容易造成混淆还会增加权限管理的复杂度。如果有多个管理员统一采用“系统级 daemon libvirt 组权限”的方案而不是让每个人在自己的用户会话里管理虚拟机。这样所有的 domain XML、镜像、日志都集中在系统目录audit 起来也方便。9.4 破坏性操作前先看 URI 与快照任何涉及删除的操作例如virsh undefine、virsh vol-delete、pool-destroy执行前至少执行一次virsh uri确认自己不在 session daemon 的世界里。如果再小心一点可以先打快照或导出 XML。在底层文件系统支持的情况下迁移或配置变更前使用快照可以极大降低回滚成本。9.5 善用最终巡检脚本文中的双 URI 巡检脚本完全可以纳入日常巡检体系。不要只在出问题时才去跑它而是把它加入 cron每天输出一份“两套世界”的资源清单。一旦某天虚拟机列表、存储池列表出现异常日志会告诉你变化是从什么时候开始的这比事后猜要可靠得多。9.6 关注 SELinux 和文件权限Rocky Linux 默认开启 SELinux如果虚拟机迁移或磁盘文件权限变化后启动失败首先要怀疑 SELinux 文件上下文。常用处理方式是检查 AVC 日志而不是直接 setenforce 0。生产环境关闭 SELinux 虽然能省事但会引入更大的安全隐患。10. 总结与后续学习方向这篇文章想传递的核心思路其实只有一句话在 Rocky Linux 10 这类新版本系统上执行 virsh 命令前先确认 URI再谈虚拟机是否存在。libvirt 已经变成多 daemon、多 socket、多数据目录的复杂架构很多“虚拟机丢失”只是连接漂移导致的假象。用virsh -c qemu:///system list --all和virsh -c qemu:///session list --all对比一下通常就能真相大白。如果你现在正好在处理类似问题建议按照文中的顺序做一次完整检查先virsh uri再对比两套 URI 下的列表最后检查 socket 和数据目录。确认是连接问题后直接把LIBVIRT_DEFAULT_URIqemu:///system写进环境变量或~/.config/libvirt/libvirt.conf中彻底解决默认连接漂移。后续如果想继续深入可以从几个方向入手学习virsh snapshot和块设备备份把虚拟机的元数据和数据都纳入备份体系研究virt-install的自动化部署方式把创建虚拟机的过程代码化再进阶一点可以了解 libvirt 的 modular daemon 与 polkit 权限模型搞明白每个 socket 背后的安全边界。这样你在 Rocky 10 上的虚拟化运维会越来越稳。
返回列表