☰
QEMU/KVM虚拟机快照原理与实操:从virsh到qemu-img
2026/10/2 14:11:56 网站建设 项目流程

我花了不少时间实测QEMU/KVM虚拟机快照,今天把操作链路和取舍逻辑一次性说清楚。很多人对虚拟机快照的理解还停留在"拍个照、能还原"的层面,真到生产环境上手才发现:内部快照、外部快照、磁盘快照、内存快照根本不是一回事,选错了存储格式直接拍不出快照,回滚的时候又发现虚拟机状态对不上。这篇博文就围绕"如何为QEMU/KVM客户机创建快照"展开,覆盖从原理、环境检查、virsh和qemu-img实操到回滚、删除、踩坑的全流程,适合刚接触虚拟化的运维新手,也适合已经在用libvirt但没系统梳理过快照机制的同行。

1. 快照的本质:先搞清楚你拍下的到底是什么

快照这个词在QEMU/KVM语境下其实承载了三层不同的东西,很多人混为一谈,后面操作就踩坑。

磁盘快照是最核心的部分。它把虚拟机磁盘在某一时刻的内容保存下来。QEMU的qcow2格式天然支持内部快照,它的原理是写时复制(Copy-On-Write,COW):快照创建之后,新的写入会落到新的数据块,原始数据块被标记为快照的一部分。这样一来,快照点的磁盘状态就被冻结住了,而虚拟机继续运行不受影响。类比一下就是给一份正在编辑的文档做了一个"版本备份",之后你随便改,随时能退回保存点。

虚拟机状态快照则是把CPU寄存器、内存内容、设备状态全部保存到一个文件里。这相当于把整台运行中的机器"暂停并冷藏"起来。virsh命令里的--memspec参数就是干这个的。如果你拍快照时不带内存状态,只保存磁盘内容,那么回滚的时候虚拟机其实回到的是关机或上次正常停止的状态,而不是快照那一刻的运行状态。这个区别在实际操作中非常容易造成误会。

虚拟硬件配置快照则记录域XML配置,包括内存大小、CPU拓扑、设备列表等。它保证的是你回滚后,虚拟机的硬件配置也回到当时的模样。

内部快照和外部快照是另一个关键维度。内部快照存储在qcow2镜像文件内部,不需要额外生成文件,管理方便,但缺点是:镜像文件会持续膨胀,而且一旦镜像文件损坏,所有快照一起报销。外部快照则把快照状态写到另一个qcow2文件里,原镜像变成后端镜像(backing file),新文件作为前端覆盖层(overlay)。外部快照更灵活,支持在线热备、增量备份,是生产环境更推荐的方向。

存储格式决定了你能用哪一种快照。qcow2支持内部快照和外部快照,raw格式不支持内部快照,LVM逻辑卷快照是存储层面的方案,跟QEMU本身无关。很多人上来就拍快照,结果发现磁盘用的是raw,virsh直接报错,就是这个原因。

2. 环境检查:动手之前先确认四件事

拍快照的操作本身不复杂,但前置条件漏掉一个,后面就是连环坑。我建议按以下顺序检查环境。

第一,确认libvirt和QEMU版本。virsh --version和qemu-img --version各自看版本号,快照功能的完整性和稳定性跟版本直接相关。老版本QEMU的外部快照、块克隆功能都有不少bug,如果版本过低,建议先升级再玩快照。我见过CentOS 7自带的QEMU 1.5.x时代版本,处理外部快照的blockcommit功能问题很多,后来升级到QEMU 4.x才稳定。

第二,确认虚拟机磁盘的格式和总线。执行以下命令查看磁盘信息:

virsh domblklist vm-name

输出会显示目标设备和对应的磁盘路径,比如vda对应的可能是/data/vms/vm-name.qcow2或者/dev/vg0/vm-name。接着用qemu-img检查格式:

qemu-img info /data/vms/vm-name.qcow2

如果file format显示qcow2,内部快照没问题;如果显示raw,就要改走外部快照或LVM快照路线。

第三,确认存储后端的空间。内部快照和外部快照都需要额外空间。内部快照导致qcow2文件膨胀,膨胀幅度取决于快照后新写入的数据量;外部快照则是新生成一个覆盖层文件。如果你的宿主机存储分区分分钟就要满了,先扩容再拍快照,否则快照写一半直接失败。

第四,确认虚拟机配置里没有使用直通设备或特殊设备。PCI直通、SR-IOV、USB重定向、大页内存这类配置,对快照的兼容性影响很大。带内存状态的快照碰到PCI直通设备,经常会出现恢复后设备状态不一致的问题。我建议:有直通设备的虚拟机,只做磁盘快照,不要试图连内存状态一起保存。

