Docker 奮闘記

# docker 奮闘記 ～コンテナ作成編～
docker の基礎を学びます。

# docker 概要
docker はコンテナを提供するための OSS ソフトウェアである。

コンテナは仮想化といわれるが、その実は chroot し、隔離されただけの単なるプロセス。
通常のプロセス同様にホストのカーネル上で動く。

```
 ホスト  | コンテナ | コンテナ     ユーザ空間
プロセス |          |         
-------------------------------  
          カーネル                 カーネル空間
```

隔離やリソースの制限には、chroot や cgroup といった Linux カーネル標準の技術が使われる。
カーネルや、CPU・メモリといったハードウェア資源のエミュレートは一切行っていない。
隔離されただけのただのプロセスなのである。

## コンテナとは
コンテナはイメージ内の実行ファイルを実行することで生成される。
イメージはファイルシステムやメタ情報の詰め合わせである。
コンテナはメインプロセスとその実行環境から構成される。

```
+------------------------------+                     +------------------------------+
| イメージ                     |                     | コンテナ                     |
+------------------------------+                     +------------------------------+
| イメージID                   |                     | コンテナID                   |
| イメージ名                   |                     | コンテナ名                   |
|                              |                     |                              |
| メタ情報                     |                     | メインプロセス（bash）  -------- コンテナの中核。
|                              |                     |   +-- 子プロセス             |   このプロセスが死ぬとコンテナは停止する。
|                              |                     |   +-- 子プロセス             |
|                              |                     |                              |   docker stop
| filesystem                   |                     | filesystem (/)               | <----------------
|   +-- bin                    |                     |   +-- bin                    |   コンテナ（プロセスも）の停止
|   +-- sbin                   |                     |   +-- sbin                   |
|   +-- usr                    |                     |   +-- usr                    |   docker start
|   |    +-- bin               |     docker run      |   |    +-- bin               | <----------------
|   |    |    +-- bash -----------+----------------> |   |    |    +-- bash         |   コンテナの起動
|   |    |    +-- sleep        |  |   コンテナ生成   |   |    |    +-- sleep        |
|   |    |    +-- etc.         |  |                  |   |    |    +-- etc.         |   docker rm
|   |    +-- sbin              |  |                  |   |    +-- sbin              | <----------------
|   +-- lib                    |  |                  |   +-- lib                    |   コンテナの削除
|   +-- etc                    |  |                  |   +-- etc                    |
|   +-- var                    |  |                  |   +-- var                    |   docker exec
|   +-- etc.                   |  |                  |   +-- etc.                   | <----------------
+------------------------------+  |                  +------------------------------+   コンテナ内の実行ファイルを指定してプロセス起動
                                  |
                                  |   docker run     +------------------------------+
                                  +----------------> | コンテナ                     |
                                  |                  +------------------------------+
                                  |
                                  |   docker run     +------------------------------+
                                  +----------------> | コンテナ                     |
                                  |                  +------------------------------+
                                  |
                                  |   docker run     +------------------------------+
                                  +----------------> | コンテナ                     |
                                  |                  +------------------------------+
                                  :                               :
```

コンテナのメインプロセスは、コンテナを作成した時点で決まる。
後から変更することはできない。

メインプロセスが死ぬか、docker stop で止められるとコンテナは停止するが、一度生成されたコンテナの情報はディスク上に保持される。
docker start で再度メインプロセスを立ち上げ、コンテナを起動することができる。

## OS バージョンが違うコンテナについて
例えばリポジトリから適当にコンテナを落として以下のように構築できた（ように思えた）とする。

```
 ホスト  | CentOS | Debian | RHEL 
プロセス |   6.5  |   9    | 7.2 
-----------------------------------
    RHEL 7.5（Linux 3.10.0-862）
```

実はこれ真っ赤な嘘。
カーネルは共通なので、コンテナの RHEL7.2 で動いているプロセスは RHEL7.2 のカーネル上で動いているかと思いきや実は RHEL7.5 のカーネル上で動いている。

この環境上に RHEL 7.2 のカーネルはどこにも存在しない。

コンテナの CentOS 6.5 やら RHEL 7.2 やらはカーネルバージョンに基づいたものではなく、
ただ単に CentOS 6.5 相当、RHEL 7.2 相当の /bin や /sbin や /usr や /lib が動いていますよ、という意味。

## Linux 以外のコンテナについて
全コンテナで Linux カーネルは共有（という表現が正しいかわからないが）。
つまり、Linux 以外のコンテナは動かない。
以下は実現不可能である。

