做了这么多年KVM虚拟化运维,如果让我选一个必须熟练掌握的命令行工具,我会毫不犹豫地选virsh。它是libvirt对外暴露的标准管理入口,几乎所有虚拟化相关操作——建虚机、启停、改配置、做快照、管网络,都能用virsh完成。说实话,早期我也习惯在virt-manager里点鼠标,觉得图形界面直观,直到某次机房意外断电,一堆虚机在没有VNC、SSH不通的情况下只能靠一条virsh命令行恢复,从那以后virsh就成了我判断一台宿主机是否“健在”的第一道关卡。这篇文章不打算把virsh的帮助手册翻译一遍,而是想以实际运维视角,把那些真正用得上的命令、常见的坑和排查思路讲清楚,适合正在接触KVM、或者已经管了一段时间虚机但对virsh还不够系统的朋友参考。
1. 为什么我坚持用virsh而非图形管理工具
1.1 virsh能干什么、不能干什么
virsh的本质是libvirt提供的一个命令行客户端,libvirt本身是一套虚拟化管理API,对上支持virsh、virt-manager、OpenStack等各类工具,对下对接QEMU/KVM、Xen、LXC等不同hypervisor。这意味着你学会的virsh操作,换一套底层的虚拟化后端,命令风格基本不变,这比直接操作qemu-system-x86_64的命令行要通用得多。
日常工作中virsh能覆盖的事情大致分几类:
- 虚机的完整生命周期管理:define、create、start、shutdown、reboot、destroy、undefine。
- 运行态信息查询:dominfo、vcpuinfo、domiflist、domblklist、domstats。
- 配置变更:setvcpus、setmem、attach-disk、detach-disk、attach-interface。
- 快照与克隆:snapshot-create-as、snapshot-list、snapshot-revert、virt-clone配合。
- 网络与存储管理:net-list、net-create、pool-list、vol-create-as。
- 迁移与调度:migrate、vcpupin、autostart。
它不能做的事也很明确:virsh不像一个带界面的管理平台那样有告警、历史趋势图、权限审批流,它更适合作为“操作层”的工具,而监控告警还是要交给底层系统或专门平台来做。另外virsh也不能替代对存储系统和物理网络的管理,它只是把虚拟化资源组织起来,底层物理资源的问题还是得回到操作系统层面去处理。
1.2 一个让我彻底转向virsh的救急现场
有一次机房因为制冷故障触发高温告警,部分物理机被自动保护性断电,等我们到了现场,发现好几台KVM宿主机起来了,但上面的虚机没有跟着启动。更要命的是,带图形界面的管理节点也一起断电了,virt-manager根本连不上。当时我手里只有一台笔记本,通过带外管理登录到宿主机,用一串virsh命令把关键业务虚机按优先级依次拉起:
virsh list --all virsh start web-node-01 virsh start db-node-02整个过程不到五分钟。如果依赖图形界面,可能需要先恢复管理节点,再等页面加载,再去一台台启动,这个时间差在故障场景里是致命的。从那次之后,我就养成了在任何一台KVM宿主机上都先确认virsh可用的习惯,而且写脚本批量处理虚机启停时,唯一靠谱的接口就是virsh命令行。
2. 连接方式与认证:virsh连接的不是“本机”那么简单
2.1 local、ssh、tcp三种连接方式怎么选
virsh支持通过不同的URI连接目标libvirt服务,最常用的三种是:
| URI格式 | 说明 | 适用场景 |
|---|---|---|
| qemu:///system | 连接本机的system libvirtd守护进程 | 日常单机管理,权限最高 |
| qemu:///session | 连接当前用户的session libvirtd | 无root权限的普通用户测试 |
| qemu+ssh://user@host/system | 通过SSH隧道远程连接 | 远程管理、脚本批量操作 |
之前踩过一个坑:直接用virsh -c qemu:///system list没问题,但一旦想用普通用户身份远程执行,忘了带+ssh,就会报类似“failed to connect to the hypervisor”的错误。其实问题不是权限不够,而是URI写错了。远程管理时如果不确定libvirtd监听的是TCP还是本地socket,最稳妥的方式就是用SSH隧道,除非你明确配置了TCP监听。
2.2 qemu:///system 和 qemu:///session 的差异
不少新手区分不清这两个URI。qemu:///system连接的是系统级的libvirtd,它管理的虚机默认放在/var/lib/libvirt/images,配置存于/etc/libvirt/qemu,服务以root身份运行,虚机可以用高权限访问设备。qemu:///session则是用户级的会话,虚机配置放在用户目录下,适合做开发测试,但缺少很多系统级能力,比如某些网络模式、设备直通就用不了。
我在生产环境只建议用system,因为session模式下的网络、存储管理和权限模型都有限制,排查问题也相对复杂。如果你在普通用户下执行virsh list --all看到的是空列表,而root用户能看到一堆虚机,别急着怀疑虚机丢了,先检查当前是不是落在了session域。
2.3 连接报错的定位思路
最常见的连接报错是:
error: failed to connect to the hypervisor error: Cannot recv data: Connection reset by peer遇到这类问题,我的排查顺序是:
- 确认libvirtd是否在运行:
systemctl status libvirtd - 用
virt-host-validate检查KVM相关内核模块是否加载 - 检查socket文件和权限:
ls -l /var/run/libvirt/libvirt-sock - 如果是远程连接,测试SSH是否通:
ssh user@host 'virsh list --all'
有一次排查了半天,最后发现是宿主机上libvirtd服务因磁盘满而崩溃,socket文件残留导致一切连接异常。这类问题用dmesg看看系统日志往往比盯着virsh报错更有效。
3. 虚机生命周期管理:从define到undefine的完整闭环
3.1 define与create的差异:配置与运行的分离
这是虚拟化运维里最容易理解偏的一个点。
virsh define只做一件事:把一份XML配置注册到libvirt,让libvirt“知道”这台虚机的存在,但虚机不会启动。virsh create则是先define再启动,一步到位。区别在于,用create创建的虚机如果后来被shutdown,它不会出现在配置里,下次要用还得重新create;而define创建的虚机配置文件常驻,生命周期可管理。
我个人的习惯是,凡是正式业务虚机一律走define,然后手动start。这样配置文件落盘,即使宿主机重启,也能通过virsh list --all看到虚机的存在,再配合autostart就可以实现开机自启。临时测试用的虚机可以用create,用完destroy就行,不污染现有配置。
3.2 启停策略:start、shutdown、destroy、reboot到底差在哪
很直观的区别:
virsh start:从关机状态启动虚机,等价于物理机按电源键。virsh shutdown:发送ACPI关机信号,让虚机内的操作系统执行正常关机流程,对应桌面系统的“开始菜单→关机”。virsh reboot:发送重启信号,也是优雅操作。virsh destroy:强制断电,相当于直接拔电源线,虚机内未落盘数据可能丢失。
生产环境里最怕有人把destroy当关机用。destroy不会通知guest OS做任何清理,数据库、业务状态都可能损坏。我见过同事在维护窗口期执行了virsh destroy db-server,后来数据库起不来,只能跑fsck和日志恢复,折腾了大半天。正确的操作顺序是:优先virsh shutdown,如果虚机内有重要服务,先登录系统用标准关机命令,确需强制时再destroy。
reboot同样有风险,如果guest内有些服务对信号响应不及时,表现为虚机一直不重启,这时候可以考虑向系统内发送reboot命令,或者destroy后重新start,后者虽然粗暴但成功率高。
3.3 守护配置:autostart和动态迁移
virsh autostart经常被忽略,但它非常关键。它控制虚机是否在libvirtd启动时跟着启动。以我在生产环境的习惯,关键业务虚机必须开启:
virsh autostart web-node-01 virsh autostart --disable web-node-02这条命令本质上是在/etc/libvirt/qemu/autostart目录里生成软链接,而不是修改虚机本身配置。所以你在virsh dumpxml里看不到autostart相关字段,别觉得奇怪。
迁移方面,virsh migrate --live vm-name qemu+ssh://target-host/system可以在两台宿主机之间做在线迁移,但前提是共享存储或者迁移前的镜像路径两边一致。这里提醒一句:迁移前一定要在两台宿主机上都跑一次virsh list --all确认命名空间不冲突,还要检查CPU型号和virtio驱动是否兼容,否则迁移过程中会直接失败或者虚机启动蓝屏。
4. 热调整配置:虚机资源变更的实操要领
4.1 vCPU和内存的热调整
线上业务扩容时,重启虚机往往不可接受,这时候热插拔就是刚需。
查看当前vCPU拓扑和在线状态:
virsh vcpuinfo vm-name在线添加vCPU,同时保证重启后依然生效:
virsh setvcpus vm-name --count 4 --live --config这里容易忽略的是--maximum参数。如果之前定义虚机时用的是老式XML,没有设置vcpu current和maximum的层次结构,直接setvcpus --live可能会报“无法修改活动配置”。正确做法是先设置最大值:
virsh setvcpus vm-name --maximum 8 --config virsh setvcpus vm-name --count 4 --live --config--config表示写入持久化配置,--live表示作用于当前运行状态,两者都带上才能保证“这次生效+重启不丢”。只加--live,虚机一重启就回到老配置,这算是最常见的配置丢失问题之一。
内存热调整类似,但要注意内存热插拔的上限受XML里<maxMemory>限制。我遇到过想给虚机加内存,结果怎么都加不上去,最后dumpxml一看,maxMemory还是之前设置的低值,得先改maxMemory再用setmem调整当前内存。
4.2 磁盘扩容:从qemu-img到blockresize
先给虚机磁盘镜像文件扩容,再在guest内扩展分区或文件系统,顺序反了会出问题。镜像文件扩容用qemu-img:
qemu-img resize /var/lib/libvirt/images/vm01.qcow2 +20G然后让宿主机的虚拟块设备感知新容量:
virsh blockresize vm-name vda 40G这里的vda是虚机内显示的磁盘名称,对应宿主机侧的目标磁盘。如果你不确定名字,用virsh domblklist vm-name查看。
扩完宿主侧,还需要进入虚机内部执行分区调整和文件系统扩展,比如在Linux里跑growpart和resize2fs或xfs_growfs。很多新手卡在这一步,以为qemu-img和blockresize执行完就完事了,结果虚机里df一看容量还是老的。
4.3 修改XML后的检查习惯
有些配置没法通过setxxx命令完成,比如添加串口、修改启动顺序、绑定CPU自动调度,这时要直接编辑XML:
virsh edit vm-name这个命令会调用默认编辑器并检查XML语法,修改后关闭编辑器自动生效。但生产环境我建议多一步校验:
virsh dumpxml vm-name > /tmp/vm-backup.xml virsh edit vm-name virsh dominfo vm-name修改前先dump一份XML备份,改完用dominfo确认运行状态没被破坏。这条经验来自一次教训:我改过一台虚机的<cpu>模式,结果保存后虚机直接失联,客制化CPU模式与宿主不相容,最后靠备份XML回滚才恢复。
5. 快照与克隆:备份和交付的正确姿势
5.1 内部快照的创建、回滚与删除
快照是虚机在某个时间点的状态记录,使用qcow2镜像时,快照可以记录内存、磁盘和配置状态。
创建快照:
virsh snapshot-create-as vm-name snap-before-upgrade "before upgrade" --disk-only加上--disk-only表示只对磁盘做快照,不包含内存状态。如果不加,快照还会包含内存,但可能要求虚机暂停,这对在线业务不够友好。
查看快照列表:
virsh snapshot-list vm-name回滚到某个快照:
virsh snapshot-revert vm-name snap-before-upgrade删除快照:
virsh snapshot-delete vm-name snap-before-upgrade快照回滚时有个容易忽略的风险:如果快照创建之后你又新增了磁盘设备或修改了网络配置,回滚时虚机的设备状态和当前配置可能不一致,导致虚机起不来。我在测试环境做过一次恢复,回滚之后虚机卡在BIOS阶段,后来检查发现是因为回滚的XML引用了一个已删除的磁盘镜像。所以快照不只是“还原数据”,你还得关注设备绑定关系。
5.2 克隆虚拟机的方式对比
克隆虚机不是virsh自带的命令,而是要用virt-clone工具。但实际管理场景中,克隆通常涉及三个步骤:复制镜像、定义新虚机、修改guest配置。
最简单的方式:
virt-clone --original vm-base --name vm-new --auto-clone--auto-clone会自动处理磁盘复制和网络MAC地址修改。但如果你想用传统方式,也可以手动做:
cp /var/lib/libvirt/images/vm-base.qcow2 /var/lib/libvirt/images/vm-new.qcow2 virsh dumpxml vm-base > /tmp/vm-new.xml # 编辑vm-new.xml中的name、uuid、mac地址、磁盘路径 virsh define /tmp/vm-new.xml手动方式更适合批量生成虚机,改XML时把uuid、mac、disk路径全部替换成新值,否则会和原虚机冲突。
5.3 快照与克隆后的常见故障
克隆后的虚机最常见问题是网络配置冲突。因为克隆时如果只改了宿主机侧的MAC地址,guest内的网络配置文件还写着原来的IP或MAC绑定,就会出现虚机启动了但网络不通。解决办法是克隆完成后登录guest,删除或重置网络接口配置,让DHCP重新分配或手工改IP。
快照回滚后我碰到过文件系统不一致的情况。如果快照是--disk-only方式,内存状态没有包含进去,回滚后数据库、缓存类服务可能处于不一致状态,需要像处理崩溃恢复那样检查日志。所以对数据库这类有严格一致性要求的服务,更推荐借助专业备份工具,而不是单纯依赖快照。
6. 网络与存储:虚拟基础设施的两个关键篮子
6.1 物理网卡到虚拟网卡的链路
virsh管理虚拟网络的核心对象是“网络”,通过virsh net-list可以查看当前已定义的虚拟网络。常见的有NAT模式的default网络、隔离网络和桥接网络。
排查网络问题前,先看虚机实际接入了哪个网络:
virsh domiflist vm-name这个命令会列出虚机所有网卡的接口信息,包括MAC地址、对应的libvirt网络名、以及宿主机侧桥接设备。有一次虚机无法访问外网,我用这个命令发现它接入的是隔离网络而不是桥接网络,问题一下子就定位到了。
新增网卡:
virsh attach-interface vm-name bridge br0 --model virtio --config --live移除网卡需要知道MAC地址:
virsh detach-interface vm-name bridge --mac 52:54:00:xx:xx:xx --config --live需要注意bridge后面的类型要和创建时一致。用--config和--live同时保证本次生效和重启保留。
6.2 存储池与卷的日常维护
存储池抽象了宿主机上的物理存储位置,可以是目录、LVM卷组、iSCSI设备等。平时用目录型池比较多:
virsh pool-define-as default dir --target /var/lib/libvirt/images virsh pool-start default virsh pool-autostart default创建虚拟磁盘卷:
virsh vol-create-as default vm-new.qcow2 20G --format qcow2为虚机附加这个卷:
virsh attach-disk vm-name /var/lib/libvirt/images/vm-new.qcow2 vdb --cache none --persistent这里我踩过--cache的坑。默认的cache模式在某些场景下性能虚高但数据安全差,宿主机一旦断电,虚机磁盘数据可能不一致。生产环境我一般显式指定--cache none或writeback,并且根据业务读写特征调整。
6.3 从domiflist和domblklist定位故障
虚机网络不通或磁盘丢失,第一反应不应该是登录guest排查,而是先在宿主机侧确认设备是否存在。
virsh domblklist vm-name virsh domiflist vm-name如果domblklist里某个磁盘路径显示-,说明设备定义缺失或后端文件已被删;如果domiflist里没有网卡记录,可能是驱动未加载或配置丢失。这两条命令的输出非常直观,能快速把故障边界从“虚机内部问题”缩小到“宿主侧配置问题”。
有一次用户报告某台虚机突然无法写入数据,我运行domblklist后发现磁盘文件厂商路径变成了一串不存在的路径,进一步排查发现是NFS挂载掉了,虚机的磁盘后端起不来。这个信息量在guest里可能只会显示“Input/Output error”,完全没方向,而在宿主侧一眼就能看到根因。
7. 日常排障:从virsh输出读出虚机健康状况
7.1 关键状态解读
virsh list --all显示第一列是Id,运行态的虚机有非零Id,关闭状态的虚机Id显示-。名字后面的状态列有几种常见值:running、shut off、paused、crashed。如果有虚机显示paused,多半是存储后端掉线或者宿主机资源耗尽触发了暂停机制。
virsh dominfo vm-name能给出更详细的配置信息,包括CPU、内存、autostart状态,以及虚机是否在运行。我通常会和virsh vcpuinfo配合使用,因为dominfo显示的CPU数是配置值,vcpuinfo显示的才是实际调度情况。两个值不一致时,要考虑热插拔后guest内驱动是否加载成功。
7.2 定位“假死”虚机的完整思路
虚机表现为“SSH连不上、Ping不通、控制台无响应”,但virsh list还显示running,这种情况我称之为“假死”。排查思路一般是这样:
- 确认虚机进程还在:
ps -ef | grep qemu | grep vm-name - 查看宿主机负载和I/O情况:
top、iostat -x 1 - 尝试在宿主侧通过
virsh suspend和virsh resume把虚机暂停再恢复,相当于“唤醒”一次 - 如果还不行,用
virsh send-key vm-name KEY_LEFTCTRL KEY_LEFTALT KEY_DELETE发送Ctrl-Alt-Del组合键让guest重启 - 实在不行才
destroy后重新start
第4步很多人不知道:send-key可以直接向虚机发送键盘事件,不需要agent,也不依赖网络。在虚机系统完全无响应、但进程还在的时候,这一招经常能把系统从假死状态拉回来。我处理过好几起类似故障,都是先尝试那种优雅唤醒,实在不行再强制重启,尽量把数据损坏概率降到最低。
7.3 我的几个习惯性操作
每天上工或者巡检宿主机时,我习惯跑这几条命令:
virsh list --all virsh dominfo <每台在线虚机> virsh vcpuinfo <核心虚机> virsh domblklist <关键业务虚机>批量巡检时可以用循环脚本,比如把所有虚机的内存在3秒内汇总出来:
for vm in $(virsh list --name); do echo "$vm: $(virsh dominfo $vm | grep 'Max memory' | awk '{print $3}') KB" done另外一个习惯是,在改任何虚机配置前,先执行:
virsh dumpxml vm-name > ~/backup/vm-name-$(date +%Y%m%d).xml这个动作不花时间,但在一次错误配置后,可能就是逃生通道。最后再分享一个技巧:如果你不确定某条命令的精确参数,virsh help比上网搜索更快,直接virsh help blockresize、virsh help setvcpus,libvirt会把这条命令的完整参数和说明打出来,比我记忆里的任何整理都全。这也是我用virsh几年下来最推荐的“文档”来源。