qemu-kvm の live migration を試してみたよ


QEMU/KVM の仮想環境(ゲスト)を止めずに別のホストに移す。

構成は以下ような感じ。

  • ホスト環境 host1(192.168.1.11) で動作しているゲスト環境 guest(192.168.1.50)を別のホスト環境 host2(192.168.1.12) に live migration する。
  • ホストOS は debian lenny
  • ゲストOS も debian lenny
  • ゲストOS は PXE boot して、ルートファイルシステムはkujira(192.168.1.10)のiSCSIターゲットをマウントする。
  • qemu-kvm のバージョンは 0.12.3
  • host1 のCPUは Intel Core2 Duo で host2 は AMD Opteron。

この際、別ホストから guest へのTCPコネクションがどうなるか。

と、guest 内で開きっぱなしのファイルがどうなるかを検証する。

rootfsの作成

iscsiターゲットのrootfsを作る。

debootstrapでお手軽サクサク。

パーティション切るとloopbackマウントがめんどいので切らない。

kujira:~# dd if=/dev/zero of=disk1.img seek=10G bs=1 count=0
kujira:~# mkfs.ext3 disk1.img
kujira:~# mkdir mnt
kujira:~# mount -t ext3 -o loop disk1.img mnt/
kujira:~# debootstrap lenny mnt/
kujira:~# echo guest > mnt/etc/hostname
kujira:~# umount mnt/

/etc/ietd.conf にこんな風に書く。

Target iqn.1999-05.net.hamano:dick1
        Lun 0 Path=/path/to/disk1.img,Type=fileio

initramfsの作成

iSCSIイニシエータとして機能する initramfsを作る。

% mkidr initrd
% cd initrd
% zcat /boot/initrd.img-2.6.26-2-686 | cpio -iv

init スクリプトの root file system をマウントする直前に以下の5行を追加。

ipconfig -t 60 ${DEVICE}
modprobe iscsi_tcp
iscsid -f &
iscsiadm -m discovery -t sendtargets -p 192.168.1.10
iscsiadm -m node -T iqn.1999-05.net.hamano:dick1 -l

iSCSIターゲットをマウントするのに必要なファイルを配置、あと kernel module の libcrc32c.ko と crc32c.ko が必要。

% cp /usr/sbin/iscsid ./sbin/
% cp /usr/bin/iscsiadm ./bin/
% mkdir etc/iscsi/
% echo "InitiatorName=iqn.1999-05.net.hamano:hoge" > etc/iscsi/initiatorname.iscsi

% cp /lib/modules/2.6.26-2-686/kernel/lib/libcrc32c.ko lib/modules/2.6.26-2-686/kernel/lib
% mkdir lib/modules/2.6.26-2-686/kernel/crypto/
% cp /lib/modules/2.6.26-2-686/kernel/crypto/crc32c.ko lib/modules/2.6.26-2-686/kernel/crypto/

あと iscsid の中で getpwuid(3)呼んでいてユーザー名が root じゃなかったら蹴られてしまうので nss関連も適当に入れておく。

% cp /lib/libnsl.so.1 ./lib/
% cp /lib/libnss_files.so.2 ./lib/
% echo "root:x:0:0:root:/:/bin/sh" > etc/passwd
% echo "passwd: files" > etc/nsswitch.conf

この initrd に chroot して

# id
uid=0(root) gid=0

って帰ってくればひとまずOK

initramfsを固めて、tftpd サーバーに配置する。

% find ./ | cpio -o -H newc | gzip -9 > ../initrd.img-2.6.26-2-686

PXE boot の設定

pxelinux.cfg/default にこんな風に書く。パラメータに noresume を付けないと起動途中で止まってしまったので付けた。

default guest
label guest
    kernel vmlinuz-2.6.26-2-686
    append load initrd=initrd.img-2.6.26-2-686 root=/dev/sda noresume

qemu-kvmのネットワーク設定

host2, host1の両方のホストでtap50をブリッジする。 この辺りいつもは qemu-ifup scriptで自動化しているけど今回は手動で。

host2:~# tunctl -u hamano -t tap50
host2:~# brctl addif br0 tap50
host2:~# ip link set tap50 up
host2:~# brctl show br0
bridge name bridge id       STP enabled interfaces
br0     8000.001e0bbc9da5   no      eth0
                                    tap50

qemu-kvmの実行

host2 側で待機する。

host2:~# qemu -enable-kvm -m 512 \
             -net nic,macaddr=00:16:3E:00:FF:32,model=virtio \
             -net tap,vlan=0,ifname=tap50,script=no,downscript=no \
             -incoming tcp:0:4444

host1 側でゲストを起動する。

host1:~# qemu -enable-kvm -m 512 \
              -net nic,macaddr=00:16:3E:00:FF:32,model=virtio \
              -net tap,vlan=0,ifname=tap50,script=no,downscript=no -boot n

guest側でファイルをオープンしっぱなしにして、別のサーバーから guest にTCPコネクションを張っておく。

準備が整ったのでいざ Live Migration!

host1 のモニターコンソールから

(qemu) migrate -d tcp:host2:4444

live migration の進捗はこれで確認できる。

(qemu) info migrate
Migration status: active
transferred ram: 8000 kbytes
remaining ram: 533648 kbytes
total ram: 541056 kbytes
(qemu) info migrate
Migration status: completed

migrationはだいたい5秒くらいで完了した。

結果

TCPコネクションは切れていない、オープン中のファイルも問題なく読み書き出来た。

理論上出来ると解っていても実際に動かしてみるとなかなか感動。

補足事項

  • qemu-kvm 0.11.1 では nic の virtio を有効にして PXE boot 出来ない。
  • 最初、host1 の kernel を 2.6.32.9、host2 の kernel を 2.6.26.8 で試すと以下のエラーメッセージが出て失敗した。

    Unknown savevm section or instance ‘kvmclock’ 0 load of migration failed

kernel 2.6.31.5 < -> kernel 2.6.26.8 で試すと成功した。kvmのバージョンは出来るだけ揃えた方が無難。