```
 ホスト  | FreeBSD | Mac | Windows
プロセス |         |     |       
-----------------------------------
    RHEL 7.5（Linux 3.10.0-862）
```

これがしたいならおとなしく KVM を使おう。

## Windows 上での docker について

コンテナは Linux カーネルの機能によって実現されている。
docker は Linux 上でしか動かない。
つまり、docker on Windows だと思えたものは実は docker on Linux Kernel on Windows である。

```
   コンテナ | コンテナ | コンテナ
-----------------------------------
           Linux カーネル
-----------------------------------
             仮想マシン
-----------------------------------
              Windows
```

# docker 実践
## 導入
インスコ

```
# yum install docker -y
```

docker.service プロキシ設定

```
# mkdir /etc/systemd/system/docker.service.d
# echo -e "[Service]\nEnvironment=\"HTTP_PROXY=http://***.***.***.***:8080/\"" | sudo tee /etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=http://***.***.***.***:8080/"
# systemctl daemon-reload
# systemctl restart docker
```

docker スタート

```
# systemctl start docker
```

docker 情報確認。プロキシ設定が反映されていることがわかる。

```
[root@hostname ~]# docker info
Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
Images: 0
Server Version: 1.13.1
  :
Http Proxy: http://***.***.***.***:8080/
  :
```

## ハローワールド
公式リポジトリからイメージをダウンロードし、コンテナを動かしてみる。

リポジトリのイメージを検索

```
[root@hostname ~]# docker search centos
INDEX       NAME                                         DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
docker.io   docker.io/centos                             The official build of CentOS.                   5421      [OK]
docker.io   docker.io/ansible/centos7-ansible            Ansible on Centos7                              121                  [OK]
docker.io   docker.io/jdeathe/centos-ssh                 CentOS-6 6.10 x86_64 / CentOS-7 7.6.1810 x...   110                  [OK]
  :
```

イメージのダウンロード

```
[root@hostname ~]# docker pull centos
Using default tag: latest
Trying to pull repository registry.access.redhat.com/centos ...
Pulling repository registry.access.redhat.com/centos
Trying to pull repository docker.io/library/centos ...
latest: Pulling from docker.io/library/centos
8ba884070f61: Pull complete
Digest: sha256:b5e66c4651870a1ad435cd75922fe2cb943c9e973a9673822d1414824a1d0475
Status: Downloaded newer image for docker.io/centos:latest
```

ローカルにあるイメージ一覧

```
[root@hostname ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
docker.io/centos    latest              9f38484d220f        3 months ago        202 MB
```

コンテナの生成。
イメージ中に存在する /bin/bash をコンテナとして立ち上げる。

```
[root@hostname ~]# docker run -it centos:latest /bin/bash
```

## 動作の確認
当然カーネルはホストと同一のものである。
ホスト名はコンテナごとにホストとは別のホスト名が与えられる。"隔離" のひとつである。

```
[root@73230f43d596 /]# uname -a
Linux 73230f43d596 3.10.0-327.el7.x86_64 #1 SMP Thu Oct 29 17:29:29 EDT 2015 x86_64 x86_64 x86_64 GNU/Linux
```

ネットワークは仮想化されており、eth0 デバイスに 172.17.0.2 が割り与えられている。
コンテナの eth0 はホスト側の docker0 とつながっており、ping 等も疎通する。

```
[root@7666ac79ff57 net]# cat dev
Inter-|   Receive                                                |  Transmit
 face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
  eth0:     648       8    0    0    0     0          0         0      648       8    0    0    0     0       0          0
    lo:       0       0    0    0    0     0          0         0        0       0    0    0    0     0       0          0
    
[root@7666ac79ff57 net]# cat fib_trie
Main:
  +-- 0.0.0.0/0 2 0 2
     |-- 0.0.0.0
        /0 universe UNICAST
     |-- 172.17.0.0
        /16 link UNICAST
Local:
  +-- 0.0.0.0/0 2 0 2
     +-- 127.0.0.0/8 2 0 2
        +-- 127.0.0.0/31 1 0 0
           |-- 127.0.0.0
              /32 link BROADCAST
              /8 host LOCAL
           |-- 127.0.0.1
              /32 host LOCAL
        |-- 127.255.255.255
           /32 link BROADCAST
     +-- 172.17.0.0/16 2 0 2
        +-- 172.17.0.0/30 2 0 2
           |-- 172.17.0.0
              /32 link BROADCAST
           |-- 172.17.0.2
              /32 host LOCAL
        |-- 172.17.255.255
           /32 link BROADCAST
```