3. 用virsh创建内部快照:最常用的操作路径

libvirt的virsh命令是管理快照最顺手的方式。内部快照分两种情况:只拍磁盘,或者连内存一起拍。

3.1 只快照磁盘内容

这是最常用的一种拍法,命令如下:

virsh snapshot-create-as vm-name snap-before-upgrade --disk-only --atomic

参数说明:

  • vm-name是虚拟机域名,snap-before-upgrade是快照名,自己起一个好认的名字
  • --disk-only表示只对磁盘做快照,跳过内存状态
  • --atomic保证快照操作要么全部完成,要么不产生任何遗留状态

这个命令执行很快,因为它本质上是让QEMU在qcow2文件内部记录一个状态点,然后继续COW。虚拟机无需关机,也无需暂停,业务无感知。

3.2 连内存状态一起快照

如果需要保存"当前运行状态",让回滚之后虚拟机回到你拍快照那一刻的现场,需要加内存参数:

virsh snapshot-create-as vm-name snap-with-memory \ --disk-only \ --memspec file=/data/vms/vm-name-memory.snap \ --atomic

注意这里有一个容易踩坑的点:--disk-only和--memspec配合使用,libvirt会把内存状态单独存到external file,磁盘快照存到qcow2内部。回滚时可以恢复到内存状态,但前提是你得把qcow2里的磁盘快照和memory文件都对齐到同一个时间点。

实际上我更推荐的做法是分两步,先把虚拟机暂停再做带内存快照:

virsh suspend vm-name virsh snapshot-create-as vm-name snap-consistent --atomic virsh resume vm-name

暂停的时间窗口很短,但能保证内存状态和磁盘状态在同一个时间点,一致性更好。数据库类应用尤其推荐这种方式,否则内存里的日志和磁盘上的数据文件不是完全同步的,回滚后可能出现文件系统不一致。

3.3 查看、回滚与删除

查看所有快照:

virsh snapshot-list vm-name

查看具体快照的详情:

virsh snapshot-info vm-name snap-before-upgrade virsh snapshot-dumpxml vm-name snap-before-upgrade

回滚到指定快照点:

virsh snapshot-revert vm-name snap-before-upgrade

这里有个关键参数要记住:--running让回滚后虚拟机自动进入运行状态,--paused则停留在暂停状态。如果你拍的是带内存的快照,回滚时会自动恢复内存状态;如果是纯磁盘快照,回滚后虚拟机处于关机状态,需要手动启动。

删除快照:

virsh snapshot-delete vm-name snap-before-upgrade

删除快照时,qcow2内部的数据块会被合并或释放,这个操作可能比较耗时,而且会占用额外的CPU和IO。一个大快照删除时,整个磁盘文件可能出现短暂的性能下降。生产环境不要赶着业务高峰去删快照。

3.4 内部快照的最大局限性

内部快照虽然方便,但有几个绕不过去的毛病。第一,qcow2文件会越来越大。假设你拍了5个快照,每个快照之后都写入了大量新数据,文件里同时存着5份历史数据和当前数据,磁盘空间开销非常可观。第二,快照链会变得越来越复杂,QEMU处理多层快照时的性能会下降。第三,所有快照和当前数据都在同一个文件里,一旦文件损坏,全部一起丢。

所以内部快照我一般只在测试环境用,或者说在开发机上做快速验证用。生产环境大概率还是走外部快照或者LVM快照。

4. qemu-img命令行快照:不经过libvirt的另一条路径

在某些瘦客户端、无libvirt的纯QEMU环境,或者需要在宿主机上直接操作镜像的场景下,qemu-img是兜底工具。它的快照命令也很清晰。

4.1 创建快照

qemu-img snapshot -c snap-name /data/vms/vm-name.qcow2

注意:这个命令执行时必须确保虚拟机处于关闭状态,或者至少磁盘没有活跃写入。如果在虚拟机运行中直接执行qemu-img snapshot,得到的快照极大概率是不一致的。qemu-img命令并不知道虚拟机内部的页面缓存和文件系统状态,拍出来的快照从QEMU的角度看是"某个时刻的块设备状态",但从虚拟机内部看可能是文件系统正在写入中间状态,回滚后ext4或者xfs很可能会报错。

如果确实要在虚拟机运行中用qemu-img方式拍快照,可以先执行virsh snapshot-create-as配合--disk-only,或者先virsh suspend再操作。

4.2 查看快照列表

