前回はLXCでも利用できるファイルシステムであるoverlayfsを紹介しました。
Linuxではいろいろなファイルシステムが使えますので、コンテナイメージの保存場所にいろいろなファイルシステムを使い、ファイルシステムが持つさまざまな便利な機能を直接使うことがあるでしょう。
LXCでも前回紹介したoverlayfsを始めとするいくつかのストレージバックエンドをサポートしており、コンテナイメージに関わる操作をする場合はLXCのコマンドやライブラリから直接ストレージバックエンド上のコンテナを操作できます。
具体的には以下の操作をする場合にストレージバックエンドを指定して操作できます。
- コンテナの作成 (
lxc-create
)
- コンテナのクローン (
lxc-clone
)
lxc-snapshot
コマンドでコンテナのスナップショットを取る場合も、内部的にはストレージバックエンドを意識しますが、コマンドでの指定はありません。
いずれのコマンドも第8回と第9回で簡単に説明しました。今回以降の数回で、ストレージバックエンドの使用にフォーカスを当ててもう少し細かく見ていきたいと思います。
今回の記事中では実際の操作はUbuntu 14.04 LTS上で実行しています。
ストレージバックエンドの種類
lxc-create
、lxc-clone
共にストレージバックエンドは-B
オプションで指定します。ここで指定できるストレージバックエンドには表1に挙げるような種類があります。
表1 LXCで指定できるストレージバックエンド
名称 |
lxc-create での使用 |
lxc-clone での使用 |
説明 |
aufs |
× |
○ |
|
best |
○ |
× |
btrfs,zfs,lvm,dirの順に試す |
btrfs |
○ |
○ |
|
dir |
○ |
○ |
ディレクトリ |
lvm |
○ |
○ |
LVM |
loop |
○ |
○ |
ループバックデバイス |
none |
○ |
× |
dirのエイリアス |
overlayfs |
× |
○ |
|
zfs |
○ |
○ |
|
lxc-create
、lxc-clone
のそれぞれで「○」が付いているストレージバックエンドしか指定できません。
コピーによるクローン
それではさっそくクローンを試してみましょう。まずはコンテナの作成からです。/var/lib/lxc
が普通にext4である環境でコンテナを作成しましょう。
-B dir
は指定しなくても同じです。ここではわかりやすいように明示的に指定しています。
クローンを実行する前にlxc-clone
のオプションを確認しておきましょう。
表2 lxc-clone
コマンドのオプション
オプション |
オプションの意味 |
-s |
クローンをスナップショットで取得。LVMとbtrfsとzfsの時に指定可能。aufsとoverlayfsの時に指定が必要 |
-p |
オリジナルのコンテナの『コンテナの保存場所』 |
-P |
クローン先のコンテナの『コンテナの保存場所』 |
-B |
元のコンテナと違うバックエンドストレージを使う場合にバックエンドストーレジ形式を指定 |
-o |
クローン元のコンテナ名 |
-n |
クローン先のコンテナ名 |
最低限必要なオプションはクローン元を指定する-o
とクローンで新たに作成するコンテナ名を指定する-n
です。
また、-s
でスナップショットによるクローンを指定しない場合は、コンテナイメージをrsyncでコピーします。
では、一番シンプルなクローンを試してみましょう。time はクローンにかかった時間を計測するためにつけています。
lxc-clone
の終了後にlxc-ls
を実行すると、clone01
コンテナが作成されているのがわかります。
クローン先のコンテナの設定ファイルはlxc-clone
コマンドが自動的に作成します。クローン元のコンテナの設定ファイルを一度内部的に展開した後に必要部分を書き換えて出力しますので、クローン元でlxc.include
を使って共通ファイルをincludeしている場合でも、クローン先の設定ファイルではlxc.include
を使わずに作成されます。
たとえばクローン元のct01
は以下のように非常にシンプルな記述です。
一方、クローン先のclone01
は以下のように全ての設定が書かれており、かなりサイズが大きくなっています。
rsyncによるコピーが行われていますので、クローン元とクローン先のコンテナイメージの容量は以下のようにほぼ同じになっています。
aufs、overlayfsを使ったクローン
この2つのUnion Filesystemは、元のコンテナとの差分のみを記録し、元のコンテナイメージとの重ねあわせてコンテナイメージを作成します。このため、当然元となるコンテナがないといけませんのでクローン時にのみ指定できます。
aufs、overlayfsを使うことでコンテナイメージを差分で管理できるようになりますので、ベースとなるイメージを用意しておいてクローンすることにより、そのベースとなるイメージは変更せずにコンテナを起動して使えます。コンテナの利便性が高まり、利用の可能性が広がりますね。
クローン元のストレージバックエンドがdirの場合はaufsとoverlayfsが、クローン元のストレージバックエンドがaufsもしくはoverlayfsの場合は、元のストレージバックエンドと同じストレージバックエンドだけが指定できます。
aufs、overlayfsでクローンを作成する場合は、スナップショットを取得するための-s
オプションを指定する必要があります。
では、先ほどのct01
コンテナのクローンをoverlayfsでスナップショットとして作成してみましょう。
time
の出力を見ると、一瞬で処理が終了しているのがわかります。先ほどのコピーによるクローンに比べてかなり短い時間を示していますね。
クローンで作成したコンテナ用のディレクトリを見てみましょう。
config
ファイルとrootfs
ディレクトリは通常のコンテナにも存在しますね。目的も同じです。この2つも含めてクローン先のコンテナ用ディレクトリの中身をまとめておきます。
config
- コンテナの設定ファイル
delta0
- overlayfsの上層側ディレクトリ
lxc_rdepends
- このコンテナがどのコンテナに依存しているかを記述したファイル。依存するコンテナの「保存場所(
/var/lib/lxc
)」と「コンテナ名(ct01
)」が書かれています
olwork
- overlayfsの
workdir
オプションで指定するディレクトリ。ここで使っているUbuntu 14.04 LTSの古いバージョンのoverlayfsでは使いません
rootfs
- コンテナイメージのルート("/")。通常のコンテナと違いコンテナが起動していない時は空で、overlayfsで下層側と上層側のディレクトリを重ねあわせてここにマウントします
LXCはクローンで作成したコンテナがoverlayfsを使用していることを、設定ファイル内のlxc.rootfs
の設定で認識します。
コピーによるクローンで作られたコンテナのlxc.rootfs
は、lxc-create
で作られたコンテナと同様にディレクトリを示しています。一方、overlayfsによるスナップショットとして作られたコンテナのlxc.rootfs
はコロンで区切られた設定となっています。
aufs、overlayfsの場合はこのようにファイルシステムと下層側ディレクトリ、上層側ディレクトリをコロンで区切って指定します。
overlayfsの場合の実際の設定を見てみると、下層側のディレクトリとしてクローン元のコンテナイメージ用ディレクトリを、上層側のディレクトリとしては、新たにクローン先の上層側ディレクトリ用に作られdelta0
というディレクトリを指定しているのがわかります。つまりクローン元のコンテナイメージは読み込み専用の下層側として使うわけですね。
クローン直後のコンテナイメージの容量を確認してみましょう。
まだコンテナの起動を行っていないので、非常に少ない容量になっていますね。delta0
ディレクトリの中を見てみると/etc/hostname
のみ存在します。LXCはコンテナ内のホスト名を変更するために/etc/hostname
の名前を変更します(コンテナに/etc/hostname
ファイルが存在する場合のみです)。
コンテナを起動すると、クローン元のコンテナイメージの上にdelta0
ディレクトリを重ねあわせて、rootfs
ディレクトリにマウントします。実際にコンテナを起動して、重ねあわせによるマウントが行われているのを確認してみましょう。
このマウントはコンテナのマウント名前空間内で行われますので、ホスト上では見えません。そこで上記のようにlxc-attach
でコンテナ内に入り込んでマウントの確認をしています。
試しにコンテナ内にファイルを作成してみましょう。
上記のようにroot/testfile
が作成されていますし、その他コンテナ起動後に作成されたログなどのファイルも上層側のディレクトリに作成されているのがわかります。"whiteout"機能もしっかり使われています。上層側ディレクトリの容量も増えていますね。
このようにoverlayfsを使うと素早く、使用する容量も少なくコンテナのクローンが取得できます。overlayfsによるクローンの場合、元のコンテナを下層側に使っており、これまで見たようにコンテナイメージとしては更新されたファイルしかありません。
このような状態でクローン元のコンテナがなくなってしまってはクローンしたコンテナの起動に困りますね。そこでLXCのコマンドやライブラリを使っている限りはこのような問題は起きないような工夫がされています。
クローンを作成すると、クローン元のコンテナ用ディレクトリの下に、自身のクローンが作成されたことを示すファイルが作成されます。
上記のようにlxc_snapshots
というファイルが作成され、いくつスナップショットが取られているかが書かれています。この状態でクローン元のコンテナであるct01
を削除しようとすると、以下のようにエラーとなります。
もちろん、ct01
用のディレクトリやファイルはLXCのコマンドを使わないでrm
コマンドなどで消去できますので、そのような操作をした場合にはクローンしたコンテナは起動しません。注意が必要です。
クローンで作成されたコンテナのクローン
overlayfsによるクローンで作成されたコンテナの更にクローンをoverlayfsで作成するとどうなるでしょうか。さっそくやってみましょう。
overlayfs01
コンテナをクローンしたときと同様に一瞬でクローンが作成されました。できあがったコンテナを先ほどと同様に確認していきましょう。
まずはlxc.rootfs
がどのように設定されているのか確認です。
overlayfs01
コンテナでも下層側ディレクトリに指定されていたct01
のコンテナイメージがそのまま下層側に指定されています。上層側ディレクトリはoverlayfs02
コンテナ用のディレクトリですね。
それではクローン元のoverlayfs01
コンテナで行われた変更はどうやってクローン先のoverlayfs02
コンテナに反映されているのでしょうか? 実は、overlayfsのコンテナからoverlayfsのコンテナへクローンすると、rsyncによって上層側のディレクトリがコピーされます。
つまりクローン時のクローン元のコンテナのファイルがコピーされるだけなので、クローンした後にクローン元(overlayfs01
)の状態が変わっても、それはクローン先(overlayfs02
)には反映されません。
overlayfs01
コンテナとoverlayfs02
コンテナの上層側ディレクトリの使用量を比べてみましょう。
同じだけ使用していますね。使用量だけ見てもわかりませんので、実際にそれぞれのdelta0
ディレクトリのツリーを比較してみましょう。
ちょっとわかりづらいコマンドの実行ですね。上記の例はそれぞれのコンテナのdelta0ディレクトリを引数に指定してtree
コマンドを実行した出力をdiff
で比較しています。tree
コマンドの1行目は引数で指定したディレクトリの部分だけが違っているのがわかります。
存在するファイルやディレクトリはクローン直後は同じであることがわかりますが、前述のようにコンテナの/etc/hostname
の変更はされています。
そして、下層側ディレクトリで指定されているct01
にクローン時にできたlxc_snapshots
ファイルの中身は、依存するコンテナが2つになったので"2"となっています。
ちなみにこのlxc_snapshots
ファイルは、依存するコンテナが全て消去された場合には、中身が"0"となるだけで消去はされません。
aufsによるクローン
ここまでoverlayfsを使ったクローンを紹介してきました。aufsを使う場合も、ここまでの例で"overlayfs"と指定していた部分を"aufs"に置き換えてそのまま実行できます。確認しておきましょう。
overlayfsよりは少し時間がかかっているようですね。でも一瞬でクローンが終わるのには変わりません。コンテナ用ディレクトリの下にできるファイルやディレクトリは、overlayfsでのみ必要なworkdir用ディレクトリ以外は同じですね。lxc.rootfs
に設定される値も"overlayfs"となっていた部分が"aufs"になっているだけです。
overlayfsと同様に、クローン直後はaufsで差分を記録する側のディレクトリであるdelta0
以下には/etc/hostname
が存在しているだけですので使用量はわずかです。
ではコンテナを起動してマウントされている様子を見てみましょう。
マウントされていますね。この情報からはわかりませんが、delta0
ディレクトリを読み書き可能で、元のコンテナのコンテナイメージを読み込み専用と指定してマウントしています。
コンテナの起動によってログなどが増えたため、delta0
ディレクトリの容量が増えていることがわかります。
overlayfsと同様に、aufsを使ったクローンで作成したコンテナを更にaufsでクローンできます。
aufsで差分を保存するためのディレクトリの中身がrsyncによってコピーされ、元のコンテナと重ね合わされるのもoverlayfsの場合と同じです。
一般ユーザによるクローン
Ubuntu 14.04 LTSのカーネルや、Plamo 5.3の3.17.6カーネルにはパッチが当たっており、ユーザ名前空間内で特権があればoverlayfsを使ってコンテナがクローンできます。つまりLXCで一般ユーザ権限がコンテナを起動できるように設定されていれば、overlayfsを使ってクローンを作成できます。
vanilla kernelだと、ユーザ名前空間内の特権ユーザではoverlayfsをマウントできませんので、3.18以降のoverlayfsをサポートするカーネルを使っても、一般ユーザはコンテナをoverlayfsでクローンできません。
同様にユーザ名前空間内の特権ユーザではaufsをマウントできませんので、一般ユーザはaufsを使ったクローンはできません。
overlayfs、aufsを使ったクローンの際の注意点
クローン元のコンテナ
先にも述べたようにoverlayfs、aufsを使うと、ベースとなるコンテナを用意しておき、そこからの差分を重ねあわせすることによりコンテナの差分管理ができますので便利です。
しかし、ベースとなるコンテナ自体が変化すると、重ねあわせた結果も変化してきますので注意が必要です。
ベースとなる側、重ねあわせる側の両方が変化すると、重ねあわせた結果の予測が困難ですので注意しましょう。
overlayfsを使う際のファイルシステム
LXCではコンテナイメージの保存場所に使われているファイルシステムが何であるかに関わらず、ストレージバックエンドとして"dir"を指定してコンテナを作成できます。
たとえば、btrfsである領域に"dir"を指定してコンテナを作れます。このようにして作られたコンテナをクローンする場合でも、ストレージバックエンドとして"aufs"、"overlayfs"が指定できます。しかし、前回説明したように、現時点ではoverlayfsはext4上でないと動きません。
LXCでdirストレージバックエンドを使用している場合に、実際のファイルシステムが何であるのかLXCは認識していませんので、btrfs上にdirストレージバックエンドを指定して作成したコンテナをoverlayfsでクローンしても、LXCではエラーにはなりません。しかし、正常に動作しませんので注意してください。
LXCのoverlayfs対応
前回説明したように、3.18カーネル以降でovarlayfsのファイルシステムタイプが"overlay"となりました。
しかし執筆時点では、LXC 1.0系列はタイプ名の"overlay"への変更に対応できていませんので、最新のLXC 1.0.7では3.18カーネルのoverlayfsは使えません。最新の開発版であるLXC 1.1系列は既に対応が終わっていますので使えます。すでに1.0系列用のブランチでもパッチがマージされていますので、次の1.0.8で対応されるはずです。
なお、"overlay"というタイプ名への変更に対応できていないだけで、workdirオプションが必要になった新しい仕様には対応しています。3.14~3.17カーネルにoverlayfsのパッチを当てて構築したカーネルの場合、つまりタイプ名は"overlayfs"でworkdirオプションが必要という場合は、筆者のパッチで対応していますので、LXC 1.0.7以降であれば使えます。Plamo 5.3のカーネルが当てはまります。
まとめ
今回はoverlayfsとaufsを使ったコンテナのクローンを細かく見ました。
LXCでこれらのファイルシステムを使って、コンテナのストレージバックエンドとしてdirを使う最も一般的なケースで、コンテナの差分管理ができるようになっていることがご理解いただけたのではないでしょうか。
次回以降も、今回紹介できなかったストレージバックエンドを使った場合のLXCの使い方や動きを紹介していきます。
LXC 1.1.0
今回の原稿を書いている最中にLXCの初のメジャーバージョンアップである1.1.0がリリースされました。
LXC 1.1.0の主な新機能は
- CRIUを使ったコンテナのチェックポイント・リストア
- initとしてsystemdを使ったコンテナのサポート
の2点です。
他にも細かい新機能や修正が多数ありますので、詳しくはLXCのNewsページをご覧ください。日本語訳もあります。