Dockerコンテナとイメージを簡易的に理解する

Dockerは便利なので開発には毎度使用していますが、インフラ系の人間ではないのでDockerの仮想化技術の仕組みを殆ど理解していなかったので社内勉強会を期に調べてみてなんとなくわかった気がしたのでまとめておきます。

# 調べる前までの私の理解度

* コンテナで手早くlinux環境を自由に作成できる
* 同じ環境を作ることが出来る
* コンテナはプロセスで実行されている？
* Dockerの説明の際に必ずと言ってよい程出てくる例の構造画像
* 例の構造画像のおかげでVMと比べて軽いらしい
* コンテナのCPUやメモリはよくわからないけどDockerエンジンがよしなにやってくれているのだろうか？

# Dockerコンテナの主な技術

* cgroups（プロセスをグループ化してリソース(CPU、メモリ、ディスクI/Oなど)の利用をコントロール(制限・隔離)するLinuxカーネルの機能）
* namespace（ホスト名やPID空間などのカーネル/OSが扱うリソースをコントロール(制限・隔離)するLinuxカーネルの機能）
* chroot (現在のプロセスとその子プロセス群に対してルートディレクトリを変更する操作である。ルートディレクトリを別のディレクトリに変更されたプロセスは、その範囲外のファイルにはアクセスできなくなる)

上記を使用してコンテナを実現している。
RHEL7のDockerでは、systemdと連携してcgroupsの制御を行っているらしい。

# cgroups系のコンテナ技術

* LXC
* systemd-nspawn(systemdには軽量コンテナを建てる機能がなぜか備わっている。)
* Docker

# イメージとコンテナには実体がある様子

ホストOS　Archlinux
ファイルシステムがbtrfsの場合

`/var/lib/docker/btrfs/subvolume/<ハッシュ値のフォルダ>`
この中に実体が存在していた。


ホストOS　DockerToolBox
ファイルシステムがaufsの場合

`/var/lib/docker/aufs/diff/<ハッシュ値のフォルダ>`
この中に実体が存在していた。

ベースイメージっぽいハッシュ値のフォルダの中身

```
# ls -l
total 68
-rw-r--r--    1 root     root         11958 Nov 28 16:08 anaconda-post.log
lrwxrwxrwx    1 root     root             7 Nov 28 16:07 bin -> usr/bin
drwxr-xr-x    2 root     root          4096 Nov 28 16:07 dev
drwxr-xr-x   47 root     root          4096 Nov 28 16:08 etc
drwxr-xr-x    2 root     root          4096 Nov  5  2016 home
lrwxrwxrwx    1 root     root             7 Nov 28 16:07 lib -> usr/lib
lrwxrwxrwx    1 root     root             9 Nov 28 16:07 lib64 -> usr/lib64
drwxr-xr-x    2 root     root          4096 Nov  5  2016 media
drwxr-xr-x    2 root     root          4096 Nov  5  2016 mnt
drwxr-xr-x    2 root     root          4096 Nov  5  2016 opt
drwxr-xr-x    2 root     root          4096 Nov 28 16:07 proc
dr-xr-x---    2 root     root          4096 Nov 28 16:08 root
drwxr-xr-x   10 root     root          4096 Nov 28 16:08 run
lrwxrwxrwx    1 root     root             8 Nov 28 16:07 sbin -> usr/sbin
drwxr-xr-x    2 root     root          4096 Nov  5  2016 srv
drwxr-xr-x    2 root     root          4096 Nov 28 16:07 sys
drwxrwxrwt    7 root     root          4096 Nov 28 16:08 tmp
drwxr-xr-x   13 root     root          4096 Nov 28 16:07 usr
drwxr-xr-x   18 root     root          4096 Nov 28 16:07 var
```

**Linuxの`/`が再現されていた。**

ホストによって`/var/lib/docker/`配下の構造が異なるのはDockerエンジンがディストリビューションの標準ストレージ・ドライバを使ってよしなにやってくれているから 。
Dockerをインストールする時、システム上の設定に応じてデフォルトのストレージ・ドライバを選択します。

## イメージとコンテナの実体

**AUFSストレージ・ドライバ時の動作例**

http://docs.docker.jp/engine/userguide/storagedriver/aufs-driver.html

イメージ・レイヤと各コンテナは、`/var/lib/docker/aufs/diff/<イメージID>または<コンテナ ID>`ディレクトリ以下に保管されます。

Docker 1.10 以降では、イメージ・レイヤ ID コンテナ ID はディレクトリ名と一致しません。

### イメージ

