KVM虛擬機的快照用來保存虛擬機在某個時間點的內存、磁盤或者設備狀態,如果將來有需要可以把虛擬機的狀態回滾到這個時間點。
根據被做快照的對象不同,快照可以分爲磁盤快照和內存快照,兩者加起來構成了一個系統還原點,記錄虛擬機在某個時間點的全部狀態;根據做快照時虛擬機是否在運行,快照又可以分爲在線快照和離線快照。
磁盤快照根據存儲方式的不同,又分爲內部快照和外部快照:內部快照只支持qcow2格式的虛擬機鏡像,把快照及後續變動都保存在原來的qcow2文件內;外部快照在創建時,快照被保存在單獨一個文件中,創建快照時間點之後的數據被記錄到一個新的qcow2文件中,原鏡像文件成爲新的qcow2文件的backing file(只讀),在創建多個快照後,這些文件將形成一個鏈——backing chain。外部快照同時支持raw和qcow2格式的虛擬機鏡像。
下文將分別具體介紹不同類型的KVM虛擬機快照。
操作環境:
l 操作系統:
[root@localhost ~]# cat /etc/redhat-release CentOS Linux release 7.4.1708 (Core) |
l Libvirt版本:
[root@localhost ~]# libvirtd --version libvirtd (libvirt) 3.2.0 |
l qemu版本:
[root@localhost ~]# 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源
[root@localhost ~]# 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. 安裝
[root@localhost ~]# yum install qemu-kvm-rhev -y
測試機上有一臺虛擬機
[root@localhost ~]# virsh list Id Name State ---------------------------------------------------- 10 vm running |
虛擬機的磁盤文件爲系統盤/data/vm.img,數據盤/data/data.img。
內存快照
1. 創建快照
命令:virsh save vm vm.snapshot1
[root@localhost ~]# virsh save vm vm.snapshot1
Domain vm saved to vm.snapshot1 |
創建完後虛擬機會關機:
[root@localhost ~]# virsh list --all Id Name State ---------------------------------------------------- - vm shut off |
2. 回滾快照
命令:virsh restore vm.snapshot1
[root@localhost ~]# virsh restore vm.snapshot1 Domain restored from vm.snapshot1
[root@localhost ~]# virsh list Id Name State ---------------------------------------------------- 11 vm running |
注:
1. 只能對關機狀態的虛擬機進行回滾快照;
2. 內存快照做完後,如果虛擬機磁盤文件發生修改,可能會導致corruption。
磁盤內部快照
磁盤內部快照可以在虛擬機開機狀態創建,但是創建過程中虛擬機處於paused狀態,
1. 創建快照
命令:virsh snapshot-create-as --domain vm --name vm1
這條命令執行後,虛擬機會變成paused狀態
[root@localhost ~]# virsh list Id Name State ---------------------------------------------------- 13 vm paused |
等快照創建完成,會重新變回running。
2. 查看快照
命令:virsh snapshot-list –domain vm
[root@localhost ~]# 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對外部快照支持不完整,只能創建,不能刪除和回滾,如果要刪除一個有外部快照的虛擬機,會出現以下報錯:
[root@localhost ~]# 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,與之前原理中分析的一致:
[root@localhost 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 [root@localhost 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命令也可以查看鏈關係:
[root@localhost 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中了:
[root@localhost 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腳本封裝外部快照的操作。