OpenVZ on Debian: ploop を試す
最新版の OpenVZ カーネルには ploop という機能が同梱されており、これを利用することでコンテナの I/O 性能と管理の利便性を大きく向上させることができます。この記事では Debian の OpenVZ システムで実際に ploop のコンテナを使ってみた感想などを書いてみます。
検証は KVM 上で動作する Debian 7.3 システムと最新の OpenVZ カーネル・管理ツールで行いました。セットアップ手順については 過去の記事 を参照してください。
root@kvz02:~# uname -a Linux kvz02 2.6.32-openvz-042stab084.17-amd64 #1 SMP Fri Dec 27 17:00:12 MSK 2013 x86_64 GNU/Linux root@kvz02:~# vzctl --version vzctl version 4.6.1
基本
ploop は OpenVZ プロジェクトによって開発されたループバックブロックデバイスのドライバです。機能的にはカーネル標準の loop ドライバと似ていますが、 qcow2 や vmdk に似たサイズ可変のイメージフォーマットを使用しており、差分イメージの作成も可能となっているなど、今日の仮想化技術に必要とされる機能を備えています。
ploop をサポートする OpenVZ コンテナ管理ツール vzctl (3.1 以降) ではコンテナレイアウトとして従来の simfs に加えて ploop が選択できます。 ploop レイアウトではコンテナごとに専用のブロックデバイスとファイルシステムを作成するため、ひとつのファイルシステムを多数のコンテナで共有する simfs に比べ、パフォーマンスの向上が期待できます。 OpenVZ Wiki によればひとつのファイルシステムのジャーナルを多数のコンテナが共有することは OpenVZ コンテナの I/O ボトルネックのひとつであったとしています。
ploop レイアウトを使うには vzctl create
コマンドに --layout ploop
オプションをつけてコンテナを作成します。 --diskspace 8G
のように ploop デバイスのサイズの指定もできます。このサイズはあとから増減することができます。
また vzctl convert
コマンドを使うと simfs と ploop で相互にレイアウトを変換できます。ただしこれは対象のコンテナが停止中である必要があります。また変換には長い時間がかかりますので、不測の事態に備えて変換前にバックアップをとっておくべきでしょう。
実際に ploop レイアウトでコンテナを作成し、起動してみましょう。
root@kvz02:~# vzctl create 1100 --ostemplate ubuntu-13.10-x86_64 --layout ploop --diskspace 8G Creating image: /srv/vz/private/1100.tmp/root.hdd/root.hdd size=8388608K Creating delta /srv/vz/private/1100.tmp/root.hdd/root.hdd bs=2048 size=16777216 sectors v2 Storing /srv/vz/private/1100.tmp/root.hdd/DiskDescriptor.xml Opening delta /srv/vz/private/1100.tmp/root.hdd/root.hdd Adding delta dev=/dev/ploop26988 img=/srv/vz/private/1100.tmp/root.hdd/root.hdd (rw) mke2fs 1.42.5 (29-Jul-2012) Discarding device blocks: done Filesystem label= OS type: Linux Block size=4096 (log=2) Fragment size=4096 (log=2) Stride=0 blocks, Stripe width=0 blocks 524288 inodes, 2096635 blocks 104831 blocks (5.00%) reserved for the super user First data block=0 Maximum filesystem blocks=4294967296 64 block groups 32768 blocks per group, 32768 fragments per group 8192 inodes per group Superblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632 Allocating group tables: done Writing inode tables: done Creating journal (32768 blocks): done Writing superblocks and filesystem accounting information: done tune2fs 1.42.5 (29-Jul-2012) Setting maximal mount count to -1 Setting interval between checks to 0 seconds Creating balloon file .balloon-c3a5ae3d-ce7f-43c4-a1ea-c61e2b4504e8 Error in reread_part (ploop.c:1106): BLKRRPART /dev/ploop26988: Device or resource busy Mounting /dev/ploop26988p1 at /srv/vz/private/1100.tmp/root.hdd/root.hdd.mnt fstype=ext4 data='' Unmounting device /dev/ploop26988 Opening delta /srv/vz/private/1100.tmp/root.hdd/root.hdd Adding delta dev=/dev/ploop26988 img=/srv/vz/private/1100.tmp/root.hdd/root.hdd (rw) Mounting /dev/ploop26988p1 at /srv/vz/root/1100 fstype=ext4 data='balloon_ino=12,' Creating container private area (ubuntu-13.10-x86_64) Unmounting file system at /srv/vz/root/1100 Unmounting device /dev/ploop26988 Opening delta /srv/vz/private/1100/root.hdd/root.hdd Adding delta dev=/dev/ploop26988 img=/srv/vz/private/1100/root.hdd/root.hdd (rw) Mounting /dev/ploop26988p1 at /srv/vz/root/1100 fstype=ext4 data='balloon_ino=12,' Performing postcreate actions Unmounting file system at /srv/vz/root/1100 Unmounting device /dev/ploop26988 CT configuration saved to /etc/vz/conf/1100.conf Container private area was created root@kvz02:~# vzctl set 1100 --save --name vm100 --hostname vm100.t0.keshi.org --ipadd 10.10.0.100 --nameserver 10.10.0.1 Name vm100 assigned CT configuration saved to /etc/vz/conf/1100.conf root@kvz02:~# vzctl start 1100 Starting container... Opening delta /srv/vz/private/1100/root.hdd/root.hdd Adding delta dev=/dev/ploop26988 img=/srv/vz/private/1100/root.hdd/root.hdd (rw) /dev/ploop26988p1: clean, 28689/524288 files, 186494/2096635 blocks Mounting /dev/ploop26988p1 at /srv/vz/root/1100 fstype=ext4 data='balloon_ino=12,' Container is mounted Adding IP address(es): 10.10.0.100 Setting CPU units: 1000 Container start in progress...
コンテナ内部で df
を実行してサイズを調べてみます。
root@kvz02:~# vzctl exec 1100 df -h / Filesystem Size Used Avail Use% Mounted on /dev/ploop26988p1 7.9G 600M 6.9G 8% /
この結果によれば 7.9GB のファイルシステムに 600MB の中身があることがわかります。これに対して実際に ploop イメージファイルが占めるサイズを調べると次のようになりました。
root@kvz02:~# ls -lh /srv/vz/private/1100/root.hdd/ total 622M -rw-r--r-- 1 root root 790 1月 6 07:41 DiskDescriptor.xml -rw------- 1 root root 0 1月 6 07:41 DiskDescriptor.xml.lck -rw------- 1 root root 494M 1月 6 07:42 root.hdd root@kvz02:~# du -h /srv/vz/private/1100/root.hdd/root.hdd 622M /srv/vz/private/1100/root.hdd/root.hdd
イメージファイル root.hdd
は du
で得たファイルサイズが ls
で得たファイルサイズよりも大きいという気味の悪い状態になっていますが (逆はよくある)、 622MB とほぼ使ったぶんだけのサイズであることがわかります。
ploop レイアウト
ploop レイアウトでは、コンテナのプライベート領域は次のような構成になります。
/srv/vz/private/CTID/
($VE_PRIVATE
) … コンテナプライベート領域Snapshots.xml
… スナップショット情報dump/
… チェックポイント保存領域{UUID}
… チェックポイントファイル{UUID}.ve.conf
… コンテナ設定ファイル
root.hdd/
… ディスクイメージ保存領域DiskDescriptor.xml
… ディスクイメージ情報root.hdd
… 最初のディスクイメージroot.hdd.{UUID}
… 差分ディスクイメージ
イメージファイル /srv/vz/private/CTID/root.hdd/root.hdd
を ploop デバイス /dev/ploop????
に割り当てた上で、その中に GPT のパーティションテーブルと ext4 ファイルシステムを作り、コンテナのマウントポイント /srv/vz/root/CTID/
にマウントしています。
ファイルシステムのサイズ変更
ploop で特筆すべきは、 ext4 ファイルシステム限定ではありますがオンライン (マウントしたまま・コンテナ稼働したまま) でのファイルシステムサイズ変更をサポートしていることです。これにより simfs の vzquota と同様の使い勝手でコンテナのストレージ使用量を制限できます。
コンテナのディスクサイズの変更は simfs の場合と同様に vzctl set
オプションの --diskspace
を使って簡単にできます。なお --diskinodes
の指定は無視されます。
先ほどのコンテナはディスクサイズに 8GB で作成しました。これを 16GB に拡大してみましょう。
root@kvz02:~# vzctl set 1100 --save --diskspace 16G Storing /srv/vz/private/1100/root.hdd/DiskDescriptor.xml.tmp Growing dev=/dev/ploop26988 size=16777216 sectors (new size=33554432) Storing GPT resize2fs 1.42.5 (29-Jul-2012) Filesystem at /dev/ploop26988p1 is mounted on /srv/vz/root/1100; on-line resizing required old_desc_blocks = 1, new_desc_blocks = 1 The filesystem on /dev/ploop26988p1 is now 4193280 blocks long. UB limits were set successfully CT configuration saved to /etc/vz/conf/1100.conf root@kvz02:~# vzctl exec 1100 df -h / Filesystem Size Used Avail Use% Mounted on /dev/ploop26988p1 16G 608M 15G 4% /
df
の結果、たしかにサイズが 16GB に拡張していることがわかります。ここには記していませんがイメージファイルのサイズもほとんど増加していません。
次は 1GB に縮小してみましょう。
root@kvz02:~# vzctl set 1100 --save --diskspace 1G dumpe2fs 1.42.5 (29-Jul-2012) Changing balloon size old_size=0 new_size=16106127360 Opening delta /srv/vz/private/1100/root.hdd/root.hdd Successfully inflated balloon from 0 to 16106127360 bytes TRUNCATED: 1 cluster-blocks (1048576 bytes) tune2fs 1.42.5 (29-Jul-2012) Setting reserved blocks count to 13055 UB limits were set successfully CT configuration saved to /etc/vz/conf/1100.conf root@kvz02:~# vzctl exec 1100 df -h / Filesystem Size Used Avail Use% Mounted on /dev/ploop26988p1 763M 608M 104M 86% /
こんどはサイズが 1GB よりかなり少ない 763MB になっているのが気になります。この原因はいまのところはっきりと理解していないのですが、巨大なバルーンファイル (後述) のオーバーヘッドによるものではないかと推測しています。
ploop ballooning
ploop がサポートする ext4 ファイルシステムは、それ自身にオンラインでサイズ拡大できる能力がありますがサイズ縮小はできません。 ploop ではこの問題を解決するために ballooning というテクニックを使っています。詳細は README を参照してください。
具体的には、ファイルシステム内に特殊なバルーンファイルというものを作り、このサイズを要求に応じて増減させています。その際、イメージファイル内で末尾にある有効なデータブロックをバルーンファイルが占めるデータブロックに移動させることで、イメージファイルサイズを減らそうとします。
ballooning のために OpenVZ カーネルでは ext4 のコードにもかなり手が入っています。具体的には、マウント時に balloon_ino
オプションで指定した inode 番号のファイル (これがバルーンファイルとなります) は一切見えなくなり、 statfs で取得できるファイルシステムのサイズも、バルーンファイルのサイズが差し引かれるようになっています。
コンテナ領域を balloon_ino
オプションを変更して再マウントすると、実際にバルーンファイルを見ることができます。
root@kvz02:~# mount /dev/ploop26988p1 /srv/vz/root/1100 -o remount,balloon_ino=0 root@kvz02:~# vzctl exec 1100 ls -la / total 15728728 drwxr-xr-x 22 root root 4096 Dec 18 14:38 . drwxr-xr-x 22 root root 4096 Dec 18 14:38 .. -rw------- 1 root root 16106127360 Jan 5 17:41 .balloon-c3a5ae3d-ce7f-43c4-a1ea-c61e2b4504e8 drwxr-xr-x 2 root root 4096 Dec 18 14:39 bin drwxr-xr-x 2 root root 4096 Dec 18 14:37 boot drwxr-xr-x 5 root root 700 Jan 5 17:42 dev drwxr-xr-x 85 root root 4096 Jan 5 17:42 etc -rw-r--r-- 1 root root 0 Dec 18 14:37 fastboot drwxr-xr-x 2 root root 4096 Dec 18 14:37 home drwxr-xr-x 12 root root 4096 Jan 5 17:42 lib drwxr-xr-x 2 root root 4096 Dec 18 14:37 lib64 drwx------ 2 root root 16384 Dec 18 14:37 lost+found drwxr-xr-x 2 root root 4096 Dec 18 14:37 media drwxr-xr-x 2 root root 4096 Dec 18 14:37 mnt drwxr-xr-x 2 root root 4096 Dec 18 14:37 opt dr-xr-xr-x 51 root root 0 Jan 5 17:42 proc drwx------ 2 root root 4096 Dec 18 14:37 root drwxr-xr-x 14 root root 500 Jan 5 17:42 run drwxr-xr-x 2 root root 4096 Dec 18 14:38 sbin drwxr-xr-x 2 root root 4096 Dec 18 14:37 srv drwxr-xr-x 7 root root 0 Jan 5 17:42 sys drwxrwxrwt 2 root root 4096 Jan 5 17:42 tmp drwxr-xr-x 10 root root 4096 Dec 18 14:38 usr drwxr-xr-x 12 root root 4096 Dec 18 14:38 var root@kvz02:~# vzctl exec 1100 df -h / Filesystem Size Used Avail Use% Mounted on /dev/ploop26988p1 16G 16G 104M 100% /
.balloon-c3a5ae3d-ce7f-43c4-a1ea-c61e2b4504e8
というのがバルーンファイルです。ファイルシステムを 16GB に拡大した後に 1GB に縮小したので、バルーンファイルのサイズはちょうど 15GB になっています (15×1024×1024×1024 = 16106127360)。また df
の結果でも本来のファイルシステムサイズ 16GB が現れています。
スナップショット
ploop で利用可能になった便利な機能としてスナップショット機能があります。
次のコマンドでコンテナのスナップショットが作成されます。コンテナが稼働中であればその時点のプロセスの状態も同時にすべて保存されます。
vzctl snapshot CTID
作成したスナップショットは次のコマンドで一覧表示できます。横に長いのでパイプで less -S
などにつなげて見るのがよいでしょう。各スナップショットには UUID が割り当てられており、復元や削除の操作ではこれで対象のスナップショットを指定します。
vzctl snapshot-list CTID | less -S
次のコマンドでコンテナの状態を指定した UUID のスナップショットに復元します。それまでのファイルシステムの変更やプロセスの状態はすべて失われますので注意してください。失いたくない場合は別のスナップショットを作ってから復元します。
vzctl snapshot-switch CTID --id UUID
次のコマンドででスナップショットを削除します。このとき子スナップショットまたは最新状態との間でブロックデバイスのマージ作業が発生します。
vzctl snapshot-delete CTID --id UUID
特定のスナップショットの中身を参照したいときには次のコマンドでマウント・アンマウントできます。ただしマウントはリードオンリーとなります。
vzctl snapshot-mount CTID --id UUID --target DIR vzctl snapshot-umount CTID --id UUID
実際にスナップショットを作成してみましょう。
root@kvz02:~# vzctl snapshot 1100 Creating snapshot {0469f5a1-79cc-4e0b-9fd1-33e9314d9283} Storing /srv/vz/private/1100/Snapshots.xml.tmp Setting up checkpoint... suspend... get context... Checkpointing completed successfully Storing /srv/vz/private/1100/root.hdd/DiskDescriptor.xml.tmp Creating delta /srv/vz/private/1100/root.hdd/root.hdd.{4d30eb8d-162f-4190-8027-ff8fcdb1f52f} bs=2048 size=33554432 sectors v2 Creating snapshot dev=/dev/ploop26988 img=/srv/vz/private/1100/root.hdd/root.hdd.{4d30eb8d-162f-4190-8027-ff8fcdb1f52f} ploop snapshot {0469f5a1-79cc-4e0b-9fd1-33e9314d9283} has been successfully created Setting up checkpoint... join context.. dump... Checkpointing completed successfully Resuming...
稼働中のコンテナのプロセス状態をチェックポイント機能で保存するとともに、新しい差分ディスクイメージを追加しています。
同様にスナップショットを 3 個作成したあとに vzctl snapshot-list
で見ると次のようになります。
root@kvz02:~# vzctl snapshot 1100 root@kvz02:~# vzctl snapshot 1100 root@kvz02:~# vzctl snapshot-list 1100 PARENT_UUID C UUID DATE NAME {0469f5a1-79cc-4e0b-9fd1-33e9314d9283} 2014-01-06 07:48:01 {0469f5a1-79cc-4e0b-9fd1-33e9314d9283} {9b8d68f5-30b2-4099-8510-388703d170d1} 2014-01-06 07:48:02 {9b8d68f5-30b2-4099-8510-388703d170d1} * {d3709ccf-5a52-4424-8c56-5bf507fee9f6} 2014-01-06 07:48:07
このように各スナップショットは UUID で親子関係とともに記録されています。 C
が *
になっているのが、現在マウント・変更しているスナップショットです。
2 番めに保存したスナップショットに切り替えてみます。
root@kvz02:~# vzctl snapshot-switch 1100 --id 9b8d68f5-30b2-4099-8510-388703d170d1 Switching to snapshot {9b8d68f5-30b2-4099-8510-388703d170d1} Storing /srv/vz/private/1100/Snapshots.xml.tmp Setting up checkpoint... suspend... get context... Checkpointing completed successfully Opening delta /srv/vz/private/1100/root.hdd/root.hdd.{4d30eb8d-162f-4190-8027-ff8fcdb1f52f} Storing /srv/vz/private/1100/root.hdd/DiskDescriptor.xml.tmp Creating delta /srv/vz/private/1100/root.hdd/root.hdd.{599805d1-1f73-41cd-9a2b-0b9ccab0b2fa} bs=2048 size=33554432 sectors v2 ploop snapshot has been successfully switched Killing... Unmounting file system at /srv/vz/root/1100 Unmounting device /dev/ploop26988 Container is unmounted Restoring container ... Opening delta /srv/vz/private/1100/root.hdd/root.hdd Adding delta dev=/dev/ploop26988 img=/srv/vz/private/1100/root.hdd/root.hdd (ro) Adding delta dev=/dev/ploop26988 img=/srv/vz/private/1100/root.hdd/root.hdd.{4d30eb8d-162f-4190-8027-ff8fcdb1f52f} (ro) Adding delta dev=/dev/ploop26988 img=/srv/vz/private/1100/root.hdd/root.hdd.{599805d1-1f73-41cd-9a2b-0b9ccab0b2fa} (rw) /dev/ploop26988p1: clean, 28727/1048576 files, 4153637/4193280 blocks Mounting /dev/ploop26988p1 at /srv/vz/root/1100 fstype=ext4 data='balloon_ino=12,' Container is mounted undump... Adding IP address(es): 10.10.0.100 Setting CPU units: 1000 resume... Container start in progress... Restoring completed successfully Storing /srv/vz/private/1100/root.hdd/DiskDescriptor.xml Removing /srv/vz/private/1100/root.hdd/root.hdd.{ac43e723-cab7-44fb-8daa-dc7a73fda889} ploop snapshot {c80a009b-81f5-4c74-88b9-a86d3b493bbb} has been successfully deleted Container has been successfully switched to another snapshot
コンテナ内部ではスナップショット作成時に存在したプロセスがすべて稼働しています。
概念的には VirtualBox のスナップショット機能とほぼ同じです。バックアップやライブマイグレーションなどいろいろ便利な応用が考えられます。詳細は OpenVZ Wiki の Backup などを参照してください。
まとめ
OpenVZ の独自機能である ploop とスナップショットについて紹介してみました。
ploop は可変サイズや差分のディスクイメージに対応したループバックブロックデバイスですが、それだけでなく独自に ext4 ファイルシステムのオンライン拡大・縮小をサポートするなど、従来の simfs でのコンテナの使い勝手を損なうことがないようだいぶ頑張っている印象です (執念に近いものすら感じます)。
スナップショット機能もチェックポイント機能と組み合わせてプロセス状態の保存・復元もサポートするなど、コンテナ仮想化でありながら完全仮想化にも比肩する機能を備えています。
上記機能はみな vzctl コマンドひとつで簡単に使えるよう整備されており、やはり先行するだけあって OpenVZ が LXC に比べ使い勝手で一日の長があるように感じられます。
スナップショット機能の実装は今後各コンテナ技術の優劣を語る上でキーポイントになりそうです。 ploop や device mapper でブロックデバイスレベルの差分を扱う他にも、 btrfs や ZFS のようなホストファイルシステムのスナップショット機能を活用する方法、あるいは docker のように aufs を使うといったアプローチもあるでしょう。これからどのようなアイディアが出てくるか、楽しみな分野です。
今回は時間の都合で使ってみるだけで終わってしまいましたが、今後 simfs と ploop の性能比較などもしてみたいと思います。
2014/01/06 10:01:00 JST