１つのイメージが複数のレイヤをもつので１つのイメージに対して複数のディレクトリを持つ。
docker commit で新しいイメージを作る度にディレクトリが増える様子でした。
コミット時にイメージ・レイヤ（差分）が上層に積み上げられていく感じのようです。

**イメージ・レイヤを統合することでイメージを再現している。**


### コンテナ

コンテナを作成すると以下の2つができた。

```
/var/lib/docker/aufs/diff/75e8cf47a9daf6b7a52d78704e408f102fe2006fea2a8218c65434af4a42bbc7-init

/var/lib/docker/aufs/diff/75e8cf47a9daf6b7a52d78704e408f102fe2006fea2a8218c65434af4a42bbc7
```

`-init`の方にはコンテナ作成時のhost設定等が入っていた。
もう一つの方にはコンテナに入って修正したもの等が入る。
2つのディレクトリは共にベースイメージの様にLinuxの`/`が再現されている様子はなかった。
（多分run時の時に作成されたイメージのスナップショット（runでの変更差分）がこの２つのディレクトリといった感じになるのでしょう）
１つのコンテナは2つのディレクトリを持つようです。

この`-init`の無い方のディレクトリは AUFS によってコンテナの最上位の書き込みレイヤとして積み重ねられるものであり、コンテナに対する全ての変更が保管されます。コンテナが停止しても、このディレクトリは存在し続けます。つまり、コンテナを再起動しても、その変更内容は失われません。コンテナが削除された時のみ、このディレクトリは削除されます。

コンテナ内でファイルを変更するとコピーオンライトでコピーされ差分のみ管理している様子でした。

### 実体のまとめ

**コンテナの実体はイメージ・レイヤ＋コンテナ差分で出来ており、ストレージ・ドライバによって異なるが`/var/lib/docker/`配下に存在している**

# Docker内のプロセスはホストOS上で動作させているだけ。

ホストOS上でtopコマンドやhtopコマンド等でプロセスを確認するとDockerのプロセスから派生したコンテナ内で起動しているサービスがリストに出てきていた。

### 補足

Linuxのプロセスはツリー構造になっており、Centos7等のSystemdでは/sbin/initが全てのプロセスの親になっている。親プロセスが止まると子プロセスも全て止まるそうです。

htopコマンドがプロセスをツリー表示で確認できるのですごく便利でした。
https://linuxfan.info/htop#F5

# 例の構造画像は実際にはこんな感じ

* 例のイメージ

![docker1.png](https://qiita-image-store.s3.amazonaws.com/0/156195/3a6aae50-17c9-27a5-54e6-6afc9e9c2f82.png)

* 実際には以下のようなイメージ

![docker2.png](https://qiita-image-store.s3.amazonaws.com/0/156195/dcfa9bf8-e636-266d-1b91-576e26df3d21.png)

# なんで軽くなるのか

レイヤー層が増える毎にCPU負荷が高くなる。
OSやミドルウェアを搭載しない方が負荷が無く結果的に早くなる。


# カーネル部分はホスト側のものに依存する

上記の構造画像のようにLinuxカーネルはホスト側のものと共用なので以下のようなことも起きた。
https://qiita.com/horikeso/items/5f89cce339e1fc198007

# Docker runコマンドのオプションでのリソース設定

オプションでコア数の指定やメモリの設定ができ、更に同時に複数コンテナを起動した場合のCPU使用の優先度を設定することも出来るそうです。

## デフォルト設定

### CPUコア数の指定

```
--cpuset.cpus
```

デフォルト設定は使用可能なすべてのCPUコアが含まれます。
cgroupがアクセスできるCPUコアのリストを指定します。たとえば、0,1,5-8という設定では、コア0、1、5、6、7および8にアクセスできます。


### CPU優先度

```
-c,--cpu-shares
```

デフォルト設定は1024。
比率なので他のコンテナもデフォルト値で作成すると1:1の優先度となります。

### メモリ

```
-m,--memory
```

デフォルト設定は使用可能な部分全て。

# 感想

参考にさせていたページほど詳しく理解してはいないですが自分なりにある程度整理できたので良かったです。
詳しい事はインフラの人に任せよう…

# 参考

cgroups　namespace　CPU　メモリ等
https://qiita.com/wellflat/items/7d62f2a63e9fcddb31cc
https://qiita.com/Gin/items/450c664ef51f38c0a520
http://enakai00.hatenablog.com/entry/20140427/1398576165

ファイルシステム
http://docs.docker.jp/engine/userguide/storagedriver/selectadriver.html#stability

コンテナのライフサイクル
http://enakai00.hatenablog.com/entry/20140628/1403933390