LXCで学ぶコンテナ入門 -軽量仮想化環境を実現する技術

第18回Linuxカーネルのコンテナ機能 [7] ─ overlayfs

今回からはLXCがコンテナイメージを置く領域に使えるいろいろなストレージバックエンドを利用し、LXCを便利に使う方法を紹介していきます。

LXCでいろいろなストレージバックエンドを利用する方法を紹介する前に、今後コンテナでの利用が増えそうな、3.18カーネルで新たに追加されたoverlayfsについて紹介しておきましょう。

タイトルは「Linuxカーネルのコンテナ機能」としていますが、これまでの機能と同様にoverlayfsもコンテナ専用として使う機能ではありません。名前空間やCgroup以上にコンテナ以外でさまざまなシーンで使えそうですね。

overlayfsとは

overlayfsはunion filesystemの実装の1つで、ディレクトリを重ね合わせて1つのディレクトリツリーが構成できます。

話題のDockerが持つ特長として、よくコンテナイメージの差分管理ができることが挙げられます。この差分管理を行うための手段のひとつとしてunion filesystemの実装であるaufsが用いられており、Dockerが急激に話題になり始めたころはaufsが良く紹介されていました。

Ubuntuなどの一部のディストリビューションではaufsが使えますが、aufsはカーネルソースに対するパッチという形で提供されているため一部のディストリビューションでしか使えません。一方、overlayfsは3.18カーネルでマージされましたので、今後はどのディストリビューションでも使えるようになっていき、それに伴いコンテナでの利用が増えていくでしょう。

Ubuntuでは3.18でカーネルにマージされる前の12.04 LTSや14.04 LTSの時点でパッチを適用してoverlayfsが使えるようになっていました。他にSUSEやopenSUSEでも以前からoverlayfsが利用できていたようです。

Ubuntuで対応していたためか、LXCでもかなり前からaufsだけでなくoverlayfsにも対応していました。

3.18カーネルでマージされたことを受けて、overlayfsが使える環境が増えてきました。

Dockerでは1.4.0でoverlayfsに対応しました

さらに、Docker関連で良く話題になるCoreOSも最近ファイルシステムとしてbtrfsの代わりにoverlayfsを使用するという提案が行われ、執筆時点でのAlphaリリースである561.0.0でファイルシステムがext4+overlayfsへ変更されているようです。

overlayfsを使ってみる

union filesystemとか「重ね合わせ」と言われてもピンと来ない方もいらっしゃるでしょうから、ここで簡単にoverlayfsの動きを見ておきましょう。

まずはUbuntu 14.04 LTSでの使い方や動きを紹介して、overlayfsがどのようなファイルシステムであるかを理解してみましょう。Ubuntu 14.10でも同様の動きになるはずです。

overlayfsのマウント

まずは必要なディレクトリを作成します。重ねあわせの下層にするディレクトリ、上層にするディレクトリ、重ねあわせてマウントするためのディレクトリを作成します。

$ mkdir lower upper overlay
$ ls -F
lower/  overlay/  upper/

以上のように普通にディレクトリを作成しました。では早速マウントしてみましょう。

overlayfsのマウントにはオプションが必要です。

lowerdir
重ねあわせの下層側のディレクトリ
upperdir
重ねあわせの上層側のディレクトリ
$ sudo mount -t overlayfs -o lowerdir=lower,upperdir=upper overlayfs overlay
$

何事もなくmountコマンドが終了しました。成功したようですね。一応、マウントされているか確認してみましょう。

$ mount -l | grep overlayfs
overlayfs on /home/ubuntu/overlay_test/overlay type overlayfs (rw,lowerdir=lower,upperdir=upper)

確かにマウントされています。では、このマウントしたディレクトリにファイルとディレクトリを作成してみましょう。

$ touch overlay/testfile_overlay
$ mkdir overlay/testdir_overlay
$ ls -F overlay/
testdir_overlay/  testfile_overlay

当たり前ですが普通にディレクトリとファイルができましたね。では、この時のlowerupperはどうなっているか確認してみましょう。

$ ls -F lower/ (下層側ディレクトリの確認)
$ ls -F upper/ (上層側ディレクトリの確認)
testdir_overlay/  testfile_overlay

以上のようにlowerには何もありませんが、upperには先ほど作成したファイルとディレクトリがあります。このように重ねあわせた上層であるディレクトリに変更が加えられていきます。

ここでさらにいろいろ試すために、一旦アンマウントします。

$ sudo umount overlay
$ mount -l | grep overlayfs
$

