Dockerのまとめ - コンテナとイメージ編

## はじめに

1年くらい前にDockerに入門した初心者です。いくつか自分でDockerfileを書いてみて、ある程度知識が得られてきたので、ここらでちょっとまとめてみます。
数回に分けて公開するつもりです。

## dockerの主な概念

- レイヤ
- イメージ
- コンテナ

今日はこのあたりの話をコマンドとともに解説します。

## この記事を読むと

- docker image周りの構造がわかります。
- データ領域を別途ボリュームに指定する理由がわかります。

## git と docker の類似性

docker には git と似たコマンドがあります。

- git commit / docker commit
- git tag / docker tag
- git pull / docker pull
- git push / docker push
- git log / docker history

これらはどれもレイヤを操作するコマンドです。

## これらのレイヤを操作するコマンドの意味は・・

- commit : コンテナで操作中のレイヤをread-onlyなレイヤとしてimage化
- tag : 特定のレイヤにタグ（別名）をつける
- pull : 特定のイメージを手元に持ってくる
- push : 特定のタグをサーバーに送信する
- history : 特定のイメージのレイヤを操作コマンドとともに表示する

## 例: docker pull alpine:latest

docker pull は指定した image を手元に持ってくるコマンドです。

```
 $ docker pull alpine:latest
latest: Pulling from library/alpine
ff3a5c916c92: Already exists
Digest: sha256:e1871801d30885a610511c867de0d6baca7ed4e6a2573d506bbec7fd3b03873f
Status: Downloaded newer image for alpine:latest
```

手元に alpine のイメージを持ってきました。

### docker history alpine:latest

docker history は image の履歴（レイヤ）を見るコマンドです。

```
 $ docker history alpine:latest
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
3fd9065eaf02        5 months ago        /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>           5 months ago        /bin/sh -c #(nop) ADD file:093f0723fa46f6cdb…   4.15MB
```

小さい小さいと言われる alpine ですが 4.15MB しかないですね。
CREATED BY 列を見るとどういうコマンドで作成されたかわかります。

### レイヤを作成する - docker commit をやってみる(1)

alpineのコンテナを起動し、ファイルを書き込んでみます。

```
 $ docker run --name kompiro_test -it alpine:latest
/ # mkdir -p /home/kompiro
/ # echo 'Hello Docker' > /home/kompiro/hello.txt
/ # exit
```

### レイヤを作成する - docker commit をやってみる(2)

終了しているコンテナから今名前をつけて起動したコンテナを選び、コミットします。

```
 $ docker container ls -a
CONTAINER ID        IMAGE                                COMMAND                  CREATED              STATUS                          PORTS               NAMES
0757c3c1e365        alpine:latest                        "/bin/sh"                About a minute ago   Exited (0) 39 seconds ago                           kompiro_test
2bf0c11c23b1        alpine:latest                        "--name kompiro_exam…"   About a minute ago   Created                                             determined_sammet
 $ docker commit kompiro_test kompiro/alpine:hello_world
sha256:80efa78a627ee1ff861bdba27445b18ff77f996aeec1e6477a581b74be5818f4
```

### レイヤを作成する - docker commit をやってみる(3)

コミットしたイメージを使ってコンテナを起動してみます。

```
 $ docker run -it kompiro/alpine:hello_world
/ # ls /home/kompiro
hello.txt
/ # cat /home/kompiro/hello.txt
Hello Docker
/ #
```

先程コミットしたイメージを元にコンテナが起動できています。

### レイヤとコンテナとイメージ

- イメージは読み込み専用の複数のレイヤを重ねたもの
- コンテナは読み書き可能なレイヤを追加して起動したもの

docker の image は、複数のレイヤを重ねてできたものです。
git の commit もファイルツリーを重ねてできたものです。とても似ていますね。

### レイヤの中身をのぞいてみる(1)

docker のレイヤの中身をのぞいてみましょう。`docker save` コマンドを使うと指定したimageをtar形式で取得できます。