qemu-img snapshot -l /data/vms/vm-name.qcow2

输出会列出快照ID、标签、VM状态、创建时间等信息。注意这个VM状态字段其实就是虚拟机内存状态的一个记录,但qemu-img快照本身不保存内存内容,所以这个字段大多是"0"或者不准确。

4.3 回滚快照

qemu-img snapshot -a snap-name /data/vms/vm-name.qcow2

-a参数是apply,把磁盘内容恢复到快照时刻。同样要求虚拟机处于关闭状态。

4.4 删除快照

qemu-img snapshot -d snap-name /data/vms/vm-name.qcow2

删除后qcow2文件会整理数据块,文件大小不一定立刻缩小,但内部空间会被释放。

qemu-img的好处是可以在宿主机上用shell脚本批量处理多个镜像的快照,适合自动化备份场景。坏处是它完全不知道虚拟机内部发生了什么,一致性完全依赖你自己控制和把握时机。

5. 生产环境更推荐的外部快照与LVM快照

内部快照痛点多,生产环境更可靠的做法是外部快照和存储级快照。

5.1 外部快照的操作方法

外部快照的本质是由当前磁盘镜像生成一个新的覆盖文件,原镜像成为backing file。执行方式:

virsh snapshot-create-as vm-name ext-snap-outside \ --disk-only \ --atomic \ --diskspec vda,snapshot=external

执行后,vda磁盘会变成一个新的qcow2文件,这个文件在XML里成为新的磁盘源,而原来的qcow2文件自动成为backing file。这个新文件一开始很小,随着虚拟机运行逐渐变大,记录从快照点以来的所有增量数据。

外部快照的好处:原镜像完全不动,适合用来做备份基线;快照点之间互不干扰;支持热备和增量备份策略。坏处:撤销和合并的操作比内部快照复杂,需要处理blockcommit或blockpull。

5.2 外部快照的合并与撤销

如果想把外部快照合并回磁盘链,使用blockcommit:

virsh blockcommit vm-name vda \ --base /data/vms/vm-name.qcow2 \ --top ext-snap-outside \ --active \ --pivot

这个命令会把覆盖层的改动合并回base镜像,并让虚拟机继续以最终状态运行。执行成功后,外部快照文件可以安全删除。

如果要回滚到外部快照点,操作思路不同:把当前覆盖层丢弃,重新以原镜像为磁盘启动虚拟机。具体做法是编辑虚拟机XML,把disk的source替换为原镜像路径,或者用blockpull把backing file拉回来。

5.3 LVM快照:存储级别的降维打击

如果虚拟机磁盘用的是LVM逻辑卷,LVM快照是目前最皮实、性能损耗最低的方案。它不需要QEMU参与,直接在宿主机上操作:

lvcreate -s -n vm-name-snap -L 20G /dev/vg0/vm-name
  • -s表示snapshot,-L指定快照卷大小,20G是给COW数据预留的空间
  • /dev/vg0/vm-name是原逻辑卷

快照创建后,原卷继续运行,所有新写入的数据会被COW机制记录到快照卷里。要恢复时,直接把逻辑卷回滚:

lvconvert --merge /dev/vg0/vm-name-snap

这个操作要求对应的逻辑卷处于非活跃状态,所以虚拟机关机后执行比较稳妥。

LVM快照必须在创建前预留足够的快照空间。预留空间不足时,快照会变成"失效"状态,回滚操作直接失败。预留比例取决于快照点到恢复点之间的数据变化量,一般我建议至少预留原卷大小的20%到30%,数据密集型的系统直接给50%。

三种方案对比:

方案空间开销一致性保障回滚复杂度适用场景
内部快照qcow2文件膨胀一般,需配合暂停或--disk-only低测试环境、快速验证
外部快照增量覆盖文件较高,可配合内存快照中生产环境备份链、增量备份
LVM快照快照卷占用固定空间高,存储层COW低生产环境应急回滚、批量克隆

6. 回滚实操、删除顺序与真实踩坑记录

6.1 回滚的整体流程

无论用了哪种快照方式,回滚前有几件必须做的事:

  1. 通知业务方,或者选择业务低峰期操作。
  2. 在虚拟机内执行sync,把文件系统缓存刷到磁盘。
sync
  1. 如果是数据库,最好先做一次干净关闭或用数据库自身的备份工具转储。快照不是数据库备份工具,它只能保证块设备层的一致性,不能保证物理上的一致性,但是先flush数据库的redo log和buffer pool,回滚后的一致性会好很多。MySQL、PostgreSQL、Oracle都有各自的pre-freeze和post-thaw机制,能挂脚本调的话一定要先调。
  2. 关闭虚拟机(或者暂停)。virsh shutdown vm-name优先,实在关不掉的virsh destroy也不是不能用,但是destroy模拟的是断电,回滚后的文件系统靠日志恢复,有风险。
  3. 执行回滚命令,然后启动虚拟机,检查内部服务和数据状态。

