KVM虚拟机的快照用来保存虚拟机在某个时间点的内存、磁盘或者设备状态,如果将来有需要可以把虚拟机的状态回滚到这个时间点。
根据被做快照的对象不同,快照可以分为磁盘快照和内存快照,两者加起来构成了一个系统还原点,记录虚拟机在某个时间点的全部状态;根据做快照时虚拟机是否在运行,快照又可以分为在线快照和离线快照。
磁盘快照根据存储方式的不同,又分为内部快照和外部快照:内部快照只支持qcow2格式的虚拟机镜像,把快照及后续变动都保存在原来的qcow2文件内;外部快照在创建时,快照被保存在单独一个文件中,创建快照时间点之后的数据被记录到一个新的qcow2文件中,原镜像文件成为新的qcow2文件的backing file(只读),在创建多个快照后,这些文件将形成一个链——backing chain。外部快照同时支持raw和qcow2格式的虚拟机镜像。
下文将分别具体介绍不同类型的KVM虚拟机快照。
操作环境:
l 操作系统:
[[email protected] ~]# cat /etc/redhat-release CentOS Linux release 7.4.1708 (Core) |
l Libvirt版本:
[[email protected] ~]# libvirtd --version libvirtd (libvirt) 3.2.0 |
l qemu版本:
[[email protected] ~]# rpm -qa|grep qemu-kvm qemu-kvm-common-ev-2.3.0-29.1.el7.x86_64 qemu-kvm-ev-2.3.0-29.1.el7.x86_64 |
centos7.4的默认yum源中的qemu-kvm不支持在线创建外部快照,需要安装Redhat的qemu-kvm-ev,安装方法:
1. 配置yum源
[[email protected] ~]# cat /etc/yum.repos.d/qemu-kvm-rhev.repo [qemu-kvm-rhev] name=oVirt rebuilds of qemu-kvm-rhev baseurl=http://resources.ovirt.org/pub/ovirt-3.5/rpm/el7Server/ mirrorlist=http://resources.ovirt.org/pub/yum-repo/mirrorlist-ovirt-3.5-el7Server enabled=1 skip_if_unavailable=1 gpgcheck=0 |
2. 安装
[[email protected] ~]# yum install qemu-kvm-rhev -y
测试机上有一台虚拟机
[[email protected] ~]# virsh list Id Name State ---------------------------------------------------- 10 vm running |
虚拟机的磁盘文件为系统盘/data/vm.img,数据盘/data/data.img。
内存快照
1. 创建快照
命令:virsh save vm vm.snapshot1
[[email protected] ~]# virsh save vm vm.snapshot1 Domain vm saved to vm.snapshot1 |
创建完后虚拟机会关机:
[[email protected] ~]# virsh list --all Id Name State ---------------------------------------------------- - vm shut off |
2. 回滚快照
命令:virsh restore vm.snapshot1
[[email protected] ~]# virsh restore vm.snapshot1 Domain restored from vm.snapshot1 [[email protected] ~]# virsh list Id Name State ---------------------------------------------------- 11 vm running |
注:
1. 只能对关机状态的虚拟机进行回滚快照;
2. 内存快照做完后,如果虚拟机磁盘文件发生修改,可能会导致corruption。
磁盘内部快照
磁盘内部快照可以在虚拟机开机状态创建,但是创建过程中虚拟机处于paused状态,
1. 创建快照
命令:virsh snapshot-create-as --domain vm --name vm1
这条命令执行后,虚拟机会变成paused状态
[[email protected] ~]# virsh list Id Name State ---------------------------------------------------- 13 vm paused |
等快照创建完成,会重新变回running。
2. 查看快照
命令:virsh snapshot-list –domain vm
[[email protected] ~]# virsh snapshot-list --domain vm Name Creation Time State ------------------------------------------------------------ vm1 2018-03-06 10:37:57 +0800 running |
快照回滚:virsh snapshot-revert --domain vm --snapshotname vm1
快照删除:virsh snapshot-delete --domain vm --snapshotname vm1
磁盘内部快照有2个缺点:
1. 只支持qcow2格式的镜像文件;
2. 创建快照虚拟机会paused,有停机时间,对于不能停机的线上业务来说是无法接受的。
磁盘外部快照
原理
假设虚拟机磁盘镜像文件为base,创建一个外部快照snapshot1,这时候的镜像之间的关系backing chain如下:
base<-snapshot1*
“*”表示目前active状态的镜像,base变为只读,snapshot1以base为backing file,虚拟机所有写入都发生在snapshot1,如果再创建一个外部快照snapshot2,backing chain会变成:
base<-snapshot1<-snapshot2*
snapshot2又以snapshot1为backing file,现在base和snapshot1都变成了只读。继续创建快照会加长这个backing chain:
base<-snapshot1<-snapshot2<-snapshot3<-snapshot4*
如果要回滚某个快照,就要把虚拟机使用的镜像指向该快照文件的backing file。例如,回滚到snapshot2,就要把虚拟机的镜像改为snapshot1;回滚到snapshot1,则要把虚拟机的镜像改为base。回滚到snapshot1会导致snapshot1之后的所有快照失效,因为他们在backing chain上游的backing file发生了变化(backing file只能是只读,如果数据发生变化,下游镜像也会失效)。
随着快照数量变多,backing chain也会越来越长,变得难以维护。如果有些快照已经没用了可以进行删除。缩短这条链通常有两种思路:
1. blockcommit,从top文件合并数据到base(下游镜像向backing file合并,称为“commit”);
2. blockpull,从base文件合并数据到top(从backing file向下游镜像合并,称为“pull”)。截止目前只能将backing file合并至当前的active的镜像中,也就是说还不支持指定top的合并。
在上面的backing chain中,如果我们要删除snapshot2,方法如下:
1. blockcommit:把snapshot2的数据合并到snapshot1,合并完后backing chain变成了
base<-snapshot1<-snapshot2(内容为snapshot2+snapshot3)<-snapshot4*
2. blockpull:把snapshot2的数据合并到snapshot3,合并完后backing chain变成了
base<-snapshot1<-snapshot3(内容为snapshot2+snapshot3)<-snapshot4*
具体操作
1. 创建外部快照
命令:virsh snapshot-create-as --domain vm --name snapshot1 --disk-only --atomic --no-metadata
--disk-only 有这个参数,snapshot-create-as命令就会创建磁盘外部快照;
--atomic 如果虚拟机有多个磁盘,则把为虚拟机所有磁盘创建快照的操作当做一个原子操作,要么全部成功,要么全部失败;
--no-metadata 不让libvirt记录快照的元数据。这个参数不是必须的,但是强烈建议使用,目前libvirt对外部快照支持不完整,只能创建,不能删除和回滚,如果要删除一个有外部快照的虚拟机,会出现以下报错:
[[email protected] ~]# virsh undefine vm error: Failed to undefine domain test error: Requested operation is not valid: cannot delete inactive domain with 1 snapshots |
加上这个参数后,libvirt不再管理外部快照,删除和回滚都不会受影响了。
快照创建成功后,在虚拟机磁盘文件目录下会多出2个新文件vm.snapshot1和data.snapshot1,分别是系统盘和数据盘的快照文件,查看镜像信息可以看出,它们分别以原镜像为backing file,与之前原理中分析的一致:
[[email protected] data]# qemu-img info vm.snapshot1 image: vm.snapshot1 file format: qcow2 virtual size: 20G (21474836480 bytes) disk size: 3.4M cluster_size: 65536 backing file: /data/vm.img backing file format: qcow2 Format specific information: compat: 1.1 lazy refcounts: false refcount bits: 16 corrupt: false [[email protected] data]# qemu-img info data.snapshot1 image: data.snapshot1 file format: qcow2 virtual size: 1.0G (1073741824 bytes) disk size: 196K cluster_size: 65536 backing file: /data/data.img backing file format: qcow2 Format specific information: compat: 1.1 lazy refcounts: false refcount bits: 16 corrupt: false |
创建完后,虚拟机xml文件中使用的磁盘文件会libvirt自动被改成这两个新文件,这两个新文件处于active状态,原镜像变为只读。
<disk type='file' device='disk'> <driver name='qemu' type='qcow2' cache='none' io='native'/> <source file='/data/vm.snapshot1'/> <target dev='vda' bus='virtio'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> </disk> <disk type='file' device='disk'> <driver name='qemu' type='qcow2' cache='none' io='native'/> <source file='/data/data.snapshot1'/> <target dev='vdb' bus='virtio'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/> </disk> |
2. 回滚快照
Libvirt目前不支持回滚外部快照,只能纯手工操作。为了证明在回滚快照后虚拟机确实回到了快照记录的状态,我们在虚拟机中在/root下新建一个空文件test。然后关闭虚拟机并把虚拟机的磁盘改回vm.img和data.img,开机后会发现/root/test不见了,可以证明虚拟机文件系统回到了创建快照的时间点。
由上面的操作我们可以得出结论:回滚到某个快照,就是把虚拟机当前磁盘文件改为这个快照文件的backing file;快照名和快照文件名并不对应,例如创建snapshot1后产生的文件vm.snapshot1中记录的并不是快照snapshot1的内容,它的backing file才是。在下面介绍删除快照时,牢记这点尤其重要。
3. 删除快照
在原理中已经介绍过,删除快照有blockcommit和blockpull两种思路,由于blockpull不支持指定top的合并,下面将只介绍blockcommit方式。我们先为虚拟机vm多创建几个快照,现在快照链为(以下操作都以系统盘为例,数据盘同理):
base<-snapshot1<-snapshot2<-snapshot3<-snapshot4*
qemu-img命令也可以查看链关系:
[[email protected] data]# qemu-img info --backing-chain vm.snapshot4 image: vm.snapshot4 file format: qcow2 virtual size: 20G (21474836480 bytes) disk size: 452K cluster_size: 65536 backing file: /data/vm.snapshot3 backing file format: qcow2 Format specific information: compat: 1.1 lazy refcounts: false refcount bits: 16 corrupt: false image: /data/vm.snapshot3 file format: qcow2 virtual size: 20G (21474836480 bytes) disk size: 196K cluster_size: 65536 backing file: /data/vm.snapshot2 backing file format: qcow2 Format specific information: compat: 1.1 lazy refcounts: false refcount bits: 16 corrupt: false image: /data/vm.snapshot2 file format: qcow2 virtual size: 20G (21474836480 bytes) disk size: 196K cluster_size: 65536 backing file: /data/vm.img backing file format: qcow2 Format specific information: compat: 1.1 lazy refcounts: false refcount bits: 16 corrupt: false image: /data/vm.img file format: qcow2 virtual size: 20G (21474836480 bytes) disk size: 1.5G cluster_size: 65536 Format specific information: compat: 0.10 refcount bits: 16 |
现在我们要删除snapshot2,根据回滚快照时得出的结论,要回滚到snapshot2就是把虚拟机磁盘指向vm.snapshot1,所以删除snapshot2就要在不影响backing chain中其他文件的前提下,把vm.snapshot2的内容合并到vm.snapshot1,vm.snapshot1的内容发生了改变,也就不能回滚到snapshot2了,达到了删除快照的目的。操作命令如下:
virsh blockcommit --domain vm vda --base /data/vm.snapshot1 --top /data/vm.snapshot2 --wait –verbose
virsh blockcommit --domain vm vdb --base /data/data.snapshot1 --top /data/data.snapshot2 --wait –verbose
合并完后,使用qemu-img命令再次查看文件信息可以发现,vm.snapshot2已经不在backing chain中了:
[[email protected] data]# qemu-img info --backing-chain vm.snapshot4 image: vm.snapshot4 file format: qcow2 virtual size: 20G (21474836480 bytes) disk size: 1.1M cluster_size: 65536 backing file: /data/vm.snapshot3 backing file format: qcow2 Format specific information: compat: 1.1 lazy refcounts: false refcount bits: 16 corrupt: false image: /data/vm.snapshot3 file format: qcow2 virtual size: 20G (21474836480 bytes) disk size: 388K cluster_size: 65536 backing file: /data/vm.snapshot1 backing file format: qcow2 Format specific information: compat: 1.1 lazy refcounts: false refcount bits: 16 corrupt: false image: /data/vm.snapshot1 file format: qcow2 virtual size: 20G (21474836480 bytes) disk size: 1.5M cluster_size: 65536 backing file: /data/vm.img backing file format: qcow2 Format specific information: compat: 1.1 lazy refcounts: false refcount bits: 16 corrupt: false image: /data/vm.img file format: qcow2 virtual size: 20G (21474836480 bytes) disk size: 1.5G cluster_size: 65536 Format specific information: compat: 0.10 refcount bits: 16 |
总结
三种快照中,只有磁盘外部快照可以不停机创建,所以这种快照最符合我们平时的需求,后续研究也重点关注外部快照。不幸的是libvirt对外部快照的支持太弱,大部分操作需要我们人脑思考、手工操作。接下来研究的重点有以下几点:
1. 测试外部快照创建时是否真正零停机时间;
2. 虚拟机运行时进行快照文件合并对性能有何影响;
3. 利用Python脚本封装外部快照的操作。
原文地址:http://blog.51cto.com/3646344/2096347