1. 为什么说KVM虚拟机管理里,快照是比备份更常用的救命牌
如果你管理过着KVM虚拟化的宿主机,肯定经历过这种时刻:马上要给一台生产虚拟机升级内核,或者要跑一个改配置的脚本,风险说不准,但环境又不敢直接动。这种时候我最先做的事永远只有一件——先打快照。
很多刚开始接触KVM的朋友容易把快照和备份混在一起。备份是把虚拟机的磁盘文件复制到另一个位置,出问题之后要花时间恢复,本质是"异地重建"。快照不同,快照记录的是虚拟机在某一时刻的完整磁盘状态(加上可选的內存状态),出问题之后可以瞬间回到那个时间点,本质是"原地时光倒流"。在一台虚机上做高风险变更,快照是成本最低、速度最快的后悔药。
virsh作为KVM虚拟化最核心的管理命令行工具,快照管理的全套能力都包含在里面。用virsh打快照、看快照、回滚快照、删快照,这些操作不需要安装任何额外的插件,底层由libvirt配合qcow2磁盘格式完成。也就是说,只要你的虚拟机磁盘是qcow2格式,就天然支持这套快照体系。
这篇文章适合正在维护KVM环境的运维人员,也适合刚开始折腾KVM虚拟化、想把快照机制彻底搞明白的人。我会把virsh快照的所有核心命令拆开讲清楚,再用一个完整的案例演示"升级前打快照→升级出问题→回滚恢复"的整个流程,最后把我在生产环境里踩过的坑统一列出来。认真看完,你在KVM快照这件事上基本不会再走弯路。
2. 动手打快照之前,先确认这三件事,否则命令会直接报错
我见过太多人上来就执行virsh snapshot-create-as,然后被一串英文报错劝退。快照能不能打成功,其实在动手之前就已经决定了,决定因素就是下面三件事。
2.1 磁盘格式必须是qcow2,raw格式做不了内部快照
virsh快照命令默认创建的是内部快照,也就是把虚拟机状态写入qcow2镜像文件自身带的一个快照区域。raw格式是裸设备式的镜像,它没有一个结构化的区域可以存放快照数据,所以libvirt可以直接拒绝你。
查看虚拟机磁盘格式用这条命令:
virsh domblklist centos7 qemu-img info /kvm/images/centos7.qcow2如果qemu-img info输出的file format不是qcow2而是raw,你还需要做一个转换。这里要注意,转换必须在虚拟机关机状态下执行,否则磁盘数据不一致:
# 在虚拟机关机后执行 qemu-img convert -f raw -O qcow2 /kvm/images/centos7.raw /kvm/images/centos7.qcow2等转换完,还涉及修改虚拟机XML里的磁盘路径以及文件权限、属主问题。实操里我会建议用virsh edit centos7修改driver type和source file两个地方,然后重启虚拟机验证系统能正常起来,再把原来的raw文件改名留作紧急回退,观察几天确实没问题再删。
2.2 虚拟机运行状态决定快照类型:要磁盘,要内存,还是都要
这是很多人最容易犯迷糊的地方。打个比方,一台虚拟机的状态可以拆成两层:磁盘里存的是静态数据,内存里存的是运行时状态。virsh创建快照时,你可以只保存磁盘,也可以把内存一起保存。
- 虚拟机关机状态下:不存在内存状态,任何快照都是磁盘快照,最简单也最安全。
- 虚拟机运行状态下:默认的内部快照会把内存状态一起存下来,这样以后回滚的时候,连当时打开着什么程序、登录着什么会话都能原样恢复。代价是快照文件会大不少,因为内存镜像也不小。
- 只要磁盘不要内存:用
--disk-only参数。这个参数绕过了内存,只把磁盘状态保存成外部快照(后面会细说),适合那些不需要恢复内存状态的场景。
我的个人习惯是:对运行中的虚拟机做重大变更前,优先打包含内存的内部快照,因为回滚后虚拟机还是"活着"的状态;如果只是临时想留一个安全网,不在乎内存状态,才用--disk-only。
2.3 快照会占宿主机磁盘空间,别等满了才后悔
很多初学者的认知是"快照和虚拟机一样大,打一份就多占一份空间"。真实情况是qcow2快照的诞生机制基于"写时复制":虚拟机里改动过的数据块才会被单独记录,没改动过的块继续引用原文件,所以快照刚打出来的那一下,额外占用很小。
但这里有个隐藏的危险:随着虚拟机持续写入,快照记录的数据会越来越多,最终确实可能膨胀到接近整个虚拟磁盘的尺寸。特别是给一台跑着数据库的虚拟机打快照,数据库文件天天在写,快照的占用空间增长速度会超出你的直觉。
所以我会在快照前先看一眼宿主机磁盘剩余空间:
df -h /kvm/images du -sh /kvm/images/centos7.qcow2经验值是:宿主机的空闲空间最好保持在虚拟磁盘实际占用大小的1.5倍以上,再决定要不要打多个快照。快照不是无限打的,它本质上是用磁盘空间换操作底气。
3. virsh快照命令完整实战拆解:从创建到删除一个不缺
确认完上面三件事,下面的命令才是真正的重头戏。我先把核心命令按使用顺序列出来,每一步都给出参数说明。
3.1 创建快照:snapshot-create-as的每个参数都值得理解
创建快照最推荐用snapshot-create-as,因为可以给快照起名、加描述,后期管理一目了然。
# 最基础用法:给运行中的virt-machine打一个包含内存的系统快照 virsh snapshot-create-as virt-machine pre-upgrade --description "升级内核前的安全快照"这条命令执行后会出现类似下面这样的输出:
Domain snapshot pre-upgrade created注意,如果虚拟机正在运行,这条命令默认同时保存内存和磁盘;如果虚拟机关机了,则只保存磁盘。还有几个实用参数我分开说一下:
# 只做磁盘快照,不保存内存(运行中虚拟机也能近乎无感知地打完) virsh snapshot-create-as virt-machine pre-upgrade --disk-only --description "仅磁盘快照" # 让libvirt尝试通知客户机冻结文件系统,确保一致性(需要客户机装了agent) virsh snapshot-create-as virt-machine pre-upgrade --quiesce --description "带静默的快照"--quiesce这个参数很多人不知道。它背后的原理是:系统在往磁盘写数据的时候,内存里往往还有没落盘的文件系统缓存。如果快照恰好打在数据都在内存里、还没有写回磁盘的那一瞬间,回滚到快照点时,磁盘上的数据就不是一致的。libvirt支持借助qemu-guest-agent让客户机先冻结文件系统、刷干净缓存再打快照。要求客户机系统里装了guest agent服务,Windows和Linux都有对应安装包。对一致性敏感的业务场景,强烈建议加上这个参数。
参数名里的"as"代表async?不是,这里你没必要死记。核心是记住两条:一是快照名建议用能表达意图的英文名,比如pre-upgrade、before-config-change;二是快照描述信息一定要写,事后再来看一堆快照,没有描述的你会完全想不起来哪个对应哪个。
3.2 查看快照:list、info、dumpxml三兄弟配合使用
打完好几个快照之后,重要的是能随时看清"我有哪些快照""每个快照占多大""快照对应的虚拟机状态是什么"。三个命令各司其职。
# 列出这台虚拟机的所有快照 virsh snapshot-list virt-machine输出大致长这样:
Name Creation Time State ------------------------------------------------------------- pre-upgrade 2024-11-20 22:35:34 +0800 running before-config 2024-11-21 09:12:18 +0800 shutoff其中State一列代表创建快照时虚拟机的运行状态,running表示快照时刻虚拟机在运行,shutoff表示已关机。
# 查看某个快照的详细信息 virsh snapshot-info virt-machine pre-upgradesnapshot-info会输出快照名的全称、id、创建时间、状态、父快照等信息。当你面对一条长长的快照链时,通过Parent字段能判断快照之间的父子关系。
# 查看快照的XML配置 virsh snapshot-dumpxml virt-machine pre-upgrade这个命令输出的是libvirt描述快照的XML结构,里面能看到当时虚拟机用了哪些磁盘、内存快照存在哪里。日常管理快照不需要反复看XML,但排查问题时它很有用,比如你怀疑某个快照没有按预期保存内存,就可以用grep memory看XML里有没有memory snapshot相关的字段。
3.3 回滚快照:snapshot-revert命令的时机选择
这是整个快照机制的灵魂操作。回滚命令本身很简单:
virsh snapshot-revert virt-machine pre-upgrade但执行之前有几个耳朵必须竖起来的点。
第一,如果当前虚拟机的状态已经严重损坏,比如系统起不来,那回滚操作可以在虚拟机关机状态下做,回滚完再启动虚拟机,干净利落。第二,如果快照创建时包含了内存状态,而虚拟机当前正在运行,执行snapshot-revert后虚拟机会被重置到快照时刻的内存状态,GPU这类设备状态也会被一并恢复,所以那些挂着的会话全都会回到那一刻。
还有一个参数很容易被忽视:
# 强制回滚,绕过一些状态检查 virsh snapshot-revert virt-machine pre-upgrade --force官方说明里提到,如果当前XML配置和快照创建时的配置有出入,可能需要--force才能完成回滚。但注意,我不建议把--force当默认习惯,因为它会跳过部分保护逻辑,滥用容易把事情搞得更糟。
回滚之后,虚拟机的磁盘内容、运行状态会回到快照的那一刻,而快照本身并不会消失。也就是说,你回滚到pre-upgrade之后,pre-upgrade这个快照还挂在快照链里,你照样可以再次回滚到其他地方。理解这一点对清理快照很重要,后面我会专门讲。
3.4 删除快照:snapshot-delete与快照链的复杂度
删除命令同样直接:
virsh snapshot-delete virt-machine pre-upgrade如果你在一条快照链的中间节点上执行删除,会看到提示说明快照的子快照需要重新处理,有时加--children参数可以连同子快照一起删。之所以复杂,是因为每个快照都可能依赖于"父快照里的数据块",删掉父亲会牵连孩子。
实际生产环境里,我删快照的经验是:先把时间安排宽松。对一个大镜像删一个长期存在的内部快照,有时需要执行相当一段时间的合并操作。不要误以为虚拟机卡住了就直接kill掉libvirtd或者qemu进程,那样搞不好会把qcow2文件结构弄坏。
4. 完整实操案例:给CentOS虚拟机做升级前的快照回滚演练
光讲命令容易飘,我把一套完整的实操流程写出来,你在自己的环境里照着走一遍,比看十遍命令帮助文档都管用。
4.1 升级前现场核对:域名、磁盘格式、快照状态一个都不能漏
假设我这台虚拟机叫web-prod-01,接下来要给它执行yum update。升级内核和系统库属于高风险操作,之前的经验告诉我,永远先核对以下几点。
# 第一步:确认虚拟机存在并查看运行状态 virsh list --all # 第二步:查看磁盘设备对应哪个镜像文件 virsh domblklist web-prod-01假设输出为:
Target Source ------------------------------------------------ vda /kvm/images/web-prod-01.qcow2接下来用qemu-img确认格式:
qemu-img info /kvm/images/web-prod-01.qcow2看到file format: qcow2就是我想要的。顺便我看一下这个镜像文件的实际占用和宿主机磁盘余量:
du -sh /kvm/images/web-prod-01.qcow2 df -h /kvm/images如果发现宿主机空闲空间不多了,我会先清理陈旧快照或日志,再继续操作。空间不足时打快照,最坏的情况是虚拟机写入新数据后镜像膨胀,把宿主机磁盘彻底挤爆,虚拟机直接卡死。
4.2 创建安全网快照并验证
确认完现场,用一条命令把整个虚拟机的状态保存下来:
virsh snapshot-create-as web-prod-01 pre-yum-update --description "2024-11-20 yum update 前安全快照,含内存"执行成功后会显示创建结果。这时候必须做验证,不要急着升级:
virsh snapshot-list web-prod-01确认列表里出现pre-yum-update,状态是running。我还会再执行一次:
virsh snapshot-info web-prod-01 pre-yum-updatesnapshot-info里的Domain字段会显示web-prod-01,State字段显示running,这就意味着这个快照保留了内存状态,升级后即便系统启动到一半挂了,我也可以完整恢复到这一刻。
4.3 模拟升级故障与回滚恢复全过程
快照打好之后,正常进行升级:
ssh root@web-prod-01 'yum update -y'假设这次升级内核之后,重启虚拟机时系统无法正常启动,或者网络服务起不来。我先尝试重启,不行就直接回滚。回滚前我把虚拟机先关掉,让回滚更干净利落:
# 在宿主机上强制关机(必要时) virsh destroy web-prod-01 # 回滚到升级前的快照 virsh snapshot-revert web-prod-01 pre-yum-update # 启动虚拟机 virsh start web-prod-01注意一个细节:由于这个快照带内存状态,其实理论上可以直接在运行状态下回滚,虚拟机会被重置到快照时刻的状态。但我在实践中更倾向于关机回滚再启动,这样得到的是一台完全从快照状态冷启动的机器,不会保留任何回滚瞬间的运行时杂物,整个过程更容易预期。
启动完成后,登录进去验证:
ssh root@web-prod-01 'uname -a && systemctl status nginx'内核版本回到了升级前,nginx服务状态正常。这类验证清单我建议至少包含:内核版本、关键服务状态、磁盘挂载情况、网络连通性。确认无误后,安全网快照的使命就完成了。
4.4 回滚后的快照清理与保留策略
回滚成功不代表可以马上手动删这个快照。我的建议是:先保留快照观察一段时间,等确认业务连续运行一天、各种任务都正常,再删除。删除命令很简单:
virsh snapshot-delete web-prod-01 pre-yum-update删除之后用snapshot-list复查列表为空,整个流程收尾。整套操作下来,你会发现最重要的不是命令本身,而是"操作前确认格式和空间,操作后验证状态和保留时间"这个闭环习惯。闭环养成之后,任何一次高危变更都敢放心动手。
5. 快照管理里最容易翻车的六个细节,全是我踩过的坑
命令都会了,流程也通了,但真实环境里总会冒出来一些文档里不写的意外。下面几个问题我都亲自遇到过,分享出来供你少走弯路。
5.1 回滚后虚拟机失联:内存快照与磁盘快照的状态错位
有一回我给一台Windows虚拟机打了--disk-only快照,然后去改系统配置。改完之后发现配置改坏了,于是关机回滚磁盘。结果机器起来了,网络却怎么都不通。排查了半天,发现是Windows在运行期间把网卡的MAC地址信息缓存在了内存里,磁盘回滚后,我通过虚拟机管理器看到的网卡连接状态和系统里的网络配置对不上,系统把网卡识别成了新设备,IP配置失效。
这个问题的根源就是--disk-only不保存内存状态。运行中的程序、文件系统缓存、网卡状态都会和回滚后的磁盘产生某种不一致。解决方案很简单:对运行中的虚拟机做重大变更且要求"完全回到过去"时,不要贪图--disk-only的小体积,直接打包含内存的内部快照。或者,在打--disk-only之前先通过virsh dompmsuspend之类的命令尽量让系统进入干净状态,但说到底还是免不了内存与磁盘的割裂。
5.2 快照链撑爆宿主机磁盘,qcow2的分级增长是怎么发生的
这是生产环境里最恐怖的事故之一。我见过有人给虚拟机连续打了七八个快照,中间还做过大量数据操作,最后宿主机磁盘被撑到100%,虚拟机全员卡死。
解释一下机制:qcow2里每打一个内部快照,镜像文件就多出一个"记录层"。虚拟机正常写入数据时,新数据会写入当前活跃层。而快照层会保存那些"被覆盖前"的旧块。所以当你删除一个文件,文件占用的块在快照层可能还保存着;你更新一个数据库,旧版本数据也会留在快照层。业务数据量越大、改动越频繁,快照占用的空间就涨得越凶。
我给自己定了一条规矩:快照链深度不超过三层,打完即用,用完即删。同时每周做一次宿主机磁盘空间巡检,重点看du -sh /kvm/images/*.qcow2的变化趋势。如果某个镜像文件突然涨了几个GB,先排查是快照太多还是虚拟机磁盘真的写了那么多。
5.3 raw格式上没有快照可用,替代方案怎么选
前文提到raw格式做不了内部快照,但有的时候客户环境就是raw起步的,一时半会不想转qcow2。这时候有几个替代思路。
一个是LVM快照。如果虚拟机的磁盘后端是LVM卷,可以走LVM的快照机制,在宿主机上执行lvcreate -s生成一个逻辑卷快照。但LVM快照同样需要先确定卷组里有足够的空闲空间,而且快照卷的读写性能会有损耗,这个方案适合短时间内的临时保护。
另一个是把raw手动转成qcow2再打快照,这也是我更推荐的正路。具体步骤前面已经写过,只要在关机状态下转换、验证启动、临时保留原名文件备回退就行。别嫌麻烦,一次转换换来的是之后所有快照操作的自由度,这笔账非常划算。
5.4 删除快照比创建慢得多,我还遇到过"删完文件反而变大"
新手最容易在删除快照时心态崩掉。创建快照可能一两秒就完了,删除一个存在了很久、包含大量差异数据的快照,可能要跑好几分钟甚至更久。原因是删除内部快照不是简单地"丢一个条目",而是要把这个快照里独有的数据块合并到活跃镜像或者相邻快照中。这个过程要做读写合并,速度取决于数据量和磁盘I/O。
更反直觉的一个现象是:删除快照之后,qcow2文件大小可能短时间内没有缩小,甚至略有增加。不要慌,这是合并过程中产生的临时开销,等qemu-img完全处理好之后,空间会慢慢释放。判断是否还在合并,最稳的办法是看qemu-img info输出的状态,或者看这个镜像文件的时间戳是否还在变化。如果实在不放心,可以等合并结束后用qemu-img check做一次完整性检查。
5.5 快照生命周期管理:别让"安全网"变成"毒药"
快照用好了是救命牌,用不好反而制造新灾害。我在实际项目管理过程中,会把快照生命周期管理当成一项常态化工作来做。
- 打完快照后立刻用
snapshot-list确认创建成功。 - 每个快照名里带上用途和时间特征,比如
pre-yum-20241120,别嫌名字长。 - 操作完成后一周内评估快照是否还必要,不必要的及时删除。
- 每个月检查一次所有运行中虚拟机的快照链状态。
还有一个细节:很多团队会同时用virsh快照和备份软件备份qcow2文件。如果备份软件在虚拟机正在写快照的时候去读取qcow2文件,可能会导致备份出来的镜像不完整。我的经验是,重要备份操作尽量放在快照列表为空、虚拟机活动低峰的时段执行。
5.6 把快照操作封装成脚本,日常管理中省心又防呆
命令行能力强不代表每次都要人工敲命令。我自己把快照的创建、列表、删除封装成了几个简单脚本,减少了误操作的概率。核心就是一个通用的创建快照函数:
#!/bin/bash # 用法: ./kvm_snapshot.sh create <vm-name> # ./kvm_snapshot.sh list <vm-name> # ./kvm_snapshot.sh revert <vm-name> <snapshot-name> VM_NAME="$2" SNAPSHOT_NAME="snap-$(date +%Y%m%d-%H%M%S)" if [ "$1" == "create" ]; then virsh snapshot-create-as "$VM_NAME" "$SNAPSHOT_NAME" \ --description "auto snapshot before operation" --quiesce if [ $? -eq 0 ]; then echo "快照创建成功: $SNAPSHOT_NAME" virsh snapshot-list "$VM_NAME" fi elif [ "$1" == "list" ]; then virsh snapshot-list "$VM_NAME" elif [ "$1" == "revert" ]; then snapshot_name="$3" virsh snapshot-revert "$VM_NAME" "$snapshot_name" fi这个脚本很简单,但已经帮团队挡掉了好几次误操作。封装的价值不只是省键盘敲击,更重要的是把"快照前确认空间、快照后确认列表"这些检查动作固定进流程,让人为失误的概率降到最低。你完全可以按自己环境的需要继续加参数,比如增加磁盘空间预检查、回滚前自动关机等逻辑。
我个人体会是,快照管理能力真正考验的不是你会不会敲一个命令,而是你有没有形成一套"动手前确认、动手后验证、用后及时清理"的操作纪律。我从刚开始管理KVM时手忙脚乱地踩坑,到现在能够在几分钟内完成一次高危操作的安全网部署,靠的就是把上面的这些命令和习惯变成肌肉记忆。希望这篇对你也有用。