2012/02/14

VMware Fusion でリンククローンを実現する

リンククローンとは、vCenter LabManager, VMware View (ViewComposer), vCloud Director などで実現されている機能で、親となる仮想マシンに対する差分だけの仮想マシンを作成、実行する機能だ。

同様の構成の仮想マシンが複数ある場合、共通部分を親で実装した後にリンククローンで展開することで共通部分の占めるディスク領域を削減でき、さらに差分だけ作ればいいので高速に展開ができる。

この機能は VMware Workstation にも搭載されており、ちょっとした検証時にディスク容量を気にせず仮想マシンを生み出せるので重宝してるのだが、残念ながら VMware Fusion には搭載されていない。
( そもそも、VMwareFusion には仮想マシンの複製機能が搭載されていない。)

これは不便だ、ということで試してみたのが以下手順。


● そもそものリンククローンの実装を見てみる

VMware View で搭載された ViewComposer はやや特殊だが、それ以外の実装は概ねスナップショットの仕組みを利用している。VMware におけるスナップショットは、「その時点でのディスクイメージを書き込み不可に設定」(+必要に応じてその瞬間のメモリ内容や仮想マシンの状態をファイルに出力)することである時点の状態を保持するものだ。

スナップショット採取後のディスクへの書き込みは、新たに作られた差分ディスクイメージに書き込まれる。読み込み時はまず差分ディスクイメージを読み、そこになければ親のディスクイメージを参照する。

なお、差分ディスクイメージはログではない。仮想マシン上のアプリケーションが、同じディスクブロックに2回書き込めば、最後の1回だけが差分ディスクに残り、先の1回は上書きされて消えてしまう。つまり、最悪のパターンを考えると親ディスクイメージと同じサイズの差分ディスク領域が作成される。これは頭から尻尾まで何らかのデータで上書きされた場合だ。

経験則では差分ディスクイメージは元ディスクイメージの2割〜3割程度だけが書き換えられる。40GBのディスク領域をもつ仮想マシンが1回スナップショットを取るたびに、8GBぐらいは消費されると考えた方がいい。

通常のスナップショットは同じ仮想マシンの仮想ディスクに対する差分になる。なので同じフォルダの中に親も差分もまとめて入れられている。

別のフォルダの親ディスクイメージ対する差分、がつくれれば、ある仮想マシンの差分だけのディスクイメージを持つ仮想マシンが作れる。これがリンククローンだ。


● VMDK

VMware における仮想ディスクイメージは VMDK と呼ばれる。
Virtual Machine Disk の略で、拡張子も .vmdk である。

しかし、vmdk の拡張子を持つファイルは単一の形態でもなければ、単一のファイルであるとも限らない。

VMDKの定義は公開されている、URLは以下の通り。

歴史的な事情もあり、VMDKの内部形式は多様にある。が、とりあえずデータセンター製品(ESX, ESXi など)で使われている形式を忘れて VMware Fusion などホスト型製品で使われている形式だけを見ると、以下の違いで4種類+αに分けられる。
  • 2GB 単位で分割するか (single or split)
  • あらかじめ容量分の領域を確保するか (growable or preallocated)
例えば、1ディスクイメージで必要に応じて容量が増大する形式は single growable virtual disk であり、2GBごとに区切られたディスクイメージで最初から指定容量を確保したものは preallocated virtual disk split in 2GB files となる訳だ。

これは、VMware Fusion のディスクイメージのチェックボックスに表現されている。



この、「事前のディスク領域を割り当てる」「2GB ファイルに分割」の2つのチェックがそれぞれの選択を示す。なお、デフォルトでは「事前のディスク領域を割り当てる」のチェックはオフ、「2GB ファイルに分割」のチェックはオン、growable virtual disk split in 2GB となっている訳だ。
(+αの形式については、また別に機会に説明したい。)

では、このデフォルトの場合で VMDK の中身を簡単に説明したい。

growable virtual disk split in 2GB  のVMDKを作成すると、<ディスク名>.vmdk のファイルと複数の <ディスク名>-s<番号>.vmdk のファイルができあがる。

