如何复制KVM虚拟机的快照

KVM虚拟机的快照(snapshot)功能可以保存虚拟机运行时的内存状态,恢复快照后虚拟机即可恢复到创建快照时的运行时状态,所有的软件运行状态依旧,类似于游戏中的SL(save/load)大法。我们可以使用快照功能实现很多业务,例如快速恢复沙箱的运行状态,而不用清理环境或是重启虚拟机等待服务一一启动。

但是KVM虚拟机的快照功能却不是很好利用,当需要大规模使用快照时就会发现似乎快照没那么好复制,复制出来的快照往往启动后也会失效,无法恢复,让批量化部署变得很困难。本文介绍了复制KVM虚拟机快照方法,用较低的成本将一个虚拟机的快照复制出来,方便批量部署。

快照命令

快照功能实际上是qemu的功能,这个功能保存了虚拟机的运行时状态,要使用这个功能,要求虚拟机的镜像为qcow2格式,raw格式等格式不支持这个功能。

libvirt作为我们常用的虚拟机管理工具,提供了快捷的方式来创建及恢复快照,这些命令如下:

1
2
3
4
5
6
7
8
9
10
11
# 为kvm01虚拟机创建快照
virsh snapshot-create kvm01

# 列出kvm01虚拟机的快照
virsh snapshot-list kvm01

# 恢复kvm01虚拟机到最近的快照
virsh snapshot-revert kvm01 --current

# 恢复kvm01虚拟机到指定的快照
virsh snapshort-revert kvm01 --snapshotname 1515143552

如果有虚拟机的话,可以尝试一把上面的命令,感受下虚拟机快照的效果。

openstack上的快照功能没有保存内存运行时的状态,仅仅保存了磁盘的状态,因此在恢复快照后虚拟机还是会重启,无法恢复到运行时的状态。

快照存储在哪儿?

快照的信息实际上是存到了镜像文件本身的,我们可以使用“qemu-img info”命令来获取到镜像的信息,我们对一个做了快照的镜像执行该命令,此时就可以看到快照的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
[[email protected] snapshot]# qemu-img info source.img 
image: source.img
file format: qcow2
virtual size: 20G (21474836480 bytes)
disk size: 3.1G
cluster_size: 65536
backing file: /opt/snapshot/base.img
Snapshot list:
ID TAG VM SIZE DATE VM CLOCK
1 1519990436 2.6G 2018-03-02 19:33:56 00:08:27.846
Format specific information:
compat: 1.1
lazy refcounts: false

在上面的镜像信息中,“Snapshot list”的部分就标示出了当前这个镜像有一个快照。

看来,我们要复制快照,就只能把做了快照的镜像也给复制一份了。一般虚拟机的镜像都有几G到几十G之大,复制起来比较浪费空间,但qcow2格式的镜像支持backing file功能,即使用一个基础镜像来创建一个镜像,创建出来的镜像仅保存增量信息,这样就大幅度地降低了要复制的文件的体积了。

于是我们就有了这么一个思路:

  1. 创建基础虚拟机镜像;
  2. 使用这个基础镜像创建新的虚拟机,并制作快照;
  3. 复制这个新的虚拟机镜像,达到复制快照的效果。

下面就开始试试这个思路吧。

创建基础虚拟机镜像

这个步骤可以略过,找一个现有的虚拟机,或是重新创建一个虚拟机都可以。

我们假设我们现在已经有了一个虚拟机镜像,名称为“base.img”。

创建新的虚拟机和快照

我们使用基础的虚拟机镜像再创建一个虚拟机镜像,名为“source.img”,命令如下:

1
qemu-img create -b base.img -f qcow2 source.img

我们把这个虚拟机镜像启动起来(比如使用libvirt创建一个叫source的虚拟机),运行到一个状态,随后创建快照:

1
2
3
4
5
# 为source创建一个快照
virsh snapshot-create --domain source

# 命令执行后将会反馈下面的内容
Domain snapshot 1519990436 created

此时快照已经创建好,名称为1519990436(使用时间戳来表示快照的名称)。

为了防止镜像体积持续变大,我们现在把虚拟机给强制关机:

1
virsh destroy source

为了确认镜像中已包含快照信息,可以使用以下的命令确认下:

1
qemu-img info source.img

或是直接使用snapshot-list命令来获取虚拟机source的快照信息:

1
2
3
4
[[email protected] ~]# virsh snapshot-list source
Name Creation Time State
------------------------------------------------------------
1519990436 2018-03-02 19:33:56 +0800 running

导出快照配置文件

使用”snapshot-dumpxml”命令可以将刚才的快照配置文件导出为一个xml文件,我们将这个导出的xml文件命名为snapshot.xml:

