Dockerについて色々記事を書いてきましたが、裏側の動作については「上手いことやってくれてるんでしょ状態」だったので、ちゃんと調べてみることにしました。 この記事では、ファイルシステムまわりについてまとめています。
イメージは差分の集まり
Dockerのイメージは複数のレイヤが重なって構成されています。
レイヤとは、docker run
でコンテナを立ち上げてからdocker commit
でイメージ化するまでの間に発生した、ファイルの差分のことです。
上の図で例えると、Debianのベースイメージを元に
- emacsを追加した
- Apacheを追加した
という差分が重なることでイメージが構成されています。
差分の管理方法
DockerはAUFS等のファイルシステムを使って、差分イメージを実現しています。 AUFSについてはこちらの解説がわかりやすいです。
AUFSはLinuxのライブCDに使われています。 ライブCDって、ハードディスクに書き込みできないのにファイルを保存できたりしますよね? それは、AUFSが下記の動きをしてくれているためです。
- CD-ROMのOS部分をRead-Onlyとする。また、メモリ上にWritableな領域を確保する。
- ファイルシステムに変更があったら、メモリ上に差分だけを書き込む。
- 結果、Read-Only領域(元の状態)にWriable領域(差分)を重ねるとディスクに書き込んだのと同じ状態になる。
では、先ほどの図に戻ります。
上図のコンテナは、Debian~add ApacheがRead-Onlyな領域、一番上のレイヤがWritableな領域です。
今の状態でdocker commit
すると、一番上のレイヤも含めてRead-Only化されたイメージができあがります。
また、作られたイメージを元にdocker run
すると、新しく一番上にWritableなレイヤが作られる仕組みです。
Writableな領域を見る
実際にコンテナを作って、Writableな領域を見てみます。
まずはUbuntuをベースにコンテナを作成します。
sudo docker run --name difftest -itd ubuntu
Unable to find image 'ubuntu:latest' locally
latest: Pulling from ubuntu
e118faab2e16: Pull complete
7e2c5c55ef2c: Pull complete
e04c66a223c4: Pull complete
fa81ed084842: Already exists
ubuntu:latest: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security.
Digest: sha256:738edd684282277c07f23277718e43562daf2ee210f7aca9a13fae65f0159ddd
Status: Downloaded newer image for ubuntu:latest
a849e9c13f0908c1d1e5e03ed731f7af05a5ae88473704c7e807ca2884ae43f8
コンテナにアタッチして、ファイルを2つ作成します。
sudo docker attach difftest
echo test1 > testfile1
echo test2 > testfile2
デタッチしたら、ホストでdocker diff
を実行します。
docker diff
は、コンテナ上で発生した差分を確認するコマンドです。
Docker command line - Docker Documentation
sudo docker diff difftest
A /testfile1
A /testfile2
先ほど追加したファイルがA(追加されたファイル)として表示されていますね。
次に、再度アタッチしてファイルを1つ消してみます。
sudo docker attach difftest
rm testfile1
もう一度、ホストでdocker diff
を実行します。
sudo docker diff difftest
A /testfile2
消したtestfile1
が表示されなくなりましたね。
元々Read-Only領域にtestfile1
は存在しなかったので、差分としてはカウントされなくなりました。
Read-Onlyな領域を見る
先ほど作成したコンテナをコミットします。
sudo docker commit difftest tanksuzuki/difftest
eb6e3cb9db92c3d50e356b5a878a9f20b1a5443ceef78f4812d61817e025e504
イメージを作成したら、docker images
を--tree
オプション付きで実行します。
--tree
を付けると、各イメージのご先祖様が表示されます。
sudo docker images --tree
Warning: '--tree' is deprecated, it will be removed soon. See usage.
└─e118faab2e16 Virtual Size: 188.1 MB
└─7e2c5c55ef2c Virtual Size: 188.3 MB
└─e04c66a223c4 Virtual Size: 188.3 MB
└─fa81ed084842 Virtual Size: 188.3 MB Tags: ubuntu:latest
└─eb6e3cb9db92 Virtual Size: 188.3 MB Tags: tanksuzuki/difftest:latest
ubuntuベースでdifftestが作られたことがわかりますね。 ただ、これだとちょっと具体性がないのでもう一歩踏み込みます。
docker save
でイメージをtar化して中身を見てみます。
sudo docker save tanksuzuki/difftest > difftest.tar
tar tvf difftest.tar
drwxr-xr-x 0/0 0 2015-06-03 09:13 7e2c5c55ef2cd7675dcc9e9cc012dc2f759ceaf0f36c950b672af6df87af5070/
-rw-r--r-- 0/0 3 2015-06-03 09:13 7e2c5c55ef2cd7675dcc9e9cc012dc2f759ceaf0f36c950b672af6df87af5070/VERSION
-rw-r--r-- 0/0 2580 2015-06-03 09:13 7e2c5c55ef2cd7675dcc9e9cc012dc2f759ceaf0f36c950b672af6df87af5070/json
-rw-r--r-- 0/0 208896 2015-06-03 09:13 7e2c5c55ef2cd7675dcc9e9cc012dc2f759ceaf0f36c950b672af6df87af5070/layer.tar
drwxr-xr-x 0/0 0 2015-06-03 09:13 e04c66a223c45a6247237510c40117cef92acb0a4355f1ba90580ef6274b490d/
-rw-r--r-- 0/0 3 2015-06-03 09:13 e04c66a223c45a6247237510c40117cef92acb0a4355f1ba90580ef6274b490d/VERSION
-rw-r--r-- 0/0 1385 2015-06-03 09:13 e04c66a223c45a6247237510c40117cef92acb0a4355f1ba90580ef6274b490d/json
-rw-r--r-- 0/0 4608 2015-06-03 09:13 e04c66a223c45a6247237510c40117cef92acb0a4355f1ba90580ef6274b490d/layer.tar
drwxr-xr-x 0/0 0 2015-06-03 09:13 e118faab2e16f9d858fcec0d86c9148e9b0fa021697239745f3253f367941dcc/
-rw-r--r-- 0/0 3 2015-06-03 09:13 e118faab2e16f9d858fcec0d86c9148e9b0fa021697239745f3253f367941dcc/VERSION
-rw-r--r-- 0/0 1210 2015-06-03 09:13 e118faab2e16f9d858fcec0d86c9148e9b0fa021697239745f3253f367941dcc/json
-rw-r--r-- 0/0 197185024 2015-06-03 09:13 e118faab2e16f9d858fcec0d86c9148e9b0fa021697239745f3253f367941dcc/layer.tar
drwxr-xr-x 0/0 0 2015-06-03 09:13 eb6e3cb9db92c3d50e356b5a878a9f20b1a5443ceef78f4812d61817e025e504/
-rw-r--r-- 0/0 3 2015-06-03 09:13 eb6e3cb9db92c3d50e356b5a878a9f20b1a5443ceef78f4812d61817e025e504/VERSION
-rw-r--r-- 0/0 1184 2015-06-03 09:13 eb6e3cb9db92c3d50e356b5a878a9f20b1a5443ceef78f4812d61817e025e504/json
-rw-r--r-- 0/0 2048 2015-06-03 09:13 eb6e3cb9db92c3d50e356b5a878a9f20b1a5443ceef78f4812d61817e025e504/layer.tar
drwxr-xr-x 0/0 0 2015-06-03 09:13 fa81ed084842076d1b39b56d084d99ec0011cd4a5ade1056be359486a8b213e4/
-rw-r--r-- 0/0 3 2015-06-03 09:13 fa81ed084842076d1b39b56d084d99ec0011cd4a5ade1056be359486a8b213e4/VERSION
-rw-r--r-- 0/0 1352 2015-06-03 09:13 fa81ed084842076d1b39b56d084d99ec0011cd4a5ade1056be359486a8b213e4/json
-rw-r--r-- 0/0 1024 2015-06-03 09:13 fa81ed084842076d1b39b56d084d99ec0011cd4a5ade1056be359486a8b213e4/layer.tar
-rw-r--r-- 0/0 101 2015-06-03 09:13 repositories
表示されたファイルは、ご先祖様分も含めた全ての差分情報です。 ディレクトリ名の先頭と、イメージのIDを突き合わせてみてください。
どのファイルが変更されたかは、各ディレクトリのlayer.tar
を見るとわかります。
tar xvf difftest.tar -O eb6e3cb9db92c3d50e356b5a878a9f20b1a5443ceef78f4812d61817e025e504/layer.tar | tar tvf -
eb6e3cb9db92c3d50e356b5a878a9f20b1a5443ceef78f4812d61817e025e504/layer.tar
-rw-r--r-- 0/0 6 2015-06-03 08:59 testfile2
差分になっていたtestfile2が確かに記録されていますね。 このように、差分の積み重ねでDockerのイメージは構成されています。
ちなみに、AUFSの場合レイヤは127層が上限です。
上限を超える場合、export
してimport
することでシングルレイヤに圧縮できます。
知らないイメージがある
先ほどdocker images
を実行した際、pullした記憶がないイメージがありました。
ubuntu:latest
よりも上のイメージですね。
sudo docker images --tree
Warning: '--tree' is deprecated, it will be removed soon. See usage.
└─e118faab2e16 Virtual Size: 188.1 MB
└─7e2c5c55ef2c Virtual Size: 188.3 MB
└─e04c66a223c4 Virtual Size: 188.3 MB
└─fa81ed084842 Virtual Size: 188.3 MB Tags: ubuntu:latest
└─eb6e3cb9db92 Virtual Size: 188.3 MB Tags: tanksuzuki/difftest:latest
これらはUbuntuの元になったイメージです。 最初にUbuntuをpullした時のログでも、確かにpullしていることがわかります。
sudo docker run --name difftest -itd ubuntu
Unable to find image 'ubuntu:latest' locally
latest: Pulling from ubuntu
e118faab2e16: Pull complete
7e2c5c55ef2c: Pull complete
e04c66a223c4: Pull complete
fa81ed084842: Already exists
ubuntu:latest: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security.
Digest: sha256:738edd684282277c07f23277718e43562daf2ee210f7aca9a13fae65f0159ddd
Status: Downloaded newer image for ubuntu:latest
a849e9c13f0908c1d1e5e03ed731f7af05a5ae88473704c7e807ca2884ae43f8
一言で言ってしまえば、複数の差分を引っ張ってきて結合する動きをしています。
結合過程で使ったイメージは、docker images -a
でも確認できます。
sudo docker images -a
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
tanksuzuki/difftest latest eb6e3cb9db92 35 minutes ago 188.3 MB
ubuntu latest fa81ed084842 2 days ago 188.3 MB
<none> <none> e04c66a223c4 2 days ago 188.3 MB
<none> <none> 7e2c5c55ef2c 2 days ago 188.3 MB
<none> <none> e118faab2e16 2 days ago 188.1 MB
おまけ:AUFSとストレージドライバ
v0.6以前のDockerは使用できるファイルシステムがAUFS一択でしたが、v0.7にてストレージドライバという機構が採用されました。 ストレージドライバとは、Dockerコンテナが利用するファイルシステムを選択する機構です。
v0.7時点ではaufs, device mapper, vfs, btrfsの4種類から選択可能です。
0.7.0 (2013-11-25)
Notable features since 0.6.0
* Storage drivers: choose from aufs, device mapper, vfs or btrfs.
v1.4でoverlayfsも追加されました。
1.4.0 (2014-12-11)
Notable Features since 1.3.0
* New Overlayfs Storage Driver
なので、現時点(v1.6)では下記の5種類を選択可能です。
- aufs
- devicemapper
- vfs
- btrfs
- overlay
ドライバ選択の仕組み
ストレージドライバは自動でファイルシステムを選択してくれるため、明示的に利用者が選択する必要はありません(指定することもできます)。
v1.6.2では、使用するドライバはgraphdriver/driver.go
のNew
にて下記の条件で決められています。
- 環境変数DOCKER_DRIVERで指定されたもの
- コマンドで指定されたもの
- 優先順位が高いもの かつ 使用可能なもの(順位は後述)
優先順位は同じくgraphdriver/driver.go
に定義されています。
次はZFSに対応?
master
も覗いてみると、zfsが追加されていました。
ここ最近マージされたみたいです。
実際に試してみた
EC2で立ち上げたCentとUbuntuでdocker info
を実行してみました。
CentOSではdevice mapperが選択されます。
sudo docker info
Containers: 0
Images: 0
Storage Driver: devicemapper
Pool Name: docker-202:1-25168351-pool
Pool Blocksize: 65.54 kB
Backing Filesystem: xfs
Data file: /dev/loop0
Metadata file: /dev/loop1
Data Space Used: 307.2 MB
Data Space Total: 107.4 GB
Data Space Available: 7.356 GB
Metadata Space Used: 729.1 kB
Metadata Space Total: 2.147 GB
Metadata Space Available: 2.147 GB
Udev Sync Supported: true
Data loop file: /var/lib/docker/devicemapper/devicemapper/data
Metadata loop file: /var/lib/docker/devicemapper/devicemapper/metadata
Library Version: 1.02.93-RHEL7 (2015-01-28)
Execution Driver: native-0.2
Kernel Version: 3.10.0-123.8.1.el7.x86_64
Operating System: CentOS Linux 7 (Core)
CPUs: 1
Total Memory: 992.8 MiB
Name: ip-172-31-0-18
ID: QLHR:7KVY:MKYW:FGF5:OPUY:PS2S:NQBY:6T2O:YM3E:64EW:2MBP:4IOF
UbuntuではAUFSが使えるようで、下記の結果になります。
sudo docker info
Containers: 0
Images: 0
Storage Driver: aufs
Root Dir: /var/lib/docker/aufs
Backing Filesystem: extfs
Dirs: 0
Dirperm1 Supported: false
Execution Driver: native-0.2
Kernel Version: 3.13.0-48-generic
Operating System: Ubuntu 14.04.2 LTS
CPUs: 1
Total Memory: 992.5 MiB
Name: ip-172-31-3-48
ID: AQLE:L35P:IC2H:5UIQ:5FQW:KMCZ:7UI7:LHYV:XYYH:ZVJ3:U4FV:7UKD
WARNING: No swap limit support