6.2 快照删除顺序

快照删除顺序也有讲究。假设有链式的快照A->B->C,直接删B会导致libvirt尝试把B和C合并,这个操作不仅慢,而且如果中途断电,整个磁盘链可能会出现损坏。正确做法是:从最上层的快照开始删,一层一层往下,也就是先删C再删B最后删A。

磁盘空间压力大的时候,不要一次性连续删除多个大快照。中间间隔一段时间观察磁盘I/O和文件系统膨胀情况,等合并完成、文件大小平稳后再删下一个。

6.3 踩坑记录

第一个坑是热备份外部快照的一致性。有一次我执行外部快照后直接拿backing file去备份,结果虚拟机内部文件系统在快照之后还在写入数据,我没等待qcow2文件的写缓存完全落盘,导致备份出来的镜像在挂载验证时出现故障。经验是:备份前在虚拟机内部执行sync,并且在宿主机上确认无活跃IO再复制文件。

第二个坑是qcow2磁盘文件有快照后,执行virsh vol-resize扩充磁盘大小,新扩充的空间不会自动并入快照链,导致原快照数据区和扩展区错位,虚拟机启动后出现分区表识别错误。正确的做法是:扩充之前在虚拟机内部用growpart或者fdisk扩展分区,然后再在宿主层面resize,最后确认qcow2文件没问题再考虑快照。

第三个坑是NFS存储上的快照。如果虚拟机磁盘挂在NFS上,快照创建和删除时的文件锁行为在不同NFS版本上表现不一样。NFSv3上libvirt的快照操作偶尔会出现"can't create snapshot: No space left on device"之类的迷惑报错,实际是文件锁抢占或者属性缓存导致的。解决办法是挂载NFS时加上nfsvers=4参数,并且关闭attribute cache的副作用:

mount -t nfs4 -o nfsvers=4,hard,timeo=600,noac host:/export /data

第四个坑是和备份任务互相干扰。很多团队在宿主机上做整文件复制备份,比如rsync整个qcow2文件。如果qcow2正在被快照操作写入,rsync复制出来的文件可能是损坏的。要复制带快照的qcow2文件,要么在虚拟机关机状态下复制,要么先执行virsh snapshot-list --tree确认没有活跃操作,要么直接改用qemu-img convert -O qcow2生成一份干净的镜像再复制。

第五个坑是大页内存(HugePages)和内存快照的兼容问题。配置了大页内存的虚拟机,带内存快照的保存和恢复速度会非常慢,而且恢复时可能因为大页内存碎片导致失败。遇到这种情况,建议只做磁盘快照,必要的时候配合应用层备份。

第六个坑是回滚后虚拟机的时钟跳跃问题。如果虚拟机靠NTP校时,回滚后时间会大幅回退,NTP服务会慢慢调整回来,但期间很多业务会报错。建议在回滚操作前先临时停止业务,回滚完成后再启动。

7. 我把话放在这里的几条经验

如果看完前面这些你还是嫌长,记住下面这几条就够用了。

做内部快照,确认磁盘是qcow2,记好--disk-only和--atomic,回滚时先关机,回滚后检查文件系统和数据库一致性。

做外部快照,用virsh snapshot-create-as配合--disk-only,合并用blockcommit,撤销时把磁盘XML换回base镜像。

做LVM快照,空间预留给足,虚拟机关机再merge,千万不要在快照卷满的时候重启主机。

带内存快照,务必配合业务方确认停机窗口,数据库类应用不要轻易做内存快照回滚。

最后我个人的体会是:快照不等于备份。快照依赖的是同一份存储上的COW数据,存储坏了快照全没。真正可靠的数据安全路径必须包含独立的异地备份和定期的恢复演练。快照是手术台上的那一针麻醉剂,让你在做危险操作之前有退路,但千万别把麻醉剂当救命药。

我实测下来,最稳的组合是:日常小操作前用内部快照快速留后路,重大变更前用LVM快照或者外部快照做保护,同时定期备份qcow2文件到独立存储,并且每月至少做一次从备份恢复虚拟机的演练。这套逻辑走下来,服务器被我折腾出问题的次数大幅减少,至少不用再为了一次升级熬夜加班恢复数据。

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

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

立即咨询