1
virsh snapshot-dumpxml --domain source --snapshotname 1519990436 > snapshot.xml

打开我们导出的快照配置文件,我们可以看到以下的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<domainsnapshot>
<name>1519990436</name>
<state>running</state>
<creationTime>1519990436</creationTime>
<memory snapshot='internal'/>
<disks>
<disk name='vda' snapshot='internal'/>
</disks>
<domain type='kvm'>
<name>source</name>
<uuid>12d3271a-c683-44b2-8244-dd4b53818035</uuid>
<memory unit='KiB'>4194304</memory>
<currentMemory unit='KiB'>4194304</currentMemory>
<vcpu placement='static'>2</vcpu>
<resource>
<partition>/machine</partition>
</resource>
<os>
<type arch='x86_64' machine='rhel6.6.0'>hvm</type>
<boot dev='hd'/>
</os>
<features>
<acpi/>
<apic/>
<pae/>
</features>
<clock offset='utc'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>restart</on_crash>
<devices>
<emulator>/usr/libexec/qemu-kvm</emulator>
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2' cache='writeback'/>
<source file='/opt/snapshot/source.img'/>
<target dev='vda' bus='virtio'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
</disk>
<controller type='usb' index='0' model='piix3-uhci'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
</controller>
<controller type='pci' index='0' model='pci-root'/>
<interface type='network'>
<mac address='52:54:00:15:ec:5b'/>
<source network='default'/>
<model type='virtio'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
</interface>
<input type='tablet' bus='usb'>
<address type='usb' bus='0' port='1'/>
</input>
<input type='mouse' bus='ps2'/>
<input type='keyboard' bus='ps2'/>
<graphics type='vnc' port='19000' autoport='no' listen='0.0.0.0'>
<listen type='address' address='0.0.0.0'/>
</graphics>
<video>
<model type='cirrus' vram='16384' heads='1' primary='yes'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
</video>
<memballoon model='virtio'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
</memballoon>
</devices>
</domain>
<cookie>
</cookie>
</domainsnapshot>

看着有点眼熟啊对不对?似乎就是在虚拟机的配置文件上面加了点东西嘛。

可以看到,这个配置文件中包含了快照的信息(名称、运行状态)和虚拟机的配置信息(几乎就是虚拟机的配置xml)。有了这个信息,我们可以考虑修 改这个配置文件,应用于我们复制出来的镜像中。

复制快照

要复制快照,我们先要复制镜像,将刚才的source.img复制为我们新的虚拟机需要使用的镜像(我们把它复制为test01.img文件):

1
cp source.img test01.img

随后我们再创建一个虚拟机test01,使用刚才复制出来的test01.img镜像文件。在define虚拟机后,我们获取下新创建的虚拟机的uuid和mac地址信息。这两个信息一般情况下我们是让libvirt随机创建的,因此需要提前获取到,使用以下命令即可查看新创建的虚拟机的配置:

1
virsh dumpxml test01

然后,我们拿这个test01虚拟机的信息来修改刚才导出的快照配置文件snaphost.xml,我们需要修改的部分如下:

  1. uuid
  2. 虚拟机名称
  3. 镜像的路径
  4. mac地址
  5. vnc端口

注意!注意!注意!如果以上的几个信息填写得和test01虚拟机的信息不符,将无法使用快照功能!

修改完成后,我们将这个修改完成的快照配置文件为test01虚拟机导入:

1
2
3
4
5
# 执行以下命令,让test01虚拟机也能感知到自己体内有个快照
virsh snapshot-create test01 snapshot.xml --redefine

# 执行命令后将会返回以下的信息
Domain snapshot 1519990436 created from 'snapshot.xml'

导入完成!

测试快照

我们可以直接使用snapshot-list命令来列出test01虚拟机的快照信息,可以看到:

1
2
3
4
[[email protected] ~]# virsh snapshot-list test01
Name Creation Time State
------------------------------------------------------------
1519990436 2018-03-02 19:33:56 +0800 running

确实是已经导入了一个快照,快照名称和上文中source虚拟机的快照是一模一样的。

我们尝试着恢复下快照:

1
virsh snapshot-revert --domain test01 --snapshotname 1519990436

此时连接到虚拟机上看看,刚才保存的运行时状态也都在跑着,成功!

其他

近期在pyajs上写一个libvirt的http wrapper,叫closestack,用于小型化的项目,但是各种事情太多,进度很慢,希望能慢慢搞完吧:https://github.com/pyajs/closestack

转载请注明出处:https://knktc.com/2018/06/12/how-to-duplicate-kvm-snapshot/

坚持原创技术分享,您的支持将鼓励我继续创作!