docs/live_migration.md
This document gives examples of how to use the live migration support in Cloud Hypervisor:
:warning: These examples place sockets in /tmp. This is done for simplicity and should not be done in production.
Launch the source VM (on the host machine):
$ target/release/cloud-hypervisor
--kernel ~/workloads/vmlinux \
--disk path=~/workloads/focal.raw \
--cpus boot=1 --memory size=1G,shared=on \
--cmdline "root=/dev/vda1 console=ttyS0" \
--serial tty --console off --api-socket=/tmp/api1
Launch the destination VM from the same directory (on the host machine):
$ target/release/cloud-hypervisor --api-socket=/tmp/api2
Get ready for receiving migration for the destination VM (on the host machine):
$ target/release/ch-remote --api-socket=/tmp/api2 receive-migration receiver_url=unix:/tmp/sock
Start to send migration for the source VM (on the host machine):
$ target/release/ch-remote --api-socket=/tmp/api1 send-migration destination_url=unix:/tmp/sock,local=on
When the above commands completed, the source VM should be successfully migrated to the destination VM. Now the destination VM is running while the source VM is terminated gracefully.
Hint: For developing purposes, same-host TCP migrations are also supported.
In this example, we will migrate a VM from one machine (src) to
another (dst) across the network. To keep it simple, we will use a
minimal VM setup without storage.
Make sure that src and dst can reach each other via the
network. You should be able to ping each machine. Also each machine
should have an open TCP port.
You will need a kernel and initramfs for a minimal Linux system. For this example, we will use the Debian netboot image.
Place the kernel and initramfs into the same directory on both
machines. This is important for the migration to succeed. We will use
/var/images:
src $ export DEBIAN=https://ftp.debian.org/debian/dists/stable/main/installer-amd64/current/images/netboot/debian-installer/amd64
src $ mkdir -p /var/images
src $ curl $DEBIAN/linux > /var/images/linux
src $ curl $DEBIAN/initrd.gz > /var/images/initrd
Repeat the above steps on the destination host.
If Unix socket is selected for migration, we can tunnel traffic through "socat".
On the receiver side, we prepare an empty VM:
dst $ cloud-hypervisor --api-socket /tmp/api
In a different terminal, configure the VM as a migration target:
dst $ ch-remote --api-socket=/tmp/api receive-migration receiver_url=unix:/tmp/sock
In yet another terminal, forward TCP connections to the Unix domain socket:
dst $ socat TCP-LISTEN:{port},reuseaddr UNIX-CLIENT:/tmp/sock
Let's start the VM on the source machine:
src $ cloud-hypervisor \
--serial tty --console off \
--cpus boot=2 --memory size=4G \
--kernel /var/images/linux \
--initramfs /var/images/initrd \
--cmdline "console=ttyS0" \
--api-socket /tmp/api
After a few seconds the VM should be up and you can interact with it.
First, we start socat:
src $ socat UNIX-LISTEN:/tmp/sock,reuseaddr TCP:{dst}:{port}
Replace {dst}:{port} with the actual IP address and port of your destination host.
Then we kick-off the migration itself:
src $ ch-remote --api-socket=/tmp/api send-migration destination_url=unix:/tmp/sock
When the above commands completed, the VM should be successfully migrated to the destination machine without interrupting the workload.
After a VM resumes from migration, snapshot restore, or any other path
that restores a previously paused VM, Cloud Hypervisor asks supported
network devices to announce the VM from its new host. For virtio-net,
the current implementation sets VIRTIO_NET_S_ANNOUNCE, raises a config
interrupt, retries that request a few times in the background, and also
sends host-side RARP announcements on the TAP interfaces. A guest
re-announcement therefore only happens when the guest negotiated
VIRTIO_NET_F_GUEST_ANNOUNCE. For vhost-user-net, the current implementation
only uses the guest announcement path.
If TCP socket is selected for migration, we need to consider migrating in a trusted network.
On the receiver side, we prepare an empty VM:
dst $ cloud-hypervisor --api-socket /tmp/api
In a different terminal, prepare to receive the migration:
dst $ ch-remote --api-socket=/tmp/api receive-migration receiver_url=tcp:0.0.0.0:{port}
Let's start the VM on the source machine:
src $ cloud-hypervisor \
--serial tty --console off \
--cpus boot=2 --memory size=4G \
--kernel /var/images/linux \
--initramfs /var/images/initrd \
--cmdline "console=ttyS0" \
--api-socket /tmp/api
After a few seconds the VM should be up and you can interact with it.
Initiate the Migration over TCP:
src $ ch-remote --api-socket=/tmp/api send-migration destination_url=tcp:{dst}:{port}
With migration parameters:
src $ ch-remote --api-socket=/tmp/api send-migration destination_url=tcp:{dst}:{port},downtime_ms=200,timeout_s=3600,timeout_strategy=cancel
Replace {dst}:{port} with the actual IP address and port of your destination host.
After completing the above commands, the source VM will be migrated to the destination host and continue running there. The source VM instance will terminate normally. All ongoing processes and connections within the VM should remain intact after the migration. See Network Announcements After Resume for the announcement behavior after a VM resumes.
TCP migration can be protected with TLS by passing tls_dir=<path> to
both receive-migration and send-migration.
The destination host needs a directory containing:
ca-cert.pem: the CA certificate used to verify source certificatesserver-cert.pem: the certificate presented by the destinationserver-key.pem: the private key for server-cert.pemThe source host needs a directory containing:
ca-cert.pem: the CA certificate used to verify the destination
certificateclient-cert.pem: the certificate presented by the sourceclient-key.pem: the private key for client-cert.pemProtect the private key files with file mode 0600 to reduce the risk
of accidental disclosure:
$ chmod 600 server-key.pem client-key.pem
Current TCP migration uses mutual TLS (mTLS) authentication. The source
verifies the destination certificate against ca-cert.pem and presents
client-cert.pem and client-key.pem. The destination presents
server-cert.pem and server-key.pem, and only accepts client
certificates that chain to ca-cert.pem.
Example receiver command:
dst $ ch-remote --api-socket=/tmp/api receive-migration receiver_url=tcp:0.0.0.0:{port},tls_dir=/path/to/dst-tls
Example sender command:
src $ ch-remote --api-socket=/tmp/api send-migration destination_url=tcp:{dst}:{port},tls_dir=/path/to/src-tls
TLS encryption is only supported with tcp:<host>:<port> migration
URLs, not with local UNIX-socket migration.
The commands below describe how to create the encryption material necessary to perform a same-host TCP migration with TLS.
First, set up the CA with a private key and a self-signed certificate.
$ certtool --generate-privkey \
--outfile ca-key.pem
To generate the certificate, provide at least your organization name in a template file, for example ca.info:
cn = Name of your organization
ca
cert_signing_key
Then you can create the certificate:
$ certtool --generate-self-signed \
--load-privkey ca-key.pem \
--template ca.info \
--outfile ca-cert.pem
Generate a private key and a certificate for each side. Cloud Hypervisor enforces mTLS, thus you also need a key for the migration sender.
The certificates also require template data. You can use separate templates for sender and receiver, but this example uses a single template, hosts.info:
organization = Name of your organization
cn = localhost
tls_www_server
tls_www_client
signing_key
dns_name = localhost
ip_address = 127.0.0.1
Then you can create the necessary files for the client (migration sender):
$ certtool --generate-privkey \
--outfile client-key.pem
$ certtool --generate-certificate \
--load-ca-certificate ca-cert.pem \
--load-ca-privkey ca-key.pem \
--load-privkey client-key.pem \
--template hosts.info \
--outfile client-cert.pem
And the necessary files for the server (migration receiver):
$ certtool --generate-privkey \
--outfile server-key.pem
$ certtool --generate-certificate \
--load-ca-certificate ca-cert.pem \
--load-ca-privkey ca-key.pem \
--load-privkey server-key.pem \
--template hosts.info \
--outfile server-cert.pem
For a same-host test, place these files in a single directory and use that directory as tls_dir for both commands.
On the receiver side, we prepare an empty VM:
dst $ cloud-hypervisor --api-socket /tmp/api-rcv
In a different terminal, prepare to receive the migration:
dst $ ch-remote --api-socket=/tmp/api-rcv receive-migration receiver_url=tcp:0.0.0.0:9000,tls_dir=/path/to/certificates
On the sender side, we start a VM:
src $ cloud-hypervisor \
--serial tty --console off \
--cpus boot=2 --memory size=4G \
--kernel /var/images/linux \
--initramfs /var/images/initrd \
--cmdline "console=ttyS0" \
--api-socket /tmp/api-src
After a few seconds the VM should be up and you can interact with it. Then trigger the migration:
src $ ch-remote --api-socket=/tmp/api-src send-migration destination_url=tcp:localhost:9000,tls_dir=/path/to/certificates
Cloud Hypervisor supports additional parameters to control the
migration process. Via the API or ch-remote, you may specify:
downtime_ms <milliseconds>: 300ms.timeout_s <seconds>: 3600s (one hour).timeout_strategy <strategy> ([cancel, ignore]): cancel.connections <amount>: 1 and 128. Defaults to 1.
Multiple connections are not supported with local UNIX-socket migration.memory_mode <precopy|postcopy>: postcopy resumes the destination first and faults
guest pages in on demand over a dedicated connection. Defaults to precopy.Cloud Hypervisor live migration compatibility has two dimensions: the Cloud Hypervisor version and the migration protocol version.
Cloud Hypervisor uses a versioned migration protocol for the messages exchanged between source and destination. Each Cloud Hypervisor release sends its current migration protocol version and accepts that version plus the immediately previous protocol version.
This means migration is supported from an older protocol version to the same or next protocol version. If the source and destination are more than one migration protocol version apart, the VM must be migrated through an intermediate Cloud Hypervisor version first.
The source sends its migration protocol version in the initial Start request.
The destination accepts the migration only if that protocol version is supported.
A zeroed Start command header is handled as protocol v0, so older
deployments that do not explicitly send a protocol version remain compatible.
The migration protocol version covers the protocol spoken after a migration connection has been established. Examples include adding a mandatory migration command, changing the order of migration protocol messages, or changing the framing or encoding of protocol command payloads.
The migration protocol version does not cover migration transport setup. For example, choosing TCP vs. UNIX sockets or opening the initial connection needs separate compatibility handling.
Migration protocol versioning is separate from snapshot state compatibility. Device and VM state changes still need to be handled by the respective snapshot serialization/deserialization code. That compatibility is Cloud Hypervisor version dependent, but it is not the responsibility of migration protocol versioning.