Rootfs over Virtfsでゲストを起動する
はじめに
Virtfsを使うと、ホストのディレクトリをゲストにmountさせることができます。ここではdebootstrap等でホストに用意したrootfsを使ってゲストをブートさせる方法について説明します。
What's Virtfs?
Virtfsは、"File system pass-through/Paravirtual file system"を実現する機能です。パススルーといえば、ゲスト(カーネル)がホストマシンの物理デバイスに直接アクセスする機能が一般的ですが、virtfsはファイルシステムで似たようなことを実現しています。Virtfsを使うと、ホストのディレクトリをゲストにmountさせることができ、その結果、ゲストのアプリケーションがホストのファイルに直接アクセスできるようになります。もし複数のゲストが同じホストのディレクトリをmountするならば、同じディレクトリが見えることになります。
これだけ聞くとNFSと同じじゃないかと思われるかもしれませんが、virtfsは(仮想)ネットワークデバイスを介した通信を必要としません。代わりに9P over VirtIO*1でゲスト・qemu間のAPI/データ転送を実現しています。(qemuが9Pサーバ、ゲストカーネルが9Pクライアントになります。)
Virtfsの詳しい説明は、開発者が公開している資料を参照ください。
環境
注意
今回はとりあえずブートさせることが目的なので、セキュリティに関しては何も考えていません。(qemuをrootで動かし、virtfsのsecurity_modelをnoneにしています。)
qemu-kvm再ビルド
残念ながらUbuntu添付のqemu-kvm (0.14.0+noroms4)はvirtfsが有効になっていません。まずは、再ビルドしてvirtfsが使えるようにします。
といっても、ソースコードを修正する必要はなく、以下のようにlibattr1-devパッケージをインストールした状態でビルドすればOKです。
sudo apt-get build-dep --no-install-recommends qemu-kvm sudo apt-get install libattr1 libattr1-dev apt-get source qemu-kvm cd qemu-kvm-0.14.0+noroms/ dpkg-buildpackage
ゲストrootfs準備
debootstrapを使えば簡単に用意することができます。
mkdir natty_root
sudo debootstrap natty natty_root
initrdカスタマイズ
ブート時に9pファイルシステムをrootにmountできるようにinitrdを修正します。
mkdir initrd cd initrd zcat /boot/initrd.img-2.6.38-8-generic|cpio -i # いろいろ修正(後述) find . | cpio --quiet --dereference -o -H newc|gzip -c > ../initrd.img-2.6.38-8-virtfs
やらなければならないことは、
- qemu引数に指定するmount_tagと9pファイルシステムのmount時に指定する識別名*2を一致させるため、カーネル引数経由で渡すmount_tagをinitrdのinitで解釈できるようにする
- 9pファイルシステムのmount前に9p/virtio関連のカーネルモジュールをロードするため、当該モジュールをinitrdに入れておく*3
修正箇所/修正作業は以下のとおりです。
- init修正、scripts/9pを用意
- 9p関連のカーネルモジュールをコピー、depmod -a
init(シェルスクリプト)は以下のdiffのように修正しました。
diff -u initrd.orig/init initrd/init --- initrd.orig/init 2011-05-16 10:37:02.796295655 +0900 +++ initrd/init 2011-05-16 10:28:30.903695192 +0900 @@ -52,6 +52,7 @@ export blacklist= export resume= export resume_offset= +export mount_tag # mdadm needs hostname to be set. This has to be done before the udev rules are called! if [ -f "/etc/hostname" ]; then @@ -207,6 +208,9 @@ hwaddr=*) BOOTIF=${x#BOOTIF=} ;; + mount_tag=*) + mount_tag=${x#mount_tag=} + ;; esac done @@ -326,6 +330,7 @@ unset readonly unset resume unset resume_offset +unset mount_tag # Chain to real filesystem exec run-init ${rootmnt} ${init} "$@" <${rootmnt}/dev/console >${rootmnt}/dev/console 2>&1
scripts/9pは新規に用意しました。(scripts/nfsを参考にしました。)
# 9p filesystem mounting -*- shell-script -*- mountroot() { modprobe virtio_pci modprobe 9p modprobe 9pnet_virtio mount -t 9p -o trans=virtio ${mount_tag} ${rootmnt} }
initrdに入れなければならなかったカーネルモジュールは以下の3つでした。virtio関連のモジュールは最初から入っていました。
/lib/modules/2.6.38-8-generic/kernel/fs/9p/9p.ko
/lib/modules/2.6.38-8-generic/kernel/net/9p/9pnet.ko
/lib/modules/2.6.38-8-generic/kernel/net/9p/9pnet_virtio.ko
cd initrd/ cp -a /sbin/depmod sbin/ sudo chroot . /sbin/depmod -a 2.6.38-8-generic
以上でinitrdの準備が終わりました。
ブート!
というわけでゲストのブートです。カーネルはホストにあるものを使います。識別名は-append 'mount_tag=natty'と指定、同じ識別名を-virtfsの引数の一部に指定します。
sudo kvm -enable-kvm -kernel /boot/vmlinuz-2.6.38-8-generic -initrd /path/to/initrd.img-2.6.38-8-virtfs -append 'mount_tag=natty single' -virtfs local,path=/path/to/natty_root,mount_tag=natty,security_model=none -curses
上手くいくとブートするはずです。
訂正:試したのはシングルユーザモードだけです。
動作確認
簡単な動作確認です。確かにrootfsが9pファイルシステムになっています。
guest# mount natty on / type 9p (rw) none on /proc type proc (rw,noexec,nosuid,nodev) none on /sys type sysfs (rw,noexec,nosuid,nodev) none on /dev type devtmpfs (rw,mode=0755) none on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=0620) none on /sys/fs/fuse/connections type fusectl (rw) none on /sys/kernel/debug type debugfs (rw) none on /sys/kernel/security type securityfs (rw) none on /dev/shm type tmpfs (rw,nosuid,nodev) none on /var/run type tmpfs (rw,nosuid,mode=0755) none on /var/lock type tmpfs (rw,noexec,nosuid,nodev) guest# touch /tmp/hellovirtfs
↑ゲストで作った(ようにみえる)ファイルが、↓ホストにもちゃんと生成されていることが確認できました。
$ ls /path/to/natty_root/tmp
hellovirtfs
おわりに
なにはともあれ、ホストのファイルシステム上にあるrootfsでゲストがブートできました。
ゲスト環境を構築するときは、virt-installなどを使ってディスクイメージを用意するのが一般的ですが、virtfsの場合、代わりにdebootstrapやOpenVZのprecreated rootfsなどを使えるので多少は楽になるのではないでしょうか。
btrfsのsubvolumeを使うと、qcow2のように一回用意したrootfsで複数の(使い捨て)環境が用意できてさらに便利だと思います。
追記:kvm tools: Add virtio-9pのスレッドでも似たような話をしてますね。ここで紹介したやり方とは違うみたいですが。。。