```
 $ docker save kompiro/alpine:hello_world > hello_world.tar
 $ tar -tvf hello_world.tar
drwxr-xr-x  0 0      0           0  6 25 01:04 22446ff33a4b67a4278adcb0d33b0972adb33c7cb209180e547a1cf60568783c/
-rw-r--r--  0 0      0           3  6 25 01:04 22446ff33a4b67a4278adcb0d33b0972adb33c7cb209180e547a1cf60568783c/VERSION
-rw-r--r--  0 0      0        1107  6 25 01:04 22446ff33a4b67a4278adcb0d33b0972adb33c7cb209180e547a1cf60568783c/json
-rw-r--r--  0 0      0        4608  6 25 01:04 22446ff33a4b67a4278adcb0d33b0972adb33c7cb209180e547a1cf60568783c/layer.tar
drwxr-xr-x  0 0      0           0  6 25 01:04 498654318d0999ce36c7b90901ed8bd8cb63d86837cb101ea1ec9bb092f44e59/
-rw-r--r--  0 0      0           3  6 25 01:04 498654318d0999ce36c7b90901ed8bd8cb63d86837cb101ea1ec9bb092f44e59/VERSION
-rw-r--r--  0 0      0         401  6 25 01:04 498654318d0999ce36c7b90901ed8bd8cb63d86837cb101ea1ec9bb092f44e59/json
-rw-r--r--  0 0      0     4403200  6 25 01:04 498654318d0999ce36c7b90901ed8bd8cb63d86837cb101ea1ec9bb092f44e59/layer.tar
-rw-r--r--  0 0      0        1501  6 25 01:04 80efa78a627ee1ff861bdba27445b18ff77f996aeec1e6477a581b74be5818f4.json
-rw-r--r--  0 0      0         292  1  1  1970 manifest.json
-rw-r--r--  0 0      0         102  1  1  1970 repositories
```

いくつか layer.tar と呼ばれるファイルとバージョン情報のjsonが含まれているようですね。この `22446ff` 等で始まるのが sha256 でハッシュ化されたレイヤです。

### レイヤの中身をのぞいてみる(2)

では `tar -xvf` で解凍して、見てみましょう。まず repositories から見てみます。

```
 $ cat repositories
{"kompiro/alpine":{"hello_world":"22446ff33a4b67a4278adcb0d33b0972adb33c7cb209180e547a1cf60568783c"}}
```

### レイヤの中身をのぞいてみる(3) - jsonファイルを開いてみる 

repositories は image の最新のレイヤを指し示しているようです。jsonファイルはなんでしょうか？

```
 $ cat 22446ff33a4b67a4278adcb0d33b0972adb33c7cb209180e547a1cf60568783c/json | jq
{
  "id": "22446ff33a4b67a4278adcb0d33b0972adb33c7cb209180e547a1cf60568783c",
  "parent": "498654318d0999ce36c7b90901ed8bd8cb63d86837cb101ea1ec9bb092f44e59",
  "created": "2018-06-24T16:04:43.795799189Z",
  ...
  "architecture": "amd64",
  "os": "linux"
}
```

レイヤ情報を管理しているファイルのようです。

### レイヤの中身をのぞいてみる(4) - layer.tarファイルを開いてみる 

layer.tar はなんでしょうか。

```
 $ tar -tvf 22446ff33a4b67a4278adcb0d33b0972adb33c7cb209180e547a1cf60568783c/layer.tar
drwxr-xr-x  0 0      0           0  6 25 01:03 home/
drwxr-xr-x  0 0      0           0  6 25 01:03 home/kompiro/
-rw-r--r--  0 0      0          13  6 25 01:03 home/kompiro/hello.txt
drwx------  0 0      0           0  6 25 01:03 root/
-rw-------  0 0      0          74  6 25 01:03 root/.ash_history
```

先程 commit したファイルレイヤで変更があったファイルがそのまま入っています。
docker commit コマンドで「レイヤを作成する」と書きました。docker は レイヤを tar に固めて保持しています。
docker はコンテナを立ち上げる際、イメージを元に起動します。
イメージに固められたtarを順番に解凍して指定されたプロセスを起動します。
もう一方の layer.tar ものぞいてみると面白いです。なぜ busybox と呼ばれるのかがよくわかります。

### FYI: docker commit の --change オプション

docker commit のヘルプを見ていて面白いことに気が付きました。

```
--changeオプションは指定したイメージにDockerfileと同じコマンドで操作ができます。  サポートしているDockerfileのコマンド: CMD|ENTRYPOINT|ENV|EXPOSE|LABEL|ONBUILD|USER|VOLUME|WORKDIR
```

だそうです。 Dockerfile を直接こねこねしないでも良さそうですね。

### docker push と docker tag

`docker push` コマンドは指定したイメージをリポジトリにpushするコマンドです。何も指定しないと公式リポジトリであるdocker hubに push します。プライベートリポジトリに push するには、 `docker tag` コマンドでURLを指定しなければなりません。

```
 $ docker tag example.com/kompiro/alpine:latest
 $ docker push example.com/kompiro/alpine:latest
```

みたいな感じです。

### まとめ

docker はプロセスを確実に起動できるよう、余計なファイル変更を削ぎ落とすアーキテクチャになっています。必要であれば commit するようにして docker image として使えるようにしておきましょう。

### 参考文献

- [Dockerイメージのレイヤー構造について](http://enakai00.hatenablog.com/entry/20140802/1406958412)
- [docker commit](https://docs.docker.com/engine/reference/commandline/commit/)