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 - vm shut off |
2. 復原快照
指令:virsh restore vm.snapshot1
[root@localhost ~]# virsh restore vm.snapshot1 Domain restored from vm.snapshot1 11 vm running |
注:
1. 隻能對關機狀态的虛拟機進行復原快照;
2. 記憶體快照做完後,如果虛拟機磁盤檔案發生修改,可能會導緻corruption。
磁盤内部快照
磁盤内部快照可以在虛拟機開機狀态建立,但是建立過程中虛拟機處于paused狀态,
指令:virsh snapshot-create-as --domain vm --name vm1
這條指令執行後,虛拟機會變成paused狀态
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 virtual size: 1.0G (1073741824 bytes) disk size: 196K backing file: /data/data.img 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'> <source file='/data/data.snapshot1'/> <target dev='vdb' bus='virtio'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/> |
Libvirt目前不支援復原外部快照,隻能純手工操作。為了證明在復原快照後虛拟機确實回到了快照記錄的狀态,我們在虛拟機中在/root下建立一個空檔案test。然後關閉虛拟機并把虛拟機的磁盤改回vm.img和data.img,開機後會發現/root/test不見了,可以證明虛拟機檔案系統回到了建立快照的時間點。
由上面的操作我們可以得出結論:復原到某個快照,就是把虛拟機目前磁盤檔案改為這個快照檔案的backing file;快照名和快照檔案名并不對應,例如建立snapshot1後産生的檔案vm.snapshot1中記錄的并不是快照snapshot1的内容,它的backing file才是。在下面介紹删除快照時,牢記這點尤其重要。
3. 删除快照
在原理中已經介紹過,删除快照有blockcommit和blockpull兩種思路,由于blockpull不支援指定top的合并,下面将隻介紹blockcommit方式。我們先為虛拟機vm多建立幾個快照,現在快照鍊為(以下操作都以系統盤為例,資料盤同理):
qemu-img指令也可以檢視鍊關系:
[root@localhost data]# qemu-img info --backing-chain vm.snapshot4 image: vm.snapshot4 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 disk size: 196K backing file: /data/vm.snapshot2 image: /data/vm.snapshot2 backing file: /data/vm.img image: /data/vm.img disk size: 1.5G compat: 0.10 |
現在我們要删除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 disk size: 1.1M backing file: /data/vm.snapshot3 compat: 1.1 image: /data/vm.snapshot3 disk size: 388K backing file: /data/vm.snapshot1 image: /data/vm.snapshot1 disk size: 1.5M image: /data/vm.img disk size: 1.5G |
總結
三種快照中,隻有磁盤外部快照可以不停機建立,是以這種快照最符合我們平時的需求,後續研究也重點關注外部快照。不幸的是libvirt對外部快照的支援太弱,大部分操作需要我們人腦思考、手工操作。接下來研究的重點有以下幾點:
1. 測試外部快照建立時是否真正零停機時間;