做虚拟化运维这几年我发现自己最常用的工具不是图形化管理平台而是virsh这个命令行工具。virsh是libvirt提供的命令行管理客户端只要宿主机跑着KVM/QEMU它就能帮你把创建虚拟机、开关机、查看状态、网络配置、快照管理这一整套操作全部收敛成一句句可复用的命令。它没有花哨的界面但胜在稳定和直接尤其适合批量管理、脚本化交付和远程排查。这篇内容就围绕virsh最常用的那些操作展开我会从环境准备讲起带你把虚拟机生命周期、日常监控、存储网络配置走一遍最后整理一些实战中容易踩的坑和排查思路。不管你是刚接触虚拟化的新手还是打算从virt-manager切到纯命令行管理的老手都可以按这篇内容动手试一遍。1. 环境准备先搞清楚virsh背后的libvirt1.1 安装组件时别只装了客户端virsh本身只是一个命令行客户端真正干活的是一整套libvirt服务加qemu-kvm进程。你可以把virsh理解成遥控器遥控器再高级电视没接电源也白搭。所以安装时要看清楚至少需要这几块libvirt服务端提供libvirtd守护进程负责创建虚拟机、管理网络和存储池。libvirt客户端包含virsh命令本身。qemu-kvm宿主机上虚拟机的实际执行引擎。在Debian/Ubuntu系列上我一般直接装apt install libvirt-daemon-system libvirt-clients qemu-kvm在RHEL/CentOS/Fedora系列上对应的包名是yum install libvirt-daemon libvirt-client qemu-kvm安装完成后先确认libvirtd服务状态正常。新版本的libvirt默认采用socket激活模式也就是当你第一次执行virsh命令时systemd会自动拉起libvirtd服务这是正常行为不需要惊慌。systemctl status libvirtd如果显示activerunning就可以继续如果显示inactive先执行systemctl start libvirtd再确认/var/run/libvirt/libvirt-sock这个sock文件存在。还有一个我一直强调的点日常操作不要用root硬怼建议把当前用户加入libvirt用户组这样既安全又方便统一权限管理。usermod -a -G libvirt $(whoami)注意改完用户组后需要退出重新登录组权限才会生效。如果你发现执行virsh list还提示权限不足问题基本都出在这一步。1.2 system和session两种连接方式的取舍virsh连接libvirt服务时最常用的URI是两个qemu:///system和qemu:///session。这两个连接方式决定了你管理的虚拟机层级和权限范围。qemu:///session是用户级连接不需要root权限管理的是当前用户自己启动的虚拟机。听起来很方便但它没有系统级网络、没有全局存储池很多生产环境需要的特性都用不了。我一般只在本地快速测试时才用它。qemu:///system是系统级连接管理的是整个宿主机上所有用户的虚拟机。它的权限更高能操作虚拟网络、存储池资源也说“虚拟机开机自启”这类系统级配置。生产环境统一用这个URI不要纠结。我建议把默认连接方式写到环境变量里避免每次敲-c qemu:///systemexport LIBVIRT_DEFAULT_URIqemu:///system然后随时可以用virsh uri确认当前连接到了哪个实例。我刚接手一台新宿主机时第一件事就是跑这个命令先把连接环境确认清楚后面操作才不会挂在莫名奇妙的权限问题上。1.3 第一次连接前先跑这几个检查命令环境准备好后我会习惯性地跑一组基础检查命令确认libvirt服务、默认网络和存储池都是可用的virsh list --all virsh net-list --all virsh pool-list --allvirsh list --all会列出宿主机上所有虚拟机包括已经关机的net-list --all查看当前可用的虚拟网络一般至少会有一个名为default的NAT网络pool-list --all则显示存储池。如果这三个命令都能正常返回结果说明环境基本就绪。如果net-list是空的先别急着建虚拟机把default网络找回来再说。2. 虚拟机生命周期从定义到销毁2.1 定义虚拟机XML配置是核心virsh管理虚拟机有一个核心概念每台虚拟机都对应一份XML描述文件里面写明了虚拟机的名称、内存、CPU、硬盘、网卡、启动顺序等所有属性。你可以把这份XML理解成虚拟机的“出生档案”libvirt依据它来创建和运行虚拟机。定义一个虚拟机通常有两种方式virsh define和virsh create。define是把XML注册到libvirt中虚拟机会出现在virsh list --all里但不会立即启动create则直接根据XML启动虚拟机并且是临时运行宿主机重启后这台虚拟机就“消失”了。生产环境我几乎只用define因为它能保证虚拟机配置被纳入libvirt的管理范畴方便后续查询和自启。一份最简的XML长这样domain typekvm namedemo/name memory unitMiB2048/memory vcpu placementstatic2/vcpu os type archx86_64 machinepc-q35-6.2hvm/type boot devhd/ /os devices disk typefile devicedisk driver nameqemu typeqcow2/ source file/var/lib/libvirt/images/demo.qcow2/ target devvda busvirtio/ /disk interface typenetwork source networkdefault/ model typevirtio/ /interface /devices /domain我平时不会手写整份XML一般是先用virt-install或图形工具创建一台参考虚拟机然后通过virsh dumpxml demo导出XML在这基础上改名字、改磁盘路径。这样比从零写要稳妥得多毕竟XML里有些隐藏字段比如设备地址、控制器类型手写容易漏。virsh define /path/to/demo.xml执行define后没有任何报错就说明XML格式没问题虚拟机已经注册成功。提示拿到一份陌生XML时先用virsh define去验证它而不要直接create。define只做语法和结构校验不会真的启动进程排错会更安全。2.2 开关机和重启的正确姿势定义好虚拟机后启动它很简单virsh start demo但真正涉及关机和重启时很多新手会踩坑。virsh的关闭操作分几种各自语义完全不同操作命令行为适用场景优雅关机virsh shutdown demo发送ACPI关机信号虚拟机内部系统正常关机需要安全停止服务的场景强制关机virsh destroy demo模拟拔掉电源立即终止虚拟机进程系统卡死、测试环境应急优雅重启virsh reboot demo发送ACPI重启信号需要重启虚拟机内部系统强制重启virsh reset demo模拟硬件复位不经过系统关停内核卡死快速恢复shutdown命令要求虚拟机内部内核支持ACPI并且系统里没把电源按钮事件屏蔽掉。很多精简版系统镜像默认不会响应ACPI信号导致你执行shutdown后虚拟机毫无反应。这时候先别急着destroy先尝试virsh send-key demo KEY_ENTER或者KEY_CTRLKEY_ALTKEY_DELETE让系统内部触发一次C-A-D重启。如果设备上跑的是已有业务的虚拟机我绝对不会先用destroy因为那等于直接拔电源磁盘缓存里的数据可能没来得及落盘。2.3 删除虚拟机并不是删个文件那么简单清理一台虚拟机时很多没经验的同学会把virsh undefine当成删除键然后跑完发现磁盘文件还在怎么解释都解释不清。undefine默认只删除XML配置不会动虚拟机的磁盘文件这其实是libvirt刻意设计的安全行为。想一想误删一台虚拟机的配置还有机会重新define回来误删磁盘文件就真的什么都找不回来了。删除一台虚拟机我的标准流程是# 1. 优雅关闭如果关不掉再考虑destroy virsh shutdown demo virsh destroy demo # 2. 删除配置本身 virsh undefine demo # 3. 确认磁盘文件路径 virsh vol-list default # 4. 确认无误后再手动删除磁盘文件 rm /var/lib/libvirt/images/demo.qcow2如果你想一次性删掉配置和磁盘可以用--remove-all-storage参数但我强烈建议不要在生产环境用这个全量参数。它会把这台虚拟机关联的所有存储全部干掉包括可能共用的物理磁盘分区。更稳妥的做法是先用domblklist查看这台虚拟机用了哪些磁盘再逐个删除。3. 日常查询与状态监控3.1 list命令的几种姿势日常运维中最频繁的操作就是查看宿主机上有哪些虚拟机、它们处于什么状态。virsh list的默认输出只显示正在运行的虚拟机想要看全部必须加--all。我这里列几个平时用得多的变体virsh list --all virsh list --state-running virsh list --state-shutoff virsh list --state-paused--state-running只看在运行的--state-shutoff只看已关机的--state-paused则是暂停中。批量运维脚本里我经常会用virsh list --name --state-running直接取运行中虚拟机的名字列表省掉一堆解析步骤。我自己还有一个习惯每当要批量操作之前先跑一遍virsh list --all加上virsh dominfo把状态和配置确认清楚。盲目地执行start或shutdown很容易对错虚拟机。比如两台虚拟机同名的不同实例虽然libvirt内部不允许同名域存在但不同宿主机上的同名域在批量脚本里也容易出现混淆。3.2 CPU、内存、磁盘怎么看启动后想了解虚拟机内部资源使用情况virsh也提供了对应的命令virsh vcpuinfo demo这个命令会列出vCPU的亲和性、运行时间、CPU占用时间等信息。判断虚拟机是否吃满CPU我一般看virsh domstats里的vcpu.time它表示vCPU累计占用的CPU时间对比两次采样差值就能算出实时负载。内存方面用virsh dommemstat它能输出实际的available和rss值数值的单位是KiB。rss就是qemu进程实际占用的物理内存它往往比XML配置的内存量要大一些因为qemu进程本身还有额外开销。如果rss常年超过配置值很多就要考虑是不是虚拟机内部内存压得比较死或者需要开启内存气球设备。磁盘信息用virsh domblklist查看它会列出一台虚拟机当前挂了哪些块设备以及对应的镜像文件路径。例如virsh domblklist demo这个命令在做磁盘扩容、迁移镜像前必须跑一遍确认源文件和目标设备一一对应。你以为是vda的设备实际操作时发现是sda那后面的JSON配置、分区操作全都会错位。3.3 想进入虚拟机的两种方式console和图形协议有一段时期我管理虚拟机完全依赖SSH直到有些虚拟机网络配置出错我才意识到必须掌握virsh console这个救急手段。virsh console demo会接入虚拟机的串口控制台但有个前提虚拟机内部系统需要启用串口终端对于Linux启动参数里要加上consolettyS0并且/etc/default/grub里配置好后执行update-grub。如果没配置执行virsh console后会卡在一个空界面按回车没反应这不是命令坏了而是虚拟机根本没往串口输出。如果你需要图形界面访问可以用virsh vncdisplay demo查询虚拟机当前的VNC显示编号然后通过本机VNC客户端连接。但VNC协议本身不安全建议通过SSH隧道转发到本地再连。这里我多说一句在纯内网环境只要宿主机SSH能连通VNC隧道访问就很丝滑跨公网环境别直接把VNC端口暴露出去。4. 存储与网络基础但经常被搞混4.1 存储池和存储卷的关系virsh的存储管理模型里存储池pool是底层存储资源的抽象存储卷vol则是池里的具体单元。举例来说一个目录池指向/var/lib/libvirt/images这个目录下每一个qcow2文件就是一个卷。虚拟机所用的磁盘本质上就是存储卷。查看宿主机有哪些存储池virsh pool-list --all默认安装后一般会有一个名为default的目录池对应路径通常是/var/lib/libvirt/images。你也可以手动建立自己的目录池virsh pool-define-as data dir --target /data/libvirt virsh pool-start data virsh pool-autostart data创建存储卷的命令是vol-create-as比如创建一台20G的qcow2镜像virsh vol-create-as data demo.qcow2 20G --format qcow2这里有个容易忽略的点--format qcow2如果不指定默认可能是raw格式。raw格式的优势是性能好但不支持快照也无法精简分配。我几乎全部用qcow2因为它支持快照、支持动态增长功能更符合日常虚拟化管理需求。对于要求极致I/O的场景再考虑raw。经验创建大容量qcow2卷时可以加上--prealloc-metadata参数预分配元数据避免首次启动时因为元数据初始化导致一瞬间的卡顿。这个参数不会占用全部20G空间只是提前写好qcow2的结构信息成本很低。4.2 默认NAT网络和桥接到物理网络选哪个默认情况下libvirt会建立一个名为default的虚拟网络它通过宿主机上的virbr0网桥提供NAT转发虚拟机可以访问外网外网却无法主动访问虚拟机。对测试环境来说很好用省事、安全还不占IP资源。但如果你想让虚拟机拥有局域网里的独立IP让其他机器直接访问就得用桥接模式。桥接模式下虚拟机网卡直接接到宿主机所在的物理网络相当于在交换机上多接了一台机器。两种方式的取舍我通常这么看对比项NAT模式default桥接模式外网访问虚拟机可出去外部进不来双向可达IP分配内部DHCP无需外部关心需要跟物理网络协调IP适用场景测试、隔离环境生产、需要直连的服务配置复杂度低中配置桥接网络时先用virsh net-list确认现有网络再定义bridge类型网络。也可以直接用virsh net-define加载桥接XML或者用nmcli在宿主机上配置好br0后在虚拟机XML里让网卡指向这个桥接器interface typebridge source bridgebr0/ model typevirtio/ /interface我踩过的一个坑是桥接网络配好后虚拟机还是ping不通外网。排查了半天发现是宿主机防火墙没放行虚拟机发出的转发流量和虚拟机自身的路由无关。记住桥接模式下虚拟机的流量是要经过宿主机iptables的不是天生无障碍。4.3 设备热插拔不是你想的那么万能virsh确实支持运行中给虚拟机添加或移除设备比如网卡、磁盘、USB控制器。命令是这样的virsh attach-disk demo /data/libvirt/new.qcow2 vdb --live virsh detach-disk demo vdb但热插拔有一个重要限制libvirt说支持热插拔也不代表虚拟机内所有驱动都支持。vda这类virtio磁盘在Linux下支持热插拔但如果你用的是IDE控制器大概率要重启才能识别。最稳妥的做法是给虚拟机加磁盘时同时指定--persistent让它在本次运行和下次启动都保持有效virsh attach-disk demo /data/libvirt/new.qcow2 vdb --live --persistent如果只加--live不加--persistent重启后这个磁盘就消失了这在批量交付场景里会被坑得很惨。我自己就遇到过给一个虚拟机加了数据盘过了几天宿主机重启业务上找不到那块盘数据还是没挂上等于白做。后来我强制要求所有热插拔操作必须加--persistent除非明确是临时挂载验证。5. 快照、克隆和迁移批量交付的进阶操作5.1 快照不是随时乱打的快照是虚拟机管理中非常实用的功能它让你能快速回到某个历史状态。创建快照的命令很简单virsh snapshot-create-as demo snap1 first snapshot before update查看已有快照virsh snapshot-list demo恢复快照virsh snapshot-revert demo snap1快照听起来很简单用起来有几个关键点。首先快照只对qcow2磁盘生效raw格式不支持内部快照强行创建会直接报错。其次创建快照时虚拟机最好是已关闭状态或者至少保证文件系统是干净的。如果运行中创建快照虽然框架支持但恢复时可能遇到文件系统不一致的问题尤其是有数据库写入的场景。恢复快照时还要注意如果快照是在虚拟机启动时拍的revert的时候可以加上--running参数让恢复后自动启动。我实际管理中很少依赖快照做备份它更适合做变更前的安全网。比如升级内核、改网络配置前拍一个快照出问题秒回退。真正的数据备份还得靠外部存储落盘快照如果和源文件放在同一块物理盘上盘崩了快照也一起没。5.2 克隆虚拟机时最容易忽略的事批量交付环境时克隆虚拟机是最常用的手段。virsh本身没有直接叫clone的命令需要借助virt-clone这个工具virt-clone --original demo --name demo-clone --auto-clone这条命令会自动复制原虚拟机的磁盘镜像并生成一份新的XML配置。别高兴太早克隆出来的虚拟机虽然磁盘内容完全一样但里面有几个关键配置是必须改的hostname、机器ID、网卡的MAC地址virt-clone会默认生成新的MAC但hostname不会自动改、还有可能重复的IP地址。如果你把原机里的网卡IP写成了静态地址克隆后就得手工进系统改。另外克隆前一定要确认原虚拟机的磁盘处于一致性状态最保险的做法是关机后再克隆。有业务的虚拟机不建议在线克隆除非你很清楚文件系统层面的风险。5.3 迁移从一个宿主机到另一个宿主机virsh也支持虚拟机在线迁移把虚拟机从一台宿主机挪到另一台宿主机业务不中断。命令格式大致是virsh migrate --live demo qemussh://host2/system执行迁移需要满足几个前提两台宿主机之间网络互通目标宿主机有足够的资源虚拟机的磁盘要么放在两台宿主都能访问的共享存储上要么在迁移时指定--copy-storage-all把镜像一并拷贝过去。另外宿主机之间的SSH信任关系要提前配好qemussh需要免密登录才能跑通全程。在线迁移属于相对进阶的运维操作真正生产环境里我建议先在测试宿主机上跑通一次。我自己第一次迁移就栽过跟头由于两边宿主机CPU型号不一致虚拟机迁移后启动直接报CPU不兼容的错误。后来用virsh migrate --live demo qemussh://host2/system --unsafe配合CPU型号统一调整才算顺利。基础使用层面先掌握迁移思路和前提条件就够了真要跑生产迁移还需要额外考虑存储、网络、安全组各种因素。6. 常见问题与排查技巧实录6.1 虚拟机启动失败先分清三类错误用得越多遇到start失败的频率就越高。我帮你把最常见的报错归好类排查会快很多。第一类是XML配置错误。启动时提示类似“error: Failed to start domain”后面跟着XML解析错误那多半是配置里的设备地址冲突、控制器类型不对或者引用了不存在的存储路径。这时用virsh dumpxml demo导出现有配置对照检查或者直接用virt-xml-validate做校验。第二类是磁盘文件权限问题。libvirtd服务通常以root身份运行但如果你把镜像文件放在某个普通用户目录下且目录没有正确的读权限qemu进程就没法读取镜像。表现为启动失败但日志里能看到“permission denied”。解决办法要么调整目录权限要么把镜像挪到默认目录池里。第三类是物理资源不足。宿主机的CPU、内存不够时start命令也能失败报错里通常有“cannot allocate memory”字样。检查方法很简单virsh freecell这条命令能显示当前NUMA节点上剩余的内存资源如果可用值很小先考虑停掉一些闲置虚拟机或者增加宿主内存。6.2 控制台卡住、ping不通排查顺序是什么控制台没反应不要觉得是virsh的问题。先确认虚拟机有没有在运行然后确认XML配置里有没有串口设备。用这条命令查看设备列表virsh domiflist demo virsh dumpxml demo | grep -A 5 console如果串口部分没有配置ttyS0相关的参数控制台是连不进的。解决方案是修改grub配置加consolettyS0,115200重启虚拟机后控制台就正常了。ping不通虚拟机时按这个顺序排查先virsh domiflist确认网卡存在再检查虚拟机内部网络是否配置了正确的IP如果虚拟机网卡是NAT模式还要确认宿主机上iptables的MASQUERADE规则没被其他规则覆盖。多数情况下问题出在虚拟机内部网卡没启用而不是宿主机网络坏了。6.3 libvirtd服务异常时的快速定位如果发现virsh命令执行时一直提示“unable to connect to libvirtd”先确认libvirtd进程状态。有时候并不是服务挂了而是socket激活模式在等待第一次连接。但如果服务真的起不来日志是最好的线索journalctl -u libvirtd -n 100常见的一个坑是系统里残留了旧版本的libvirt配置升级后服务启动失败日志里提示加载模块冲突。解决办法通常是把相关配置文件备份后重新生成。还有种情况是误删了/var/lib/libvirt下的状态目录导致libvirt无法恢复之前的虚拟机状态。遇到这种问题先检查/var/lib/libvirt/qemu目录是否存在且权限正确再重启libvirtd服务。排查时我有个习惯遇到不认识的报错先跑一次virsh -c qemu:///system version确认版本号很多错误跟版本特性相关旧版本的libvirt不支持新XML里的某些字段报错信息又晦涩这时候升级libvirt往往比改配置更有效。我现在重装完一台新的宿主机第一件事一定是用virsh把常用虚拟机模板重新define一遍而不是急着打开图形界面。原因很简单命令行操作天然适合沉淀成脚本和文档把命令保存成文件换一台机器也能照跑。最后分享一个小习惯任何一台要上生产环境的虚拟机我都会在首次启动前virsh dumpxml demo demo-init.xml备份一份初始配置之后每次更新硬件或磁盘再导出一份新的XML作为标记。这样即使环境出了大问题也能照着重现配置。virsh就是这样一个工具它不会替你做决策但会把所有操作留成清晰可追溯的接口越用越顺手。