kvm環境で、ホストOSの終了時にゲストOSをサスペンドさせるお話とupstartのお話(on Ubuntu Server 10.10)
皆さん仮想化してますか?
近頃ナウなヤングの間でばっかうけな仮想化ソリューションLinux kvmですけれど、これを実運用しようと思うと、色々手間暇かけてやる必要があります。
ということで、今回は「kvm環境でホストOSのシャットダウン時、ゲストOSを安全に守る方法」として、ホストOSの終了時にゲストOSをハイバネーションさせる(メモリ内容をファイルに書きだしてサスペンド)方法を紹介します。
環境
今回の環境はUbuntu server 10.10です。また、kvmの管理ツールとしてvirshが入っているものとします。
本当はLTSである10.04を使いたかったのですが、10.04のaptから入るkvm環境だと、何故かWindowsゲストのレジュームに失敗してしまうため、10.10を対象としました。
概要
実はlibvirt-suspendonrebootとかいうスクリプトが用意されていまして、これを/etc/init.dに置いてやると、さもサスペンドが実現できる……かのような錯覚に陥ります。しかし、initにupstartを採用しているUbuntuでは非同期にサービスの起動・終了が行われます。
つまり、サスペンドの処理が完了する前にkvm関連のサービス(libvirtdとか)がぶち殺されてしまうことがあり得るということです。
解決方法は、upstartの設定を書いてやることです。ハイバネーションを行うjobを定義し、ハイバネーションのjobが完了した後にlibvirtdが終了するように依存関係を記述します。
実際の設定
では、実際に設定をみてみます。
ハイバネーション処理を行うjobの定義
まず、/etc/init/kvm-guest.confを作成し、以下の内容を記述します。
description "kvm-guest" author "you" start on started libvirt-bin stop on starting rc RUNLEVEL=[06] # ランレベルが0または6の時に実行 pre-start script for domain in ${suspenddir}/*dump; do if [ -f $domain ]; then domain=$(basename $domain .dump) echo "resuming $domain ..." virsh restore ${suspenddir}/${domain}.dump && rm ${suspenddir}/${domain}.dump fi done end script post-stop script suspenddir=/var/lib/libvirt/autosuspend for domain in /etc/libvirt/qemu/*xml; do domain=$(basename $domain .xml) state=$(virsh domstate $domain) if [ "$state" = "running" ]; then virsh save ${domain} ${suspenddir}/${domain}.dump fi done end script
処理内容はlibvirt-suspendonrebootの処理内容を丸コピペしたものです。
"pre-start script"から"end script"までがレジュームの処理となります。この部分は"start on started libvirt-bin"の記述により、システム起動時にlibvirt-binジョブ完了の後(つまりlibvirtd起動後)に呼び出され、レジューム処理(virsh restoreの実行)を行います。
"post-stop script"から"end script"までがハイバネーション処理となり、システム終了時に呼び出されて実行されます(virsh save)。
なお、ハイバネーション時に作成されるメモリのダンプファイルは"/var/lib/libvirt/autosuspend"ディレクトリ以下に作成されるので、このディレクトリを作成しておいてください。
sudo mkdir /var/lib/libvirt/autosuspend
libvirtd、及びqemu-kvmのinitファイルの編集:依存関係の記述
次に、libvirtdの定義ファイルを編集し、サスペンド処理のジョブが完了した後にlibvirtが終了されるように依存関係を記述します。
/etc/init/libvirt.conf :
description "libvirt daemon" author "Dustin Kirkland <kirkland@canonical.com>" start on runlevel [2345] # stop on runlevel [!2345] # デフォルトのものをコメントアウト stop on stopped kvm-guest # kvm-guestジョブが完了後にこのジョブが実行 expect daemon respawn ……(省略)……
次に、qemu-kvmの定義ファイルを編集し、libvirtdが終了した後にqemu-kvmのジョブが実行されるように、依存関係を記述します*1。
# qemu-kvm description "KVM" author "Dustin Kirkland <kirkland@canonical.com>" start on runlevel [2345] stop on stopped libvirt-bin # 追記:libvirt-binジョブ完了後にこのジョブが実行 pre-start script …(省略)…
virsh saveを行えるようにする
さて、上の設定を行ったところで、実はこの段階ではvirsh saveが実行できず、ハイバネーションが実行できない場合があります*2。
virsh # save MACHINENAME /var/lib/libvirt/autosuspend/MACHINENAME.dump # このまま固まって応答がない、/var/lib/libvirt/autosuspend/MACHINENAME.dumpのファイルサイズが一向に大きくならない
これは、セキュリティツールであるapparmor*3による制限です。この場合はapparmorの設定を変更して下さい。設定ファイルは/etc/apparmor.d以下のディレクトリにあります。
なお、10.04の環境においては、"/etc/apparmor.d/abstractions/libvirt-qemu"に、以下の設定を書き加えることで動作しました。
owner /var/lib/libvirt/autosuspend/** rw,
設定を変更した後には、apparmorの再起動を行って下さい。
sudo service apparmor restart
最終確認
さて、最後にserviceコマンドを用いてsuspend/resumeがきちんとできるか確認を行います。
# 仮想マシンが動いている状態で sudo service kvm-gueset stop # サスペンドに暫く時間が掛かる ls /var/lib/libvirt/autosuspend/MACHINENAME.dump # dumpファイルができていたら、サスペンド成功。 sudo service libvirt-bin start sudo service kvm-guest start virsh list Id Name State ---------------------------------- 1 MACHINENAME running
後は、再起動テストなどをして確認を行って下さい。
最後に
割とsaveコマンドの挙動が怪しかったり、virsh saveの実行完了までに時間が掛かったりするので、acpiでshutdownを発行するのも良いかと思います*4。その場合は、マシンがきちんとshut off状態になるまで待ってあげるなどの工夫をしてあげて下さい。
「なんでわざわざゲストOSのハイバネーション用ジョブを作ってるの?libvirt-bin.confにpre-stopで記述すればいいんじゃ?」と思った人、鋭いです。私も初めそのつもりでやっていたのですが、どうやらpre-stopはシステムシャットダウン時には呼ばれず、initctl stopを用いた場合のみにしか呼ばれないようです*5。10.04が出たばかりの頃はそんな挙動じゃなかったんですけれど……。
今回のエントリについてですが、検証するのに手間が掛かることから、割と記憶に頼っている部分が多いです。また、Upstartの挙動とかも割と自信ないです。抜けや間違いがありましたら、是非コメント欄やメール、twitterなどで教えて頂けると有難いです。
参考リンク
*1:おそらくこれはやらなくても大丈夫だとは思いますが、念のため
*2:10.04にて再現。10.10のクリーンインストールの状態では未検証のため不明
*3:プログラムごとにアクセスできるライブラリ、およびファイルを制限するセキュリティツール
*4:FreeBSDはacpiでのシャットダウンがうまくいかないからハイバネーションにこだわったんですけどNE……
*5:http://www.usupi.org/sysad/189.htmlより。私の環境でも確かにそうであることを確認