マウント状態は全く異なるが、コンテナ内から見える物理ディスクはホストと同じである。
物理ディスクの仮想化は行われておらず、コンテナが chroot されただけのプロセスであることを示す証左のひとつである。

```
[root@73230f43d596 ~]# lsblk
NAME    MAJ:MIN RM    SIZE RO TYPE  MOUNTPOINT
fd0       2:0    1      4K  0 disk
sda       8:0    0     20G  0 disk
|-sda1    8:1    0    500M  0 part
|-sda2    8:2    0   17.5G  0 part  /run/secrets
|-sda3    8:3    0      1G  0 part
`-sda4    8:4    0      1G  0 part
sdb       8:16   0      1G  0 disk
|-sdb1    8:17   0    100M  0 part
`-sdb2    8:18   0    200M  0 part
sdc       8:32   0      1G  0 disk
`-sdc1    8:33   0   1023M  0 part
  `-md0   9:0    0 1022.4M  0 raid1
sdd       8:48   0      2G  0 disk
`-sdd1    8:49   0      1G  0 part
sde       8:64   0      1G  0 disk
`-sde1    8:65   0   1023M  0 part
  `-md0   9:0    0 1022.4M  0 raid1
sr0      11:0    1   1024M  0 rom
```

"/" には overlay マウントされている。
overlay マウントに関しては以下が参考になります。
https://gihyo.jp/admin/serial/01/linux_containers/0018

```
[root@73230f43d596 secrets]# mount
overlay on / type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/UX5WTFVOLK7ZIK7JSLY5RY4NM2:/var/lib/docker/overlay2/l/WIJMYGLDI52JVYHCWVNVZAAO33,upperdir=/var/lib/docker/overlay2/a4e259dee9705431074082e0ce3b010da5a4c24d0394a4b64b281bdaa04c259a/diff,workdir=/var/lib/docker/overlay2/a4e259dee9705431074082e0ce3b010da5a4c24d0394a4b64b281bdaa04c259a/work)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev type tmpfs (rw,nosuid,mode=755)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666)
sysfs on /sys type sysfs (ro,nosuid,nodev,noexec,relatime)
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,relatime,mode=755)
cgroup on /sys/fs/cgroup/systemd type cgroup (ro,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/blkio type cgroup (ro,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/memory type cgroup (ro,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/cpuacct,cpu type cgroup (ro,nosuid,nodev,noexec,relatime,cpuacct,cpu)
cgroup on /sys/fs/cgroup/net_cls type cgroup (ro,nosuid,nodev,noexec,relatime,net_cls)
cgroup on /sys/fs/cgroup/freezer type cgroup (ro,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/devices type cgroup (ro,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/cpuset type cgroup (ro,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/perf_event type cgroup (ro,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (ro,nosuid,nodev,noexec,relatime,hugetlb)
mqueue on /dev/mqueue type mqueue (rw,nosuid,nodev,noexec,relatime)
/dev/sda2 on /etc/resolv.conf type xfs (rw,relatime,attr2,inode64,noquota)
/dev/sda2 on /etc/hostname type xfs (rw,relatime,attr2,inode64,noquota)
/dev/sda2 on /etc/hosts type xfs (rw,relatime,attr2,inode64,noquota)
shm on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,relatime,size=65536k)
/dev/sda2 on /run/secrets type xfs (rw,relatime,attr2,inode64,noquota)
devpts on /dev/console type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000)
proc on /proc/bus type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/fs type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/irq type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/sys type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/sysrq-trigger type proc (ro,nosuid,nodev,noexec,relatime)
tmpfs on /proc/acpi type tmpfs (ro,relatime)
tmpfs on /proc/kcore type tmpfs (rw,nosuid,mode=755)
tmpfs on /proc/timer_list type tmpfs (rw,nosuid,mode=755)
tmpfs on /proc/timer_stats type tmpfs (rw,nosuid,mode=755)
tmpfs on /proc/sched_debug type tmpfs (rw,nosuid,mode=755)
tmpfs on /proc/scsi type tmpfs (ro,relatime)
tmpfs on /sys/firmware type tmpfs (ro,relatime)
```

ホスト側からは docker ps コマンドで稼働中のコンテナを確認できる。

```
[root@hostname ~]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
73230f43d596        centos:latest       "/bin/bash"         3 seconds ago       Up 2 seconds                            ecstatic_brattain
```

exit でコンテナ（bash）を終了すると、docker ps からもあっさり消える。
コンテナが仮想マシンではなく、単なるプロセスであることがよくわかる。

```
[root@73230f43d596 /]# exit
exit

[root@hostname ~]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
```

イメージ中に存在する実行ファイルであれば、何であってもコンテナとして実行可能である。
以下は ps コマンドを実行した例。
自身のプロセスだけしか表示されておらず、コンテナが task_struct 構造体のリンク構造からも隔離されていることがわかる。

```
[root@hostname ~]# docker run -it centos:latest /bin/ps alx
F   UID   PID  PPID PRI  NI    VSZ   RSS WCHAN  STAT TTY        TIME COMMAND
4     0     1     0  20   0  49616  1556 -      Rs+  ?          0:00 /bin/ps alx
```

コンテナは単にプロセスなので、ps コマンドに映る。

```
[root@hostname ~]# docker run -it centos:latest /bin/sleep 1234

[root@hostname ~]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS              PORTS               NAMES
131a35b8006c        centos:latest       "/bin/sleep 1234"   About a minute ago   Up About a minute                       cranky_tesla

[root@hostname ~]# ps alx|grep sleep|grep -v grep
4     0 26912 18475  20   0 221844 12960 ep_pol Sl+  pts/0      0:00 /usr/bin/docker-current run -it centos:latest /bin/sleep 1234
4     0 26956 26939  20   0   4364   360 hrtime Ss+  pts/1      0:00 /bin/sleep 1234
0     0 27076  4818  20   0 107892   620 hrtime S    ?          0:00 sleep 60
```

コンテナは単なるプロセスなので、SIGKILL で死ぬ

```
[root@hostname ~]# kill -KILL 26956
[root@hostname ~]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
```

コンテナのメインプロセスから子プロセスを生成する。
この場合、1コンテナ = nプロセス となる。
コンテナでは PID も隔離されており、ホストとは異なるシリアルで PID が割り振られる。
コンテナ内で生成された子プロセスも、ただのプロセスなので ps に映る。
つまりコンテナ内で生成されたプロセスは、コンテナ内としての PID と、コンテナ外（ホスト）としての PID のふたつをもつ

```
[root@hostname ~]# docker run -it centos:latest /bin/bash
[root@4c1ca7643ceb /]# sleep 1234 &
[1] 13
[root@4c1ca7643ceb /]# sleep 12345 &
[2] 14
[root@4c1ca7643ceb /]# sleep 123456 &
[3] 15

[root@hostname ~]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
4c1ca7643ceb        centos:latest       "/bin/bash"         31 seconds ago      Up 30 seconds                           angry_jepsen
[root@hostname ~]# ps alx|grep sleep|grep -v grep
0     0 27451  4818  20   0 107892   616 hrtime S    ?          0:00 sleep 60
0     0 27657 27616  20   0   4364   360 hrtime S    pts/1      0:00 sleep 1234
0     0 27663 27616  20   0   4364   360 hrtime S    pts/1      0:00 sleep 12345
0     0 27665 27616  20   0   4364   356 hrtime S    pts/1      0:00 sleep 123456
```

同じイメージから同じコマンドで生成したコンテナでも、生成するたびに違うコンテナが生成されている。
ホスト名は生成するたびに変わるし、あるコンテナで作成したファイルは別のコンテナからは見えない。

```
[root@hostname ~]# docker run -it centos:latest /bin/bash
[root@5b5388494463 /]# echo test > test
[root@5b5388494463 /]# cat /test
test
[root@5b5388494463 /]# exit
exit

[root@hostname ~]# docker run -it centos:latest /bin/bash
[root@d213b445f351 /]# cat /test
cat: /test: No such file or directory
```

コンテナの停止や再起動をしてもファイルシステムのデータは保持される。
docker start -i で interactive モードでコンテナを起動できる。

```
[root@hostname ~]# docker start 7666ac79ff57 -i
[root@7666ac79ff57 /]# echo test > /test
[root@7666ac79ff57 /]# cat test
test
[root@7666ac79ff57 /]# exit
exit
[root@hostname ~]# docker start 7666ac79ff57 -i
[root@7666ac79ff57 /]# cat test
test
[root@7666ac79ff57 /]# exit
exit
```

# 参考
以下を参考にさせていただきました。

https://jp-redhat.com/migration/docker/article.html
https://qiita.com/homines22/items/00eebe3c36a9591c3803