元から上層と下層にファイルが存在する状態でマウント

それではマウントされていない状態で下層であるlowerと上層であるupperにファイルとディレクトリを置いてみましょう。

$ touch lower/testfile_lower(下層にファイルを作成)
$ mkdir lower/testdir_lower (下層にディレクトリを作成)
$ touch upper/testfile_upper(上層にファイルを作成)
$ mkdir upper/testdir_upper (上層にディレクトリを作成)
$ ls -F lower/ (下層側ディレクトリの確認)
testdir_lower/  testfile_lower
$ ls -F upper/ (上層側ディレクトリの確認)
testdir_overlay/  testdir_upper/  testfile_overlay  testfile_upper

lowerにはファイルとディレクトリがひとつずつ、upperには先ほどoverlayfsをマウントした状態で作ったファイルとディレクトリに加えて、今作ったファイルとディレクトリが存在する状態です。

それでは再度マウントしてみましょう。

$ sudo mount -t overlayfs -o lowerdir=lower,upperdir=upper overlayfs overlay
$ ls -F overlay/ (マウントしたディレクトリの確認)
testdir_lower/    testdir_upper/  testfile_overlay
testdir_overlay/  testfile_lower  testfile_upper

先ほどマウントした状態で作ったファイル、ディレクトリと、マウントしない状態で作ったファイル、ディレクトリが全て見えますね。つまり下層と上層それぞれのディレクトリの内容が重ねあわせて見えるようになるわけです。これがunion filesystemです。

上層側への変更

それでは存在するファイルに変更を加えてみましょう。先ほどマウントした状態では上層にファイルができましたし、上層に存在するファイルに変更を加えた時の動きは容易に想像がつくのではないでしょうか。

$ echo "test" > overlay/testfile_upper (上層側ファイルを変更)
$ cat overlay/testfile_upper (マウントしたディレクトリでファイルを確認)
test
$ cat upper/testfile_upper(上層側ディレクトリでファイルを確認)
test

上層にあるファイルに変更を加えると、そのまま上層にあるファイルが変更されています。上層にあるディレクトリに変更を加えても同様にそのまま上層が変更されます。

$ touch overlay/testdir_upper/testfile (上層側ディレクトリにファイルを作成)
$ ls overlay/testdir_upper/ (マウントしたディレクトリでファイルを確認)
testfile
$ ls -F upper/testdir_upper/ (上層側ディレクトリでファイルを確認)
testfile

上層に存在するディレクトリ内にファイルを作ると、そのまま上層であるディレクトリupper/testdir_upper以下にファイルが作成されました。

下層側への変更

それでは下層側に存在するファイルやディレクトリに変更を加えるとどうなるのでしょうか? 早速変更して下層側を確認してみましょう。

$ echo test > overlay/testfile_lower  (下層側ファイルを変更)
$ touch overlay/testdir_lower/testfile(下層側ディレクトリにファイルを作成)
$ cat lower/testfile_lower (下層側ディレクトリでファイルを確認)
$ ls lower/testdir_lower/  (下層側ディレクトリでディレクトリを確認)
$

下層側に存在するファイルは元通りで変更は加えられていませんし、ディレクトリにファイルは存在しません。上層側を確認してみましょう。

$ ls -F upper/ (上層側ディレクトリの確認)
testdir_lower/    testdir_upper/  testfile_overlay
testdir_overlay/  testfile_lower  testfile_upper
$ cat upper/testfile_lower  (上層側にできたファイルの確認)
test
$ ls -F upper/testdir_lower/(上層側にできたディレクトリの確認)
testfile

先ほどまでは上層には存在しなかったtestdir_lowertestfile_lowerが存在していますね。ファイルの中身は先ほど書き込んだ中身になっていますし、出現したディレクトリ内には先ほど作成したファイルが存在します。

このことから、変更は常にupperdirオプションで指定した上層側ディレクトリ以下になされることがわかります。ディレクトリやファイルが下層にも上層にも存在する場合は上層側のものが見えます。

ファイルやディレクトリの消去

ここまではファイルの作成や変更を見てきました。それではファイルやディレクトリを削除した場合はどうなるでしょう?

上層側に存在するファイルやディレクトリの場合はわかりやすいと思います。上層側に存在するファイルが消去されます。

