【学習基盤としてのDocker】Docker何するものぞ　ファイルシステム編

≪ 前回 [【学習基盤としてのDocker】Dockerでとっかかろう](http://qiita.com/fireowl11/items/b96a4c2308a6a2c72d0d)

# Docker自体についても知っておきたいこと

このシリーズでは「Dockerはこうやって学習の役に立つよ」という事を重点的に書いていきたいのであまりDocker自体の仕組みや哲学には触れませんが、それでも使う以上は知っておかないと困る事、既知未知とで歴然とした差が出る事についてはやはり押さえておきたいです。

## そもそもDockerとは何か

Dockerとはざっくり言うと「Linuxの[^1]コンテナ技術を使う為の便利ツールを組み合わせた物」です。コンテナとは…上辺だけ使う分には「すっごい軽い仮想マシン」と思っておけば大体OK[^2]。実際には別物ですが、同じ様に使える事が目標のひとつの様な物ですし。そして”便利ツール”の方の最たるものが”ユニオンファイルシステム”と"Docker Registory"（≒公式のRegistoryである"Docker Hub"）です。特にdockerを軽くでも運用していく上で避けて通れないのがユニオンファイルシステムなので、これだけは掘り下げましょう。

[^1]: Windowsもコンテナ技術に対応したOS(Windows Server 2016)を近々リリース予定。Windows上で独立したWindows環境を持つコンテナを動作させることができるようになります~~がだからどうした~~。

[^2]: 「コンテナ＝仮想マシン」で全く同じだと思っているとsystemdとかsystemctlで往生するのですが、まあぶちあたってから”systemd docker”とかでぐぐればいいです。

## ユニオンファイルシステム

前回さっとjenkinsのDockerイメージをrunしていましたが、その時のコンテナの状態を`docker ps -as`で見てみると以下のようになります。

```bash:
docker@boot2docker:~$ docker run -d jenkins
9a0711d8727aae885ee9f90d96e9ab87e42198048838de8f45c1ea3be9d3cf0d
docker@boot2docker:~$ docker ps -as
CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS                 NAMES               SIZE
9a0711d8727a        jenkins             "/usr/local/bin/jenk   8 seconds ago       Up 5 seconds        8080/tcp, 50000/tcp   reverent_tesla      1.795 MB (virtual 881.8 MB)
docker@boot2docker:~$
```

注目はSIZEの`1.795 MB (virtual 881.8 MB)`の部分。Linuxの仮想マシン相当の物を動かしているのだから900MB近くストレージの容量を消費するのは不思議ではありませんが、では`1.795 MB`とは？実はこれがコンテナが実際に消費している容量なんです。

まず、この時使用しているjenkinsというイメージは下図（かなり端折ってますが）のように成り立っています。

![jenkins.png](https://qiita-image-store.s3.amazonaws.com/0/42843/95d9767c-cf0c-93e9-c683-30f7cfb3dec1.png)
（カーネルはひとまず置いておいて）Debianというディストリビューションをインストールしたイメージ、そこにjavaやgit等のプログラムをインストールしたイメージ、更にそこにjenkins.warというファイルをコピーしたイメージ、という様にストレージの状態**の差分**をレイヤにして重ね合わせた物が"jenkins"というイメージになっています。これがユニオンファイルシステムです。
このjenkinsイメージを使用して`docker run jenkins`として起動されたコンテナからは、これらを重ね合わせた結果のファイルシステムが見えています。そして動作中のコンテナの中でファイルに変更が加わると、jenkinsイメージに対して**ではなく**「jenkinsイメージに対して加えた変更」が別の場所に記録されていきます。これが上述の`1.795 MB`の正体です。具体的な実体は、jenkins.war起動時に作られたテンポラリファイルやログなんかでしょうね。

## ユニオンファイルシステム　ハンズオン

それでは実際に差分が記録されていくところを細かく確認してみましょう。busyboxというほぼ空っぽのDockerイメージを使ってコンテナを起動し、コンテナ上のファイルに変更を加え、その時点のコンテナを元に更にイメージを作成して～という手順を追っていきます。

まずbusyboxイメージを指定してrunし、起動したコンテナのサイズを確認します。busyboxイメージのサイズは2.433MB。小さいですね。起動直後のコンテナでは何もファイルを変更していないのでコンテナの消費量は0byteです。　※コンテナをバックグラウンドで起動しっぱなしにするために`-d tail -f /dev/null`しています。余り気にしないでください…

```bash
docker@boot2docker:~$ docker run -d busybox tail -f /dev/null
226a6c9b79c479d2d56e0f23103db682a5a73fa9c96603cd0eec59f4f175aae7
docker@boot2docker:~$ docker ps -as
CONTAINER ID        IMAGE               COMMAND               CREATED             STATUS              PORTS               NAMES               SIZE
226a6c9b79c4        busybox             "tail -f /dev/null"   5 seconds ago       Up 5 seconds                            dreamy_cray         0 B (virtual 2.433 MB)
```

次にコンテナの中に適当にファイルを作成します。`docker exec`は「コンテナの内部でこのコマンドを実行せよ」というdockerコマンドで、ここではddコマンドを使って256kbyteの"dmy"という名前のファイルを作成しています。

```busy
docker@boot2docker:~$ docker exec 226a6c9b79c4 dd if=/dev/zero of=dmy bs=256k count=1
1+0 records in
1+0 records out
docker@boot2docker:~$ docker exec 226a6c9b79c4 ls -l /
total 304
drwxrwxr-x    2 root     root          4096 May 22  2014 bin
drwxr-xr-x    5 root     root           360 Jul 22 15:36 dev
-rw-r--r--    1 root     root        262144 Jul 22 15:39 dmy
drwxr-xr-x    6 root     root          4096 Jul 22 15:36 etc
drwxrwxr-x    4 root     root          4096 May 22  2014 home
drwxrwxr-x    2 root     root          4096 May 22  2014 lib
lrwxrwxrwx    1 root     root             3 May 22  2014 lib64 -> lib
lrwxrwxrwx    1 root     root            11 May 22  2014 linuxrc -> bin/busybox
drwxrwxr-x    2 root     root          4096 Feb 27  2014 media
drwxrwxr-x    2 root     root          4096 Feb 27  2014 mnt
drwxrwxr-x    2 root     root          4096 Feb 27  2014 opt
dr-xr-xr-x  127 root     root             0 Jul 22 15:36 proc
drwx------    2 root     root          4096 Feb 27  2014 root
lrwxrwxrwx    1 root     root             3 Feb 27  2014 run -> tmp
drwxr-xr-x    2 root     root          4096 May 22  2014 sbin
dr-xr-xr-x   13 root     root             0 Jul 22 15:36 sys
drwxrwxrwt    3 root     root          4096 May 22  2014 tmp
drwxrwxr-x    6 root     root          4096 May 22  2014 usr
drwxrwxr-x    4 root     root          4096 May 22  2014 var
docker@boot2docker:~$
docker@boot2docker:~$ docker ps -as
CONTAINER ID        IMAGE               COMMAND               CREATED             STATUS              PORTS               NAMES               SIZE
226a6c9b79c4        busybox             "tail -f /dev/null"   2 minutes ago       Up 2 minutes                            dreamy_cray         262.1 kB (virtual 2.695 MB)
```

256kbyteぴったりではありませんが、近いサイズのファイルが作成され、その分SIZEが増加したことが確認できました。さて、ここでコンテナに発生した差分の実体がどのように記録されているのかについても見てみましょう。boot2dockerでは`/mnt/sda1/var/lib/docker/aufs/diff/` がdockerの使用するユニオンファイルシステムの差分置き場になっています。パスの最後のやたら長いディレクトリ名はdocker runした時に表示された長い文字列＝コンテナIDのフル桁です。が先頭の数文字を入力してTabキーを押せば補完されるので、自分で打ち込む必要はありません。

```bash
docker@boot2docker:~$ ll $(find /mnt/sda1/var/lib/docker/aufs/diff/226a6c9b79c479d2d56e0f23103db682a5a73fa9c96603cd0eec59f4f175aae7 -type f)
find: /mnt/sda1/var/lib/docker/aufs/diff/226a6c9b79c479d2d56e0f23103db682a5a73fa9c96603cd0eec59f4f175aae7/.wh..wh.orph: Permission denied
find: /mnt/sda1/var/lib/docker/aufs/diff/226a6c9b79c479d2d56e0f23103db682a5a73fa9c96603cd0eec59f4f175aae7/.wh..wh.plnk: Permission denied
-r--r--r--    1 root     root             0 Jul 22 15:36 /mnt/sda1/var/lib/docker/aufs/diff/226a6c9b79c479d2d56e0f23103db682a5a73fa9c96603cd0eec59f4f175aae7/.wh..wh.aufs
-rw-r--r--    1 root     root        262144 Jul 22 15:39 /mnt/sda1/var/lib/docker/aufs/diff/226a6c9b79c479d2d56e0f23103db682a5a73fa9c96603cd0eec59f4f175aae7/dmy
docker@boot2docker:~$
```

直下にdmyという262kbyteのファイルがぽつんと存在しているのが分かります。これが「コンテナID 226a6c9b79c4… に対して発生した差分」というわけです。

続いてこのコンテナの状態をイメージにします。
`docker commit`というコマンドを使用してイメージにしたいコンテナID（or コンテナ名）と作成するイメージの名前を指定します。今回はbusyplusという名前にしました。
`docker images`は現在ローカルにあるイメージを一覧するコマンドで、
`docker history`はそのイメージの来歴を表示するコマンドです。

```bash
docker@boot2docker:~$ docker commit 226a6c9b79c4 busyplus
f19122d2d430b0c5f26086b8f066a8b9109e007e3bc1e73577099e35b9117378
docker@boot2docker:~$ docker images
REPOSITORY           TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
busyplus             latest              f19122d2d430        19 seconds ago      2.695 MB
busybox              latest              8c2e06607696        3 months ago        2.433 MB
docker@boot2docker:~$ docker history busyplus
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
f19122d2d430        About an hour ago   tail -f /dev/null                               262.1 kB
8c2e06607696        3 months ago        /bin/sh -c #(nop) CMD ["/bin/sh"]               0 B
6ce2e90b0bc7        3 months ago        /bin/sh -c #(nop) ADD file:8cf517d90fe79547c4   2.433 MB
cf2616975b4a        3 months ago        /bin/sh -c #(nop) MAINTAINER Jérôme Petazzo     0 B
```

busyboxより262kbほど大きいbusyplusというイメージがたった今作成されたことが分かります。
さて、イメージが出来たという事は、このイメージを利用して`docker run`することができるという事です。早速今までの手順をなぞってみましょう。

```bash
docker@boot2docker:~$ docker run -d busyplus tail -f /dev/null
d2cf37cdb03516ec3cef129c43f9da66f6a5a53aac93313c0457cd5b32ad8d28
docker@boot2docker:~$ docker ps -as
CONTAINER ID        IMAGE               COMMAND               CREATED             STATUS              PORTS               NAMES               SIZE
d2cf37cdb035        busyplus            "tail -f /dev/null"   14 seconds ago      Up 13 seconds                           desperate_hypatia   0 B (virtual 2.695 MB)
226a6c9b79c4        busybox             "tail -f /dev/null"   49 minutes ago      Up 49 minutes                           dreamy_cray         262.1 kB (virtual 2.695 MB)
docker@boot2docker:~$ docker exec d2cf37cdb035 dd if=/dev/zero of=/var/dmy2 bs=512k count=1
1+0 records in
1+0 records out
docker@boot2docker:~$ docker ps -as
CONTAINER ID        IMAGE               COMMAND               CREATED             STATUS              PORTS               NAMES               SIZE
d2cf37cdb035        busyplus            "tail -f /dev/null"   5 minutes ago       Up 5 minutes                            desperate_hypatia   524.3 kB (virtual 3.22 MB)
226a6c9b79c4        busybox             "tail -f /dev/null"   53 minutes ago      Up 53 minutes                           dreamy_cray         262.1 kB (virtual 2.695 MB)
docker@boot2docker:~$ docker exec d2cf37cdb035 ls -l /dmy /var/dmy2
-rw-r--r--    1 root     root        262144 Jul 22 15:39 /dmy
-rw-r--r--    1 root     root        524288 Jul 22 16:26 /var/dmy2
docker@boot2docker:~$ ll $(find /mnt/sda1/var/lib/docker/aufs/diff/d2cf37cdb03516ec3cef129c43f9da66f6a5a53aac93313c0457cd5b32ad8d28 -type f)
find: /mnt/sda1/var/lib/docker/aufs/diff/d2cf37cdb03516ec3cef129c43f9da66f6a5a53aac93313c0457cd5b32ad8d28/.wh..wh.orph: Permission denied
find: /mnt/sda1/var/lib/docker/aufs/diff/d2cf37cdb03516ec3cef129c43f9da66f6a5a53aac93313c0457cd5b32ad8d28/.wh..wh.plnk: Permission denied
-r--r--r--    1 root     root             0 Jul 22 16:25 /mnt/sda1/var/lib/docker/aufs/diff/d2cf37cdb03516ec3cef129c43f9da66f6a5a53aac93313c0457cd5b32ad8d28/.wh..wh.aufs
-rw-r--r--    1 root     root        524288 Jul 22 16:26 /mnt/sda1/var/lib/docker/aufs/diff/d2cf37cdb03516ec3cef129c43f9da66f6a5a53aac93313c0457cd5b32ad8d28/var/dmy2
```

ただ今作成したbusyplusイメージでdocker runするとd2cf37cdb035~というコンテナが作成され、また512kの/var/dmy2というファイルを作成するとd2cf37cdb035~用の差分ディレクトリにそれが作成されていることが確認できました。
"dmy"というファイルはd2cf37cdb035の差分ディレクトリに見当たりませんが、コンテナ上でlsすると"dmy"と"/var/dmy2"の両方存在しているように見えます。コンテナから見ると、dmyが存在するbusyplusイメージf19122d2d430b0~の差分ディレクトリと/var/dmy2が存在するコンテナd2cf37cdb035~の差分ディレクトリが重ね合わせられているからです。コンテナ内で起こった変更は全てそのコンテナに閉じて管理され、busyplusのイメージのデータ若しくは更にそのベースとなったイメージのデータは、それらが最初に作成された時から不変です。

ちなみに、busyplusの元になったコンテナ226a6c9b79c4で今から更に変更を加えるとどうなるか？busyplusイメージのデータが変わってしまうのでは？勘のいい人ならお気づきでしょうが、`docker run busybox`したコンテナとcommitによって出来たイメージとではIDが違います。コンテナはコンテナ、イメージはイメージで差分管理が分断されています。

## 次回

前置きはこのくらいにしてさっさと本題にいきましょう。Docker Hub上のイメージ、まずは[language stacks](https://blog.docker.com/2014/09/docker-hub-official-repos-announcing-language-stacks/)のイメージ群を使った例を示しながら、dockerの活用法（とdockerコマンドの実用例等）に入っていきます。

今回「ファイルシステム編」と銘打ったものの、その他にDockerが利用している技術やエコシステム（Docker Registoryとは何ぞや とかDocker（boot2docker）に適応したネットワーキングとは とかその辺）の説明は今後実用例の中に紛れ込ませていこうと思っています。Dockerについて詳しくなるのが主題では無いですし、あんまりドッカードッカー言ってても飽きますしね。