libvirtで管理さてるゲストマシンを高速シンプロビジョニングしてくれるツールを作った

virt-clone コマンドって便利ですよね。
これ確かにスゲー便利なんですが、30回も40回も同じこと繰り返してると、スゲー疲れるわけです。
具体的に何に疲れるかって言うと、

  1. 待ち時間
  2. ネットワークの再設定


待ち時間に関しては、シェルスクリプトにしておくなりスパースでやるオプションを付けておけば"ある程度”我慢できるけど、ネットワークの再設定に関してはスゲータルい。
なんせ1回はゲストに入らなきゃいけない。


コレを回避するには、ゲストをクローンしたときのMACアドを、DHCPサーバに書いて決め打ちにすること。
そうすればある程度は管理できる。


…のはずだったけど、RHEL6系からは新しく追加されたネットワークデバイスは自動的に ONBOOT="no" な仕様にかわったので、そもそも立ち上がらないし、RHEL5系だと、すでにeth0があった場合、インクリメントされてeth1としてアタッチされたりで、コレはコレで美味しくない。


という訳で、これらの問題を一括で片付けるソリューションを思いついたので、やっつけで書いた。
https://github.com/kazuhisya/virt-thinpro
GitHub - kazuhisya/virt-thinclone: Virtualmachine ThinProvisioning Clone Tool

具体的には下記の流れになる

  1. qemu-imgでオリジナルのゲストの仮想ディスクから新しいゲストの仮想ディスクを作成する
    • ベースイメージから作成する場合、"皮”の部分だけ作られるので、一瞬で終わる(多分RHEVのpool機能はコレを使ってるんじゃないかな)
  2. virt-cloneに--preserve-dataオプションを付けてクローンする
    • --preserve-dataオプションは--fileオプションと一緒に使うんだけど 1. で作った仮想ディスクをアタッチする。これも一瞬
  3. 新しくできたクローンからMACアドをひっぱて来て、仮想ディスク内の各コンフィグに直接書きこんでしまう
    • これはlibguestfsっていうかなりミラクルなツールがあって、それを利用
    • 仮想ディスクをホスト側にマウントして、ifcfg-eth0とudevのルールを書き換えてます
    • 余談: ruby-libguestfsってライブラリもあるので、フツーに使えるはずなんだけど、オリジナル現行バージョンとRHELyumにあるバージョンがスゲー離れてる上、互換性なさそうな雰囲気がプンプンなので、後先考えてココは sysetm()で直接実行してます
  4. 最後の仕上げに新しい方の仮想ディスクのスナップショットを取る

出来上がった新しいゲストを実行してやれば、いきなりネットワークが設定された状態で立ち上がる。

[root@rhel6 virt-thinpro]# time ./virt-thinpro.rb -o el6-tmpl -n el6-01 -i 192.168.XXX.XXX
Formatting '/data/el6-01.img', fmt=qcow2 size=10485760000 backing_file='/data/el6-tmpl.img' encryption=off cluster_size=0 
Clone 'el6-01' created successfully.
Success: el6-01

real    0m10.841s
user    0m2.830s
sys     0m0.930s
[root@rhel6 virt-thinpro]# 

10秒ちょいですね!
多分ストレージがSSDとかだともちっと速いんでしょうが、一番時間掛かるのはゲストの仮想ディスクをマウントする所なので、劇的には改善されないと思います。(要確認?)

条件

  • ライセンス
    • GPLv2+
  • ホストOS
    • RHEL6(ないしそれと互換性のある物、例えばScientific Linux6とか、Fedora14もイケル?)
    • ruby-1.8.7
    • ruby-libvirt-0.3.0
    • libvirt
    • libvirt-client
    • qemu-img
    • python-virtinst(virt-clone)
    • guestfish-1.2.7
      • ruby-libvirt-0.3.0 は標準ではない。 公式gitから落としてコンパイルするなり、gemから落とすなりしてください
      • KVMで使うことを想定したけど、Xenでも他の動くはず。 qemu://〜 でコネクションしている部分を Xen用のURIに変えれば良いんじゃないですかね
  • ゲストOS
    • RHEL6(ないしそれと互換性のある物、例えばScientific Linux6とか、Fedora14もイケル?)
  • 書き換えてるのは下記
    • /etc/sysconfig/network-scripts/ifcfg-eth0
    • /etc/udev/rules.d/70-persistent-net.rules
  • どうしてもRHEL5系でやりたいなら下記を編集するロジックを入れればいいと思います
    • /etc/sysconfig/hwconf

制限

  • ゲストOSの仮想NICはeth0のみアサインされており、IP アドレスは固定IPを設定しているものとする
    • デフォゲの設定等はスクリプトの頭の方にあるコンフィグをいじるべし
  • ゲストOSの仮想ディスクは、ファイル形式かつqcow2形式の仮想ディスクが 1 個だけアサインされているものとする
      • /のマッピングは /dev/mapper/VolGroup-lv_root (LVM利用)がデフォ、違う場合はコンフィグをいじるべし
  • libvirtに元となるゲストが管理されていること
  • 元となるゲストは以後絶対に起動しないこと
    • 元ゲストのファイルシステムに変更が入ると、クローンした全てのゲストに変更が適応される
    • 検証してないけど最悪破損する? アップデートとかが一括で出来て楽ちんだーとか決して思わないこと
    • RedHatのソリューションであるところのRHEVもそういう仕様になってる(1回テンプレート化したら2度と戻せない)
    • なので、元ゲストはテンプレートマシンだと捉えるべき
  • 今のところ、ローカルのlibvirtにしかアクセスできない

使用方法

[root@rhel6 virt-thinpro]# ./virt-thinpro.rb --help
Usage: virt-thinpro [options]
    -o, --original          Original Domain Name
    -n, --new           New Domain Name
    -i, --ip              New Domain IP
  • -o: libvirtに登録されている元となるゲストの名前
  • -n: 新しくクローンして作成されるゲストの名前
  • -i: 新しくクローンして作成されるゲストのIP


コード的にあんまし美しくないから、ちょくちょく綺麗にしていきたいなぁ。
特にsystem()で直に叩いちゃってるところは、かなりイヤーンな感じ。
qemu-img createを叩いてる所はlibvirtのAPIで何とかできそうな気がしますね。

virt-clone叩いてる所はvirt-clone(python)からrubyに置き換えができそう。
あとlibguestfsの所を改善出来れば、リモートでも叩けそうですね。