$ rm overlay/testfile_overlay   (マウントしたディレクトリでファイルを削除)
$ rmdir overlay/testdir_overlay/ (マウントしたディレクトリでディレクトリを削除)
$ ls -F overlay/(マウントしているディレクトリで確認)
testdir_lower/  testdir_upper/  testfile_lower  testfile_upper
$ ls -F upper/ (上層側ディレクトリで確認)
testdir_lower/  testdir_upper/  testfile_lower  testfile_upper

先ほどまで上層側に存在していたtestdir_overlaytestfile_overlayがマウントされたディレクトリでも上層側のディレクトリでも消去されていますね。

では、下層側のファイルやディレクトリを消去した場合はどうなるでしょう?

$ ls -F overlay/
testdir_lower/  testdir_upper/  testfile_lower  testfile_upper
$ rm overlay/testfile_lower(下層側ファイルをマウントしたディレクトリで削除)
$ ls -F overlay/(マウントしたディレクトリで確認)
testdir_lower/  testdir_upper/  testfile_upper
$ ls -F lower/ (下層側ディレクトリで確認)
testdir_lower/  testfile_lower
$ ls -F upper/ (上層側ディレクトリで確認)
testdir_lower/  testdir_upper/  testfile_lower@  testfile_upper

下層にも上層にも存在していたtestfile_lowerファイルを消してみました。すると、下層側にはそのままの状態でファイルが残っているようですが、上層側では先ほどまでファイルとして存在していたtestfile_lowerがシンボリックリンクになっています。もう少し詳しく見てみると、

$ ls -lF upper/testfile_lower
lrwxrwxrwx 1 root root 18 Jan 19 20:29 upper/testfile_lower -> (overlay-whiteout)

ご覧にように上層側ディレクトリ以下に存在したtestfile_lowerは"(overlay-whiteout)"へのシンボリックリンクになっています。つまり消去をした場合は上層側に変更が加えられ、⁠削除した」という印のために"(overlay-whiteout)"へのシンボリックリンクになります。下層に存在するファイルを上層で修正するために"whiteout(白の修正液)"というわけです。

ディレクトリも消去してみましょう。

