First attempt to use guestfish/libguestfs
はじめに
Fedora 11にはVMディスクイメージ内のファイル操作のためのツール(guestfish)が用意されています。
VMディスクイメージ内のファイルにアクセスするには、rawイメージならば単純にループバックマウントすれば良いだけですが、qcow2フォーマット等の場合だとqemu-nbdを使う(qemu-nbdの使いかた - KVM日記)などイメージフォーマットを解釈できるツールの併用が必要になり、作業が煩雑になってしまいます。(さらにLVMだったりするとかなり悲惨です;<)
guestfishを使うと、イメージフォーマットやパーティションの位置、マウントポイントや一時ファイルなどを意識することなくスクリプトで、もしくはインタラクティブにファイル操作が可能になります。
準備
以下ではホストOS、ゲストOSともにFedora 11、guestfish(libguestfs)はバージョン1.0.57をhttp://libguestfs.org/download/からダウンロードしてコンパイルしたものを用います。libguestfsをコンパイルするとguestfishが生成されます。guestfishはlibguestfsライブラリを使ってファイル操作を実行します。(libguestfsをコンパイルする途中にfebootstrapが起動してなにやらyum installが始まりますが、これは後で説明するゲスト用カーネルとinitrdを生成するためです。気長に待ちましょう。)
あと準備としてゲストVMディスクイメージを用意します。私はすでにFedora 11がインストールされたイメージを持っていたので、これをベースとするqcow2イメージを作ってそれを使いました。
host$ qemu-img create -b f11.img -f qcow2 f11.qcow2.img
簡単な使い方
では試しに上記イメージ(f11.qcow2.img)内の/root/ディレクトリ(rootパーティションは/dev/sda3にあります)をlsしてみましょう。引数なしでguestfishを起動するとインタラクティブモードになります。(virshに似ていますね。) なおプロンプトは">
host$ guestfish Welcome to guestfish, the libguestfs filesystem interactive shell for editing virtual machine filesystems. Type: 'help' for help with commands 'quit' to quit the shell ><fs> add-drive f11.qcow2.img ><fs> launch ><fs> mount /dev/sda3 / ><fs> ls /root/ .bash_history .bash_logout .bash_profile .bashrc .cshrc .tcshrc anaconda-ks.cfg install.log install.log.syslog
このように無事ファイルリストを得ることができました。
次はホストにあるファイルをコピーしてみましょう。
host$ echo hoge > /tmp/fuga ><fs> ls /tmp/ .ICE-unix yum.log ><fs> upload /tmp/fuga /tmp/fuga ><fs> ls /tmp/ .ICE-unix fuga yum.log ><fs> cat /tmp/fuga hoge
うまくいきました。
guestfishではゲスト内にあるファイルをviで編集することもできます。
><fs> edit /tmp/fuga ><fs> cat /tmp/fuga hoge fuga
作業が終わったら以下のようにguestfishを終了させます。
><fs> umount-all ><fs> kill-subprocess ><fs> quit
launchやumount-allだけでなくlsやmountも実はguestfishの内部コマンドです。(commandというディスクイメージ内の任意のコマンドを実行するコマンドもあります。)使用可能なコマンドはオンラインマニュアル等を参照してください。
内部実装
さてここからは内部実装の話題です。(むしろこっちがメイン?)
guestfishはどうやってVMディスクイメージの内部にアクセスしているのでしょうか?実はguestfishはサブプロセスとしてqemu(VM)を起動し、そのqemuがVMディスクイメージのマウントやファイルアクセスを行なっています。
上記例では、launchコマンドの箇所でqemuが起動しています。psで見てみると下ようなqemuプロセスが立ち上がっているのがわかります。
host$ ps xa |grep qemu-kvm /usr/bin/qemu-kvm -drive file=f11.qcow2.img,cache=off,if=ide -m 500 -no-reboot -kernel /usr/local/lib/guestfs/vmlinuz.fedora-11.x86_64 -initrd /usr/local/lib/guestfs/initramfs.fedora-11.x86_64.img -append panic=1 console=ttyS0 guestfs=10.0.2.4:6666 -nographic -serial stdio -net channel,6666:unix:/tmp/libguestfsQz9LZs/sock,server,nowait -net user,vlan=0 -net nic,model=virtio,vlan=0 -no-hpet -rtc-td-hack
この中の-kernel, -initrdで指定しているのがlibguestfsのコンパイル時にゴリゴリと生成していたカーネルとinitrdです。
host$ file /usr/local/lib/guestfs/* /usr/local/lib/guestfs/initramfs.fedora-11.x86_64.img: gzip compressed data, from Unix, last modified: Sat Jul 11 01:02:44 2009, max compression /usr/local/lib/guestfs/vmlinuz.fedora-11.x86_64: Linux kernel x86 boot executable bzImage, version 2.6.29.5-191.fc11.x86_64 (mockb, RO-rootFS, root_dev 0x902, swap_dev 0x2, Normal VGA
つまり、guestfishで立ち上げるVMではVMディスクイメージ内部のカーネルは使っていないということですね。
さて今度は引数"-net channel,6666:unix:/tmp/libguestfsQz9LZs/sock,server,nowait"とゲストカーネル引数(-append)に指定している"guestfs=10.0.2.4:6666"に注目してみましょう。
前者は、/tmp/libguestfsQz9LZs/sock (UNIXソケット)へのtcp接続はゲスト内部の6666ポートへ転送しろ、という意味で、qemuの機能です。netstatで見ると確かにqemuがLISTENしていますね。
host$ sudo netstat -nap |grep libguestfs unix 2 [ ACC ] STREAM LISTENING 2203601 1993/qemu-kvm /tmp/libguestfsQz9LZs/sock unix 3 [ ] STREAM CONNECTED 2203606 1993/qemu-kvm /tmp/libguestfsQz9LZs/sock
guestfishはこのUNIXソケットを介してゲスト内のデーモンにコマンド実行をお願いしているようです。
では、ゲスト内で動いているのは誰か?それはguestfsdというデーモンです。(実行ファイルはinitrdの中に用意されています。)カーネル引数"guestfs=10.0.2.4:6666"は、カーネルではなく、このguestfsdに渡すために指定されているようです。
guestfishを使って確かめてみましょう。
><fs> sh 'ps | grep guestfsd' 1 ? 00:00:01 guestfsd ><fs> command 'netstat -nap' Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 10.0.2.10:40125 10.0.2.4:6666 ESTABLISHED 1/guestfsd Active UNIX domain sockets (servers and established) Proto RefCnt Flags Type State I-Node PID/Program name Path unix 2 [ ] DGRAM 3082 64/udevd @/org/kernel/udev/udevd
たしかに動いてますね。
というわけで、guestfishがlibguestfsで決められたAPIでguestfsdにリクエストを出して、ゲストVMディスクを操作しているらしいということがわかりました。なかなかアクロバティックなことをやっていますね ;-)
まとめ
guestfishはVMディスクイメージ内のファイル操作を、雑多な手順を踏む事なくインタラクティブに実行したり、(上記例では取り上げなかった)スクリプト処理することを可能にするとても便利なツールです。仮想化基盤システムの自動化などに大いに役立つのではないかと思います。
最後に試用中に発見した不具合を報告 ;)
## FIXED!! #><fs> add-drive ~/f11.qcow2.img #libguestfs: error: ~/f11.qcow2.img: No such file or directory ><fs> download /tmp/fuga /tmp/fuga libguestfs: error: guestfs_download: internal error: reply callback called twice libguestfs: error: guestfs_download: internal error: reply callback called twice libguestfs: error: guestfs_download reply failed, see earlier error messages
ホストのファイル指定に~が使えないようです。(不具合というか単に未実装なだけだと思いますが。)またゲストのファイルをホストにコピーするdownloadコマンドもうまく動きませんでした。
(追記)libguestfsの作者のRichardさんが颯爽と現れて前者の機能を作ってくれました!最新1.0.62では上記コマンドは成功します。すばらしい!