[08-JAN-2015 Update: Correct the blockcopy
CLI and update the final step to re-use the copy to be consistent with the scenario outlined at the beginning. Corrections pointed out by Gary R Cook at the end of the comments.]
[17-NOV-2014 Update: With recent libvirt/QEMU improvements, another way (which is relatively faster) to take a live disk backup via libvirt blockcommit
, here’s an example]
QEMU and libvirt projects has had a lot of block layer improvements in its last few releases (libvirt 1.2.6 & QEMU 2.1). This post discusses a method to do live disk storage migration with libvirt’s blockcopy
.
Context on libvirt blockcopy
Simply put, blockcopy
facilitates virtual machine live disk image copying (or mirroring) — primarily useful for different use cases of storage migration:
- Live disk storage migration
- Live backup of a disk image and its associated backing chain
- Efficient non-shared storage migration (with a combination of
virsh
operations snapshort-create-as
+blockcopy
+blockcommit
)
- As of IceHouse release, OpenStack Nova project also uses a variation of libvirt
blockcopy
, through its Python API virDomainBlockRebase
, to create live snapshots, nova image-create
. (More details on this in an upcoming blog post).
A blockcopy
operation has two phases: (a) All of source disk content is copied (or mirrored) to the destination, this operation can be canceled to revert to the source disk (b) Once libvirt gets a signal indicating source and destination content are equal, the mirroring job remains awake until an explicit call to virsh blockjob [. . .] --abort
is issued to end the mirroring operation gracefully . If desired, this explicit call to abort can be avoided by supplying --finish
option. virsh
manual page for verbose details.
Scenario: Live disk storage migration
To illustrate a simple case of live disk storage migration, we’ll use a disk image chain of depth 2:
base <-- snap1 <-- snap2 (Live QEMU)
Once live blockcopy is complete, the resulting status of disk image chain ends up as below:
base <-- snap1 <-- snap2
^
|
'------- copy (Live QEMU, pivoted)
I.e. once the operation finishes, ‘copy’ will share the backing file chain of ‘snap1’ and ‘base’. And, live QEMU is now pivoted to use the ‘copy’.
Prepare disk images, backing chain & define the libvirt guest
[For simplicity, all virtual machine disks are QCOW2 images.]
Create the base image:
$ qemu-img create -f qcow2 base 1G
Edit the base disk image using guestfish
, create a partition, make a file-system, add a file to the base image so that we distinguish its contents from its qcow2 overlay disk images:
$ guestfish -a base.qcow2
[. . .]
><fs> run
><fs> part-disk /dev/sda mbr
><fs> mkfs ext4 /dev/sda1
><fs> mount /dev/sda1 /
><fs> touch /foo
><fs> ls /
foo
><fs> exit
Create another QCOW2 overlay snapshot ‘snap1’, with backing file as ‘base’:
$ qemu-img create -f qcow2 -b base.qcow2 \
-o backing_fmt=qcow2 snap1.qcow2
Add a file to snap1.qcow2:
$ guestfish -a snap1.qcow2
[. . .]
><fs> run
><fs> part-disk /dev/sda mbr
><fs> mkfs ext4 /dev/sda1
><fs> mount /dev/sda1 /
><fs> touch /bar
><fs> ls /
bar
baz
foo
lost+found
><fs> exit
Create another QCOW2 overlay snapshot ‘snap2’, with backing file as ‘snap1’:
$ qemu-img create -f qcow2 -b snap1.qcow2 \
-o backing_fmt=qcow2 snap2.qcow2
Add another test file ‘baz’ into snap2.qcow2 using guestfish
(refer to previous examples above) to distinguish contents of base, snap1 and snap2.
Create a simple libvirt XML file as below, with source file pointing to snap2.qcow2 — which will be the active block device (i.e. it tracks all new guest writes):
$ cat <<EOF > /etc/libvirt/qemu/testvm.xml
<domain type='kvm'>
<name>testvm</name>
<memory unit='MiB'>512</memory>
<vcpu>1</vcpu>
<os>
<type arch='x86_64'>hvm</type>
</os>
<devices>
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/>
<source file='/export/vmimages/snap2.qcow2'/>
<target dev='vda' bus='virtio'/>
</disk>
</devices>
</domain>
EOF
Define the guest and start it:
$ virsh define etc/libvirt/qemu/testvm.xml
Domain testvm defined from /etc/libvirt/qemu/testvm.xml
$ virsh start testvm
Domain testvm started
Perform live disk migration
Undefine the running libvirt guest to make it transient[*]:
$ virsh dumpxml --inactive testvm > /var/tmp/testvm.xml
$ virsh undefine testvm
Check what is the current block device before performing live disk migration:
$ virsh domblklist testvm
Target Source
------------------------------------------------
vda /export/vmimages/snap2.qcow2
Optionally, display the backing chain of snap2.qcow2:
$ qemu-img info --backing-chain /export/vmimages/snap2.qcow2
[. . .] # Output removed for brevity
Initiate blockcopy
(live disk mirroring):
$ virsh blockcopy --domain testvm vda \
/export/blockcopy-test/backups/copy.qcow2 \
--wait --verbose --shallow \
--pivot
Details of the above command: It creates copy.qcow2 file in the specified path; performs a --shallow
blockcopy (i.e. the ‘copy’ shares the backing chain) of the current block device (vda
); –pivot will pivot the live QEMU to the ‘copy’.
Confirm that QEMU has pivoted to the ‘copy’ by enumerating the current block device in use:
$ virsh domblklist testvm
Target Source
------------------------------------------------
vda /export/vmimages/copy.qcow2
Again, display the backing chain of ‘copy’, it should be the resultant chain as noted in the Scenario section above).
$ qemu-img info --backing-chain /export/vmimages/copy.qcow2
Enumerate the contents of copy.qcow2:
$ guestfish -a copy.qcow2
[. . .]
><fs> run
><fs> mount /dev/sda1 /
><fs> ls /
bar
foo
baz
lost+found
><fs> quit
(You can notice above: all the content from base.qcow2, snap1.qcow2, and snap2.qcow2 mirrored into copy.qcow2.)
Edit the libvirt guest XML to use the copy.qcow2, and define it:
$ virsh edit testvm
# Replace the <source file='/export/vmimages/snap2.qcow2'/>
# with <source file='/export/vmimages/copy.qcow2'/>
[. . .]
$ virsh define /var/tmp/testvm.xml
[*] Reason for the undefining and defining the guest again: As of writing this, QEMU has to support persistent dirty bitmap — this enables us to restart a QEMU process with disk mirroring intact. There are some in-progress patches upstream for a while. Until they are in main line QEMU, the current approach (as illustrated above) is: make a running libvirt guest transient temporarily, perform live blockcopy
, and make the guest persistent again. (Thanks to Eric Blake, one of libvirt project’s principal developers, for this detail.)