$ rm -rf overlay/testdir_lower/(下層側に存在するディレクトリをマウントしたディレクトリで削除)
$ ls -F lower/(下層側ディレクトリで確認)
testdir_lower/  testfile_lower
$ ls -lF upper/*_lower(上層側ディレクトリで確認)
lrwxrwxrwx 1 root root 18 Jan 19 20:37 upper/testdir_lower -> (overlay-whiteout)
lrwxrwxrwx 1 root root 18 Jan 19 20:29 upper/testfile_lower -> (overlay-whiteout)

ディレクトリでも同様になりましたね。

ところで、上記の例では表面的には見えていませんが、"(overlay-whiteout)"へのシンボリックリンクが張られた際、同時に拡張ファイル属性"trusted.overlay.whiteout"に"y"がセットされるようです。これも確認しておきましょう。

$ sudo getfattr -h -n "trusted.overlay.whiteout" upper/testdir_lower
# file: upper/testdir_lower
trusted.overlay.whiteout="y"

$ sudo getfattr -h -n "trusted.overlay.whiteout" upper/testfile_lower
# file: upper/testfile_lower
trusted.overlay.whiteout="y"

まとめると、

  • 変更は常にupperdirオプションで指定した上層側ディレクトリ以下になされる
  • lowerdirオプションで指定した下層側に存在するファイルに加えられた変更は上層側になされる。変更対象であるファイルやディレクトリが上層側にない場合は作成される
  • 下層側に存在するファイルやディレクトリを削除すると"(overlay-whiteout)"へのシンボリックリンクが作成され、拡張ファイル属性"trusted.overlay.whiteout"が"y"に設定される

となります。

非常にシンプルでわかりやすい動きですね。

3.18カーネルのoverlayfs

さて、Ubuntu 14.04 LTSを使ってoverlayfsの動きを見てみました。Ubuntu14.04 LTSを使った理由は、現時点でoverlayfsを試す環境として一番お手軽ではないかと考えたからです。

しかし、実はUbuntu 14.04 LTSで使えるoverlayfsと、3.18カーネルで導入されたoverlayfsでは仕様が異なるため、3.18 カーネルで前述の例を実行してもエラーになります。また、下層側に存在するファイルやディレクトリを変更した際の動きも変更されています。

使い方として現れる変更点は以下です。

ファイルシステムのタイプ名が変更された理由はコミットログに書かれています。UbuntuやSUSEでは3.18より前のカーネルでパッチを適用してoverlayfsをサポートしてきました。

タイプ名を変更することにより、互換性のためにこの古い仕様のoverlayfsを維持する一方で、新しい仕様のoverlayfsも使えるようにできます。

また、このように複数の仕様のoverlayfsが存在する場合、プログラムでどちらのバージョンのoverlayfsが実行されているかを判定する必要もでてきます。

タイプ名を変更しておくと、/proc/filesystems内で"overlay"という行が存在するか、"overlayfs"という行が存在するかで判定できるようになります。LXCでも今後この方法でマウントの際のオプションを判定する変更がなされる予定です。

それではUbuntu 14.04 LTSで試したのと同様の操作を3.18カーネルで行って変化を見てみましょう。

ここではPlamo Linux 5.3に3.18.3カーネルをインストールした環境で試しています。

# mkdir lower upper overlay work (workdir用にworkという名前でディレクトリを作成)
# touch lower/testfile_lower upper/testfile_upper (テスト用ファイルの作成)
# mkdir lower/testdir_lower upper/testdir_upper   (テスト用ディレクトリの作成)
# mount -t overlayfs -o lowerdir=lower,upperdir=upper,workdir=work overlay overlay
("overlayfs"というタイプ名でマウント)
mount: unknown filesystem type 'overlayfs' ("overlayfs"というタイプ名だとエラー)
# mount -t overlay -o lowerdir=lower,upperdir=upper,workdir=work overlay overlay
("overlay"というタイプ名でマウント)
# mount -l | grep overlay (マウントの確認)
/root/overlay_test/overlay on /root/overlay_test/overlay type overlay (rw,lowerdir=lower,upperdir=upper,workdir=work)
# ls -F overlay/  (マウントしたディレクトリの確認)
testdir_lower/  testdir_upper/  testfile_lower  testfile_upper

テスト用にファイルやディレクトリを作成したあと、Ubuntu 14.04 LTSと同様に"overlayfs"という名前でマウントしようとすると"unknown filesystemtype"でエラーになりました。タイプ名を"overlay"にすると無事マウントできていますね。

マウントしたディレクトリ内で下層側と上層側それぞれに存在するファイル、ディレクトリがきちんと見えているのがわかります。

それではマウントした状態で、下層側に存在するファイルに変更を加えてみましょう。

# echo "test" > overlay/testfile_lower(下層側ファイルを変更)
# touch overlay/testdir_lower/testfile(下層側ディレクトリにファイルを作成)
# cat lower/testfile_lower(下層側ファイルの確認)
# cat upper/testfile_lower(上層側にできたファイルの確認)
test
# ls lower/testdir_lower/(下層側ディレクトリの確認)
# ls upper/testdir_lower/(上層側にできたディレクトリの確認)
testfile

以上のようにUbuntu 14.04 LTSで実行したときと特に動きに変化はありませんね。

それではいよいよ、マウントした状態で下層側に存在するファイル、ディレクトリを削除してみましょう。

# rm overlay/testfile_lower (下層側ファイルの削除)
# rm -rf overlay/testdir_lower/ (下層側ディレクトリの削除)
# ls -F overlay/
testdir_upper/  testfile_upper (消去された)
# ls -lF upper/*_lower (上層側ディレクトリの確認)
c--------- 1 root root 0, 0  1月 20日  19:26 upper/testdir_lower
c--------- 1 root root 0, 0  1月 20日  19:26 upper/testfile_lower

Ubuntu 14.04 LTSで実行したときは特別なシンボリックリンクだったのが、以上のようにノード番号0,0であるデバイスファイルになっているのがわかります。

拡張ファイル属性も見ておきましょう。

# getfattr -n trusted.overlay.whiteout upper/testdir_lower 
upper/testdir_lower: trusted.overlay.whiteout: No such attribute
# getfattr -n trusted.overlay.whiteout upper/testdir_lower 
upper/testdir_lower: trusted.overlay.whiteout: No such attribute

拡張属性はついていません。

opaque(不透明)ディレクトリ

overlayfsにはopaqueディレクトリという機能があります。これはどのバージョンのoverlayfsでも使えるはずです。

通常のoverlayfsでは上層側と下層側が透過的に重ねあわせられますが、上層側で「不透明」の指定がなされているディレクトリの内部では、上層側に存在するオブジェクトだけが見えて、下層側に存在するオブジェクトは見えません。

簡単に見てみましょう。ここでは3.18.3カーネルを入れたPlamo 5.3上で試しています。

# mkdir lower upper overlay work
# mkdir lower/opaquetest upper/opaquetest  (上層、下層側ディレクトリにディレクトリを作成)
# touch lower/opaquetest/testfile_lower upper/opaquetest/testfile_upper
(作成したディレクトリにファイルを作成)

以上のようにopaquetestというディレクトリを上層側と下層側のディレクトリに作成します。その中にそれぞれファイルを作成します。

このままovarlayディレクトリにマウントすると、overlay/opaquetestの下には2つのファイルが見えるはずです。

# mount -t overlay -o lowerdir=lower,upperdir=upper,workdir=work overlay overlay
# ls overlay/opaquetest/  (マウントしたディレクトリの確認)
testfile_lower  testfile_upper
# umount overlay

期待通り2つファイルが存在しますね。確認したら一度アンマウントします。

そして、上層側のディレクトリに拡張ファイル属性"trusted.overlay.opaque"として"y"を設定します。

# setfattr -n "trusted.overlay.opaque" -v "y" upper/opaquetest/
# getfattr -n "trusted.overlay.opaque"  upper/opaquetest/
# file: upper/opaquetest/
trusted.overlay.opaque="y"

#

拡張ファイル属性が設定されているのを確認してマウントします。

# mount -t overlay -o lowerdir=lower,upperdir=upper,workdir=work overlay overlay
# ls overlay/opaquetest/
testfile_upper (上層側に存在するファイルしか見えない)

先ほどと違って、上層側であるディレクトリに存在するファイルしか見えなくなっていますね。このように下層側に存在する同名のディレクトリを無視する機能がこの「opaqueディレクトリ」機能です。

overlayfsを使う上での注意

現時点でoverlayfsを使う場合には注意すべき点があります。

3.18カーネルの時点では、overlayfsを使う場合のベースとなるファイルシステムはext4しか使えません。これは先に紹介した"whiteout"機能[1]がext4にしか実装されていないためです。

以下はほとんど当てはまる人はいないと思いますが、3.14~3.17のカーネルでoverlayfsのリポジトリから取得したソースを使って独自にoverlayfsを使えるカーネルを構築して使ってる場合は、先に紹介した/proc/filesystemsを使ったoverlayfsの仕様の判定ができませんので注意してください。この間のoverlayfsはタイプ名が"overlayfs"である一方、仕様は新しいためです。Plamo 5.3の現時点のカーネルは3.17.6でoverlayfsを使えるようにしているのでこれに当てはまります。

overlayfsの将来

overlayfsは今後も仕様が変わっていくようです。

overlayfsのリポジトリにある"overlayfs-next"というブランチを見ていると、下層側のディレクトリを複数重ねあわせる変更が行われています。

筆者の手元で3.18.3カーネルにこのoverlayfs-nextブランチのパッチを適用して簡単に試してみました。

下層用にディレクトリを3つ作成し、上層用とwork用にディレクトリを1つずつ作成しました。lower1~3のディレクトリにはそれぞれファイルを1つ置いています。

# ls -F
lower1/  lower2/  lower3/  overlay/  upper/  work/
# ls -F lower?/*
lower1/testfile_lower1  lower2/testfile_lower2  lower3/testfile_lower3

ここでlowerdirオプションで3つのディレクトリを":"で連結してマウントしてみると、以下のようにoverlayディレクトリ以下にはtestfile_lower1testfile_lower2testfile_lower3という3つのファイルが存在するように見えます。

# mount -t overlay -o lowerdir=lower1:lower2:lower3,upperdir=upper,workdir=work overlay overlay
# mount -l | grep overlay (マウントの確認)
/root/overlay/overlay on /root/overlay/overlay type overlay (rw,lowerdir=lower1:lower2:lower3,upperdir=upper,workdir=work)
# ls -F overlay (マウントしたディレクトリの確認)
testfile_lower1  testfile_lower2  testfile_lower3

lowerdirのみを指定してupperdirを指定しないでマウントできるようにもなるようで、この場合は読み込み専用でマウントされます。

この変更がいつの時点でカーネルにマージされるのかはわかりません。将来マージされて複数の下層ディレクトリが使えるようになるとコンテナの差分管理がさらに便利になりそうですね[2]⁠。

まとめ

今回は、3.18カーネルで新たに追加され、今後コンテナでの利用が広がりそうなoverlayfsについて紹介しました。

コンテナ以外でも面白い使い方ができそうなファイルシステムです。皆さんも面白い使い方を考えてみてはいかがでしょうか?

次回以降はoverlayfsを含め、さまざまなストレージバックエンドを使ったLXCの便利な使い方について説明していきます。

おすすめ記事

記事・ニュース一覧