标签:
学习 KVM 的系列文章:
本文将梳理 QEMU/KVM 快照相关的知识,以及在 OpenStack Nova 中使用 libvirt 来对 QEMU/KVM 虚机做快照的过程。
QEMU/KVM 快照的定义:
关于 崩溃一致(crash-consistent)的附加说明:
快照还可以分为 live snapshot(热快照)和 Clod snapshot:
libvit 做 snapshot 的各个 API:
snapshot | 做快照的 libvirt API | 从快照恢复的 libvirt API | virsh 命令 |
磁盘快照 | virDomainSnapshotCreateXML(flags = VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY ) |
virDomainRevertToSnapshot | virsh snapshot-create/snapshot-revert |
内存(状态)快照 |
virDomainSave virDomainSaveFlags virDomainManagedSave |
virDomainRestore virDomainRestoreFlags virDomainCreate virDomainCreateWithFlags |
virsh save/restore |
系统检查点 | virDomainSnapshotCreateXML | virDomainRevertToSnapshot | virsh snapshot-create/snapshot-revert |
分别来看看这些 API 是如何工作的:
1. virDomainSnapshotCreateXML (virDomainPtr domain, const char * xmlDesc, unsigned int flags)
作用:根据 xmlDesc 指定的 snapshot xml 和 flags 来创建虚机的快照。
flags 包含 | 虚机处于运行状态时快照的做法 | 虚机处于关闭状态时快照的做法 |
0 | 创建系统检查点,包括磁盘状态和内存状态比如内存内容 | 保持关机时的磁盘状态 |
VIR_DOMAIN_SNAPSHOT_CREATE_LIVE | 做快照期间,虚机将不会被 paused。这会增加内存 dump file 的大小,但是可以减少系统停机时间。部分 Hypervisor 只在做外部的系统检查点时才设置该 flag,这意味着普通快照还是需要暂停虚机。 | |
VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY | 只做指定磁盘的快照。对应运行着的虚机,磁盘快照可能是不完整的(类似于突然电源被拔了的情形)。 | 只做指定磁盘的快照。 |
其内部实现根据虚机的运行状态有两种情形:
这里有其实现代码,可见其基本的实现步骤:
static virDomainSnapshotPtr qemuDomainSnapshotCreateXML {
.... call qemuDomainSnapshotCreateDiskActive { call qemuProcessStopCPUs # 停止 vCPUs for each disk call qemuDomainSnapshotCreateSingleDiskActive { call qemuMonitorDiskSnapshot # 调用 QEMU Monitor 去为每个磁盘做snapshot } call qemuProcessStartCPUs # 启动 vCPUs
}
.... }
2. virDomainSave 相关的几个 API
这几个API 功能都比较类似:
virDomainSave | 该方法会 suspend 一个运行着的虚机,然后保存期内存内容到一个文件中。成功调用以后,domain 将不会处于 running 状态。使用 virDomainRestore 来恢复虚机。 |
virDomainSaveFlags | 类似于 virDomainSave API,可使用几个 flags。一些 Hypervisor 在调用该方法前需要调用 virDomainBlockJobAbort() 方法来停止 block copy 操作。 |
virDomainManagedSave | 也类似于 virDomainSave API。主要区别是 libvirt 将其内存保存到一个受 libvirt 管理的文件中,因此libvirt 可以一直跟踪 snapshot 的状态;当调用 virDomainCreate/virDomainCreateWithFlags 方法重启该 domain的时候,libvirt 会使用该受管文件,而不是一个空白的文件,这样就可以 restore 该snapshot。 |
Features/SnapshotsMultipleDevices 这篇文章讨论同时对多个磁盘做快照的问题。
对运行中的 domain d-2 运行 “virsh save” 命令。命令执行完成后,d-2 变成 “shut off” 状态。
看看 domain 的磁盘镜像文件和 snapshot 文件:
内存数据被保存到 raw 格式的文件中。
要恢复的时候,可以运行 “vish restore d-2.snap1” 命令从保存的文件上恢复。
先看看它的用法:
virsh # help snapshot-create-as
NAME
snapshot-create-as - Create a snapshot from a set of args
SYNOPSIS
snapshot-create-as <domain> [<name>] [<description>] [--print-xml] [--no-metadata] [--halt] [--disk-only] [--reuse-external] [--quiesce] [--atomic] [--live] [--memspec <string>] [[--diskspec] <string>]...
DESCRIPTION
Create a snapshot (disk and RAM) from arguments
OPTIONS
[--domain] <string> domain name, id or uuid
[--name] <string> name of snapshot
[--description] <string> description of snapshot
--print-xml print XML document rather than create
--no-metadata take snapshot but create no metadata
--halt halt domain after snapshot is created
--disk-only capture disk state but not vm state
--reuse-external reuse any existing external files
--quiesce quiesce guest‘s file systems
--atomic require atomic operation
--live take a live snapshot
--memspec <string> memory attributes: [file=]name[,snapshot=type]
[--diskspec] <string> disk attributes: disk[,snapshot=type][,driver=type][,file=name]
其中一些参数,比如 --atomic,在一些老的 QEMU libary 上不支持,需要更新它到新的版本。根据 这篇文章,atomic 应该是 QEMU 1.0 中加入的。
(1)默认的话,该命令创建虚机的所有磁盘和内存做内部快照,创建快照时虚机处于 paused 状态,快照完成后变为 running 状态。持续时间较长。
<memory snapshot=‘internal‘/> <disks> <disk name=‘vda‘ snapshot=‘internal‘/> <disk name=‘vdb‘ snapshot=‘internal‘/> <disk name=‘vdc‘ snapshot=‘internal‘/> </disks>
每个磁盘的镜像文件都包含了 snapshot 的信息:
root@compute1:/var/lib/nova/instances/eddc46a8-e026-4b2c-af51-dfaa436fcc7b# qemu-img info disk
image: disk
file format: qcow2
virtual size: 1.0G (1073741824 bytes)
disk size: 43M
cluster_size: 65536
backing file: /var/lib/nova/instances/_base/fbad3d96a1727069346073e51d5bbb1824e76e34
Snapshot list:
ID TAG VM SIZE DATE VM CLOCK
1 1433950148 41M 2015-06-10 23:29:08 05:16:55.007
Format specific information:
compat: 1.1
lazy refcounts: false
你可以运行 snapshot-revert 命令回滚到指定的 snapshot 。
virsh # snapshot-revert instance-0000002e 1433950148
根据 这篇文章,libvirt 将内存状态保存到某一个磁盘镜像文件内 (”state is saved inside one of the disks (as in qemu‘s ‘savevm‘system checkpoint implementation). If needed in the future,we can also add an attribute pointing out _which_ disk saved the internal state; maybe disk=‘vda‘.)
(2)可以使用 “--memspec” 和 “--diskspec” 参数来给内存和磁盘外部快照。这时候,在获取内存状态之前需要 Pause 虚机,就会产生服务的 downtime。
virsh # snapshot-create-as 0000002e livesnap2 --memspec /home/s1/livesnap2mem,snapshot=external --diskspec vda,snapshot=external Domain snapshot livesnap2 created virsh # snapshot-dumpxml 0000002e livesnap2 <memory snapshot=‘external‘ file=‘/home/s1/livesnap2mem‘/> <disks> <disk name=‘vda‘ snapshot=‘external‘ type=‘file‘> <driver type=‘qcow2‘/> <source file=‘/home/s1/testvm/testvm1.livesnap2‘/> </disk> </disks>
(3)可以使用 “--disk-only” 参数,这时会做所有磁盘的外部快照,但是不包含内存的快照。不指定快照文件名字的话,会放在原来的磁盘文件所在的目录中。多次快照后,会形成一个外部快照链,新的快照使用前一个快照的镜像文件作为 backing file。
virsh # snapshot-list instance-0000002e --tree 1433950148 #内部快照 1433950810 #内部快照 1433950946 #内部快照 snap1 #第一个外部快照 | +- snap2 #第二个外部快照 | +- 1433954941 #第三个外部快照 | +- 1433954977 #第四个外部快照
而第一个外部快照的镜像文件是以虚机的原始镜像文件作为 backing file 的:
root@compute1:/var/lib/nova/instances/eddc46a8-e026-4b2c-af51-dfaa436fcc7b# qemu-img info disk.snap1
image: disk.snap1
file format: qcow2
virtual size: 30M (31457280 bytes)
disk size: 196K
cluster_size: 65536
backing file: /var/lib/nova/instances/eddc46a8-e026-4b2c-af51-dfaa436fcc7b/disk.swap #虚机的 swap disk 原始镜像文件
backing file format: qcow2
Format specific information:
compat: 1.1
lazy refcounts: false
也可以回滚到某一个snapshot:
snapshot-revert instance-0000002e 1433954941
(4)还可以使用 “--live” 参数创建系统还原点,包括磁盘、内存和设备状态等。使用这个参数时,虚机不会被 Paused(那怎么实现的?)。其后果是增加了内存 dump 文件的大小,但是减少了系统的 downtime。该参数只能用于做外部的系统还原点(external checkpoint)。
virsh # snapshot-create-as 0000002e livesnap3 --memspec /home/s1/livesnap3mem,snapshot=external --diskspec vda,snapshot=external --live Domain snapshot livesnap3 created virsh # snapshot-dumpxml 0000002e livesnap3 <memory snapshot=‘external‘ file=‘/home/s1/livesnap3mem‘/> <disks> <disk name=‘vda‘ snapshot=‘external‘ type=‘file‘> <driver type=‘qcow2‘/> <source file=‘/home/s1/testvm/testvm1.livesnap3‘/> </disk> </disks>
注意到加 “--live” 生成的快照和不加这个参数生成的快照不会被链在一起:
virsh # snapshot-list 0000002e --tree livesnap1 #没加 --live | +- livesnap2 #没加 --live livesnap3 #加了 --live | +- livesnap4 #加了 --live
综上所述,对于 snapshot-create-as 命令来说,
目前 libvirt 还不支持直接删除一个外部快照,可以参考 这篇文章 介绍的 workaround。
(1)对从镜像文件启动的虚机做快照
(2)对从卷启动的虚机做快照
严格地说,Nova 虚机的快照,并不是对虚机做完整的快照,而是对虚机的启动盘(root disk,即 vda 或者 hda)做快照生成 qcow2 格式的文件,并将其传到 Glance 中,其作用也往往是方便使用快照生成的镜像来部署新的虚机。Nova 快照分为 Live Snapshot (不停机快照)和 Clold Snapshot (停机快照)。
满足 2.1.1 中所述条件时,运行命令 ”nova image-create <instance name or uuid> <name of new image>“ 后,Nova 会执行 Live Snapshot。其过程如下:
(a)执行 qemu-img create -f qcow2 (qemu-img create 创建一个基于镜像1的镜像2,镜像2的文件将基于镜像1,镜像2中的文件将基于镜像1中的。在镜像2中所作的任何读写操作都不会影响到镜像1. 镜像1可以被其他镜像当做backing file. 但是要确保镜像1不要被修改)。比如: qemu-img create -f qcow2 -o backing_file=/var/lib/nova/instances/_base/ed39541b2c77cd7b069558570fa1dff4fda4f678,size=21474836480 /var/lib/nova/instances/snapshots/tmpzfjdJS/7f8d11be9ff647f6b7a0a643fad1f030.delta
(b)相当于执行 virsh blockjob <domain> <path> [--abort] [--async] [--pivot] [--info] [<bandwidth>]
(c)执行 ‘qemu-img convert -f qcow2 -o dest_fmt‘ 来将带 backing file 的 qcow2 image 转化成不带 backing file 的 flat image。其中 dest_fmt 由 snapshot_image_format 决定,有效值是 raw, qcow2, vmdk, vdi,默认值是 source image 的 format。比如: qemu-img convert -f qcow2 -O qcow2 /var/lib/nova/instances/snapshots/tmpzfjdJS/7f8d11be9ff647f6b7a0a643fad1f030.delta /var/lib/nova/instances/snapshots/tmpzfjdJS/7f8d11be9ff647f6b7a0a643fad1f030
来看看其中的一个关键 API int virDomainBlockRebase (virDomainPtr dom, const char * disk, const char * base, unsigned long bandwidth,unsigned int flags)
该 API 从 backing 文件中拷贝数据,或者拷贝整个 backing 文件到 @base 文件。
Nova 中的调用方式为:domain.blockRebase(disk_path, disk_delta, 0,libvirt.VIR_DOMAIN_BLOCK_REBASE_COPY |libvirt.VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT |libvirt.VIR_DOMAIN_BLOCK_REBASE_SHALLOW)
默认的话,该 API 会拷贝整个@disk 文件到 @base 文件,但是使用 VIR_DOMAIN_BLOCK_REBASE_SHALLOW 的话就只拷贝差异数据(top data)因为 @disk 和 @base 使用相同的 backing 文件。 VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT 表示需要使用已经存在的 @base 文件因为 Nova 会预先创建好这个文件。
简单的示意图:
这里 有个过程的 PoC 代码描述该过程。
这里 有该过程的完整 libvirt 日志分析。
这里 有文章讲 Libvirt Features/SnapshotsMultipleDevices。
当虚机不在运行中时或者不满足 live snapshot 的条件的情况下,Nova 会执行 Cold snapshot。其主要过程如下:
(1)当虚机处于 running 或者 paused 状态时:
(2)调用 qemu-img convert 命令将 root disk 的镜像文件转化一个相同格式的镜像文件。
(3)调用 virDomainCreateWithFlags API 将虚机变为初始状态
(4)将在步骤1 中卸载的 PCI 和 SR-IOV 设备重新挂载回来
(5)将元数据和 qcow2 文件传到 Glance 中
(0)从卷启动虚机,并且再挂载一个卷,然后运行 nova image-create 命令。
| image | Attempt to boot from volume - no image supplied | | key_name | - | | metadata | {} | | name | vm10 | | os-extended-volumes:volumes_attached | [{"id": "26446902-5a56-4c79-b839-a8e13a66dc7a"}, {"id": "de127d46-ed92-471d-b18b-e89953c305fd"}]
(1)从 DB 获取该虚机的块设备( Block Devices Mapping)列表。
(2)对该列表中的每一个卷,依次调用 Cinder API 做快照。对 LVM Driver 的 volume 来说,执行的命令类似于 " lvcreate --size 100M --snapshot --name snap /dev/vg00/lvol1“。
s1@controller:~$ cinder snapshot-list +--------------------------------------+--------------------------------------+-----------+------------------------+------+ | ID | Volume ID | Status | Name | Size | +--------------------------------------+--------------------------------------+-----------+------------------------+------+ | a7c591fb-3413-4548-abd8-86753da3158b | de127d46-ed92-471d-b18b-e89953c305fd | available | snapshot for vm10-snap | 1 | | d1277ea9-e972-4dd4-89c0-0b9d74956247 | 26446902-5a56-4c79-b839-a8e13a66dc7a | available | snapshot for vm10-snap | 1 | +--------------------------------------+--------------------------------------+-----------+------------------------+------+
(3)将快照的 metadata 放到 Glance 中。(注:该 image 只是一些属性的集合,比如 block device mapping, kernel 和 ramdisk IDs 等,它并没有 image 数据, 因此其 size 为 0。)
s1@controller:~$ glance image-show e86cc562-349c-48cb-a81c-896584accde3 +---------------------------------+----------------------------------------------------------------------------------+ | Property | Value | +---------------------------------+----------------------------------------------------------------------------------+ | Property ‘bdm_v2‘ | True | | Property ‘block_device_mapping‘ | [{"guest_format": null, "boot_index": 0, "no_device": null, "snapshot_id": | | # 分别是该虚机挂载的两个volume 的 | "d1277ea9-e972-4dd4-89c0-0b9d74956247", "delete_on_termination": null, | | snapshot 的信息 | "disk_bus": "virtio", "image_id": null, "source_type": "snapshot", | | | "device_type": "disk", "volume_id": null, "destination_type": "volume", | | | "volume_size": null}, {"guest_format": null, "boot_index": null, "no_device": | | | null, "snapshot_id": "a7c591fb-3413-4548-abd8-86753da3158b", | | | "delete_on_termination": null, "disk_bus": null, "image_id": null, | | | "source_type": "snapshot", "device_type": null, "volume_id": null, | | | "destination_type": "volume", "volume_size": null}] | | Property ‘checksum‘ | 64d7c1cd2b6f60c92c14662941cb7913 | | Property ‘container_format‘ | bare | | Property ‘disk_format‘ | qcow2 | | Property ‘image_id‘ | bb9318db-5554-4857-a309-268c6653b9ff | | Property ‘image_name‘ | image | | Property ‘min_disk‘ | 0 | | Property ‘min_ram‘ | 0 | | Property ‘root_device_name‘ | /dev/vda | | Property ‘size‘ | 13167616 | | created_at | 2015-06-10T05:52:24 | | deleted | False | | id | e86cc562-349c-48cb-a81c-896584accde3 | | is_public | False | | min_disk | 0 | | min_ram | 0 | | name | vm10-snap | | owner | 74c8ada23a3449f888d9e19b76d13aab | | protected | False | | size | 0 # 这里 size 是 0,表明该 image 只是元数据, | | status | active | | updated_at | 2015-06-10T05:52:24 | +---------------------------------+----------------------------------------------------------------------------------+
那为什么 Nova 不实现虚机的快照而只是系统盘的快照呢?据说,社区关于这个功能有过讨论,讨论的结果是不加入这个功能,原因主要有几点:
KVM 介绍(7):使用 libvirt 做 QEMU/KVM 快照和 Nova 快照 (Nova Instances Snapshot Libvirt)
标签:
原文地址:http://www.cnblogs.com/sammyliu/p/4468757.html