このうち、<ディスク名>.vmdk は実はテキストファイルで読み書きが可能だ。
少々大きいが引用してみる。

# Disk DescriptorFile
version=1
encoding="UTF-8"
CID=13b99327
parentCID=ffffffff
isNativeSnapshot="no"
createType="twoGbMaxExtentSparse"
# Extent description
RW 4192256 SPARSE "osx-s001.vmdk"
RW 4192256 SPARSE "osx-s002.vmdk"
RW 4192256 SPARSE "osx-s003.vmdk"
RW 4192256 SPARSE "osx-s004.vmdk"
RW 4192256 SPARSE "osx-s005.vmdk"
RW 4192256 SPARSE "osx-s006.vmdk"
RW 4192256 SPARSE "osx-s007.vmdk"
RW 4192256 SPARSE "osx-s008.vmdk"
RW 4192256 SPARSE "osx-s009.vmdk"
RW 4192256 SPARSE "osx-s010.vmdk"
RW 4192256 SPARSE "osx-s011.vmdk"
RW 4192256 SPARSE "osx-s012.vmdk"
RW 4192256 SPARSE "osx-s013.vmdk"
RW 4192256 SPARSE "osx-s014.vmdk"
RW 4192256 SPARSE "osx-s015.vmdk"
RW 4192256 SPARSE "osx-s016.vmdk"
RW 4192256 SPARSE "osx-s017.vmdk"
RW 4192256 SPARSE "osx-s018.vmdk"
RW 4192256 SPARSE "osx-s019.vmdk"
RW 4192256 SPARSE "osx-s020.vmdk"
RW 40960 SPARSE "osx-s021.vmdk"
# The Disk Data Base
#DDB
ddb.adapterType = "lsilogic"
ddb.geometry.sectors = "63"
ddb.geometry.heads = "255"
ddb.geometry.cylinders = "5221"
ddb.uuid = "60 00 C2 98 9f a9 e6 26-65 9d 58 77 e0 5b 46 f6"
ddb.longContentID = "f2f9ab1f6c644c8cb517ee3113b99327"
ddb.toolsVersion = "8322"
ddb.virtualHWVersion = "7"
ddb.deletable = "true"

バージョンはこの設定ファイルの記述形式 encoding は文字コードを示している。CID は content ID の略で、このVMDKを示す 32bit の番号で、VMDKの作成時に乱数で決められる。parentCID は、このVMDKの親ディスクイメージとなる VMDKの CID になる。これは親を持たないVMDKのため ffffffff が記載されている。createType は先の 2GB で分けるか分けないか容量可変か固定サイズかをしめす識別子だ。

# から始まる行はコメントで、そこから下の RW で始まる一連の領域は2GBの差分ディスクの名前とどこからどこまでを担当しているかを示す。DDB は Disk DataBase の略でこのディスクイメージの諸元情報が記載されている。HDDとしてのセクタ、ヘッダ、シリンダの数や SCSIか IDEか、SCSIの場合は SCSIインターフェイスが BusLogic のSCSIカードのエミュレーションか LSI Logic のSCSIカードのエミュレーションかを示している。

面白いのは ddb.toolsVersion だ。これは、実は VMware Tools がそのディスク上のOSにインストールされているか、されているならその内部バージョンが記されている。VMware Tools が古いとか、入っていないとかは実はここで識別されているのだ。

閑話休題、parentCID で親ディスクイメージとの関連が取られていることが分かるが、では親ディスクイメージはどこにあるか。CIDに対応するファイルのパスを一々仮想化ソフトウェア側が覚えているかというとそうではない。

スナップショットを取ってみると分かる。

VMware Fusion でスナップショットをとると「<ディスク名>-<6桁の番号>.vmdk 」と「<ディスク名>-<6桁の番号>-s<番号>.vmdk 」という一連のファイルができあがってるはずだ。この <ディスク名>-<6桁の番号>.vmdk 側はやはりテキストファイルで VMDK の設定情報が書かれている。こちらをみると、こんな記述があるはずだ
CID=5daf4124
parentCID=c07bdeb7
isNativeSnapshot="no"
createType="twoGbMaxExtentSparse"
parentFileNameHint="OPENSTEP-000003.vmdk"
この parentFileNameHint が親ディスクイメージの名前と場所を記録している、という訳だ。

なお、1ディスクイメージにまとめた場合は、VMDK ファイルの先頭にこのテキストと同等の情報が記載された後、実際のディスクの内容となるバイナリデータが連結された形となる。読んで読めなくはないが、以下に述べるような編集操作が難しいのが難点。

分割ファイルが 2GB ずつなのは、そもそもこの形式 FATファイルシステムで作られたため。Windows 95 までで主に使われてきた FAT16 形式では、ファイルサイズの最大は 2GB であったため、2GB ずつ分解するのが適切だったのだ。
(FAT32 の導入は Windows98 より正確には Windows95 OSR2から )

VMware Fusion でこの2GBごと分割形式がデフォルトなのは、TimeMachine との相性からだと思われる。TimeMachine はファイル単位で更新されたファイルをバックアップするため、1ファイル形式だと VMDKの中の1バイトがかわっただけでも全てのディスクイメージがバックアップされてしまう。2GB ごとなら変更されたブロックを含むエクステントファイルだけがバックアップされるので、少しは効率が良くなる。


● リンククローンをでっち上げる

さて、説明は長いが、やることは実はそう難しくない(笑)

まず、仮想マシンを作成し OSをインストール、必要なところまでシステムを構成する。
なおこのとき 「2GB ファイルに分割」は入れておくことをお勧めする。理由は述べたとおり、後で VMDK に細工するときに single 形式は面倒だからだ。

十分できあがったところで仮想マシンをシャットダウンする。

仮想マシンが止まったところで、Finder で .vmwarevm フォルダの下をみて、どういう名前で VMDK が作られているか確認しておこう。

確認できたらスナップショットを取る。

スナップショットは恐らく、「<ディスク名>-<6桁の番号>.vmdk 」と「<ディスク名>-<6桁の番号>-s<番号>.vmdk 」という一連のファイルができあがってるはずだ。

安全のためにここで仮想マシンを VMware Fusion の仮想マシンのリストから削除しておいた方がいい。なお、削除する際は「ファイルの保持」を選択、仮想マシンの構成ファイルが削除されないように気をつける。


次にリンク先の仮想マシンを作成する。名前は適当でいい。
そして仮想マシンの設定からハートディスクを選択、詳細オプションを広げて「ハードディスクの削除」を行う。

これで稼働ハードディスクのない仮想マシンができあがる。ここでまだ「設定」パネルは閉じない。

さて、ターミナルを起動、ウィンドウを1つ開く。コピー元の仮想マシンのフォルダにあるスナップショットでできた一連の VMDK ファイルを、リンク先の仮想マシンの .vmwarevm  フォルダに全部コピーする。

そして、適当なエディタで 「<ディスク名>-<6桁の番号>-s<番号>.vmdk 」ファイルを開き、
parentFileNameHint="VMname.vmdk"
parentFileNameHint="../VMname/VMname.vmdk"
という感じでパスに書き換える。相対パスでも絶対パスでもいいのだが、Fusion の場合 ホームの下の書類(Documents)の下の「仮想マシン」(VirtualMachine.localized) フォルダにまとめて置いておくことが多いので、例のように相対パスの方が楽、だろう。

そして仮想マシンの設定の右上にある「デバイスを追加...」ボタンを押し、「既存のハードディスク」を選択、上記でコピーした VMDK を選択する。

これでリンククローン(もどき)は完了。あとは仮想マシンの電源を入れれば起動する、はずだ。


なお、この方式で Windows のリンククローンを作った場合、SIDは全て同一になる。

SIDが同じでも普通は困らない。ActiveDirectory 環境下でも困らないことはマイクロソフト自身が明言している。

しかし、一部のアプリケーションなどで SID が一意であることを前提に作られたものがある。そういった場合は SIDが重複していると問題が起こるかも知れない。
(実は、VMware View の ViewManager や vCenterServer などでこの問題が発生する。)

問題が起こった場合は、リンククローンされた仮想マシンで sysprep を実行、SIDを再生成させればいい。