Play with Docker Classroomでhands-onを体験してみた。

dockerの公式チュートリアル兼Hands on labがあったので、やってみた。

※途中でlabのセッション切れたりで、実行ログに矛盾があるかもです。

# URL
[Play with Docker Classroom](https://training.play-with-docker.com/)
[Docker for IT Pros and System Administrators Stage 1](https://training.play-with-docker.com/ops-stage1/)
[Your First Linux Containers](https://training.play-with-docker.com/ops-s1-hello/)


※利用にはDockerアカウント登録が必要な模様。
　メールアドレスのみでサクッと作れます。

# lab環境の確認
```
$ uname -a
```

```
Linux node1 4.4.0-169-generic #198-Ubuntu SMP Tue Nov 12 10:38:00 UTC 2019 x86_64 Linux
```

うぶんちゅでーす。

# コンテナを起動してみる。
```
$ docker container run hello-world
```

``` 
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete
Digest: sha256:4fe721ccc2e8dc7362278a29dc660d833570ec2682f4e4194f4ee23e415e1064
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent
   it to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/
```

## 何が起きたか
Docker engineちゃんが下記の流れで動くご様子。

1. "hello-world"というイメージを探す。
1. ローカルに該当イメージがなかたので、デフォルトのDocker registryであるDocker Storeに
該当イメージを探しに行く。
1. 見つけたら落としてきて、コンテナの中で該当イメージを起動する。

## VMと何が違う？
VMはハードウェアの抽象化、コンテナはアプリケーションの抽象化。
VMの中でコンテナを動かすことが可能。

## イメージ
以降のlabはAlpine Linuxコンテナを使用するらしい。理由は、めっちゃ軽いから。

```
$ docker image pull alpine
```

```
Using default tag: latest
latest: Pulling from library/alpine
e6b0cf9c0882: Pull complete
Digest: sha256:2171658620155679240babee0a7714f6509fae66898db422ad803b951257db78
Status: Downloaded newer image for alpine:latest
docker.io/library/alpine:latest
```

早い…！
今回もデフォルトのDocker registryであるDocker Storeからpullしてきたが、
Docker registryは変更可能。ただし、このlabだとできないらしい。

```
$ docker image ls
```

```
REPOSITORY          TAG                 IMAGE ID            CREATED    SIZE
alpine              latest              cc0abc535e36        7 days ago    5.59MB
hello-world         latest              fce289e99eb9        12 months ago    1.84kB
```

先ほどpullしてきた"hello-world"と合わせ、2つのイメージが確認できる。

## alpineコンテナをいじってみる

```
$ docker container run alpine ls -l
```

```
total 8
drwxr-xr-x    2 root     root          4096 Dec 24 15:04 bin
drwxr-xr-x    5 root     root           340 Jan  1 15:45 dev
drwxr-xr-x    1 root     root            66 Jan  1 15:45 etc
drwxr-xr-x    2 root     root             6 Dec 24 15:04 home
drwxr-xr-x    5 root     root           185 Dec 24 15:04 lib
drwxr-xr-x    5 root     root            44 Dec 24 15:04 media
drwxr-xr-x    2 root     root             6 Dec 24 15:04 mnt
drwxr-xr-x    2 root     root             6 Dec 24 15:04 opt
dr-xr-xr-x  532 root     root             0 Jan  1 15:45 proc
drwx------    2 root     root             6 Dec 24 15:04 root
drwxr-xr-x    2 root     root             6 Dec 24 15:04 run
drwxr-xr-x    2 root     root          4096 Dec 24 15:04 sbin
drwxr-xr-x    2 root     root             6 Dec 24 15:04 srv
dr-xr-xr-x   13 root     root             0 Dec  4 04:54 sys
drwxrwxrwt    2 root     root             6 Dec 24 15:04 tmp
drwxr-xr-x    7 root     root            66 Dec 24 15:04 usr
drwxr-xr-x   12 root     root           137 Dec 24 15:04 var
```

見慣れたlsコマンドの結果が返ってきたが、dockerを通すと裏では下記の流れで処理されている。

1. Docker clientがalpineイメージを見つけて、コンテナを建てる。
1. 建てたalpineコンテナ上で`ls -l`を実行する。
1. `ls -l`が終了したら、コンテナをシャットダウンする。

コンテナに渡したコマンドが終了したら**コンテナがシャットダウンする**というのがミソ。
OSをVMとしてではなく、アプリケーションとして扱うからこその動作と思われる。

先ほどの`ls -l`と同じ流れで、alpineコンテナ上で‘echo‘を実行しつつ、`time`で実行時間を計測してみる。

```
$ time docker container run alpine echo "hello from alpine"
```

```
hello from alpine

real    0m1.163s
user    0m0.036s
sys     0m0.012s
```

仮にVMで同じ操作（VM起動→コマンド実行→VM停止）をする場合を考えると、
比較にならないほど早い。これは、VMと違ってハードウェアのエミュレーションが
不要であるところが大きい。

今度は、alpineコンテナ上で`/bin/sh`を実行してみる。

```
$ docker container run alpine /bin/sh
```

Docker client上でプロンプトが返るだけ。
意図したとおりに`/bin/sh`が実行されたは良いが、そのままshellを閉じて返ってきてしまった。

shellを対話形式で実行したい場合は、下記のように記述する。

```
$  docker container run -it alpine /bin/sh
```

参考までにオプションのヘルプ抜粋。

```
$ docker run --help
```

| オプション           | 意味                                                  |
|:--------------------|-------------------------------------------:|
| -i, --interactive     | Keep STDIN open even if not attached |
| -t, --tty                | Allocate a pseudo-TTY                        |

コンテナ上の標準入力に擬似端末を割り当てる、的な意味だと思う。

alpineコンテナのshell上でreleaseを確認してみる。

```
# cat /etc/alpine-release
```

```
3.11.2
```

alpineであることが確認できる。
`uname -a`を打つと…。

```
# uname -a
```

```
Linux dd6e08781c56 4.4.0-169-generic #198-Ubuntu SMP Tue Nov 12 10:38:00 UTC 2019 x86_64 Linux
```

うぶんちゅでーす。
コンテナはDocker hostのカーネルで動作する。こういった点も、コンテナが軽量である理由の1つ。

満足したのでalpineコンテナから抜けておく。

```
exit
```

Docker clientに戻ったところで、下記コマンドで実行中のコンテナを確認してみる。

```
$ docker container ls
```

```
CONTAINER ID        IMAGE               COMMAND             CREATED    STATUS              PORTS               NAMES
```

表示なし。
今まで実行したコマンドは、コンテナ内でプログラムを実行して、プログラム終了後にコンテナを
シャットダウンしてしまうので、コンテナは残っていない。

`-a`オプションを追記することで、コンテナの実行履歴が確認できる。
`STATUS`の時間が短いのは、ターミナルのセッション切れてやり直したからです。

```
$ docker container ls -a
```

```
$ docker container ls -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS               NAMES
b552d69dbb21        alpine              "/bin/sh"                6 minutes ago       Exited (0) 6 minutes ago                       distracted_moser
f1bf62c070a3        alpine              "/bin/sh"                6 minutes ago       Exited (0) 6 minutes ago                       keen_solomon
a512e55a5320        alpine              "echo 'hello from al…"   6 minutes ago       Exited (0) 6 minutes ago                       quirky_payne
7a34aab5edca        alpine              "ls -l"                  6 minutes ago       Exited (0) 6 minutes ago                       goofy_booth
164107ad610a        hello-world         "/hello"                 6 minutes ago       Exited (0) 6 minutes ago                       magical_volhard
```

# リソースの分離
`docker container ls -a`で参照したコンテナは、全て分離されていた。
これは、各コンテナが異なる名前空間上で、ファイルシステムを実行しているから。

ここで言う名前空間（namespaces）は、Linuxが提供するシステムリソースのwrap機能を指す。
プロセス（今回の場合はコンテナ）が使用するシステムリソースをLinuxの機能で他のプロセスから
分離しているので、各コンテナが使用するシステムリソースがセキュアに分離できている、というお話。
分離させるシステムリソースは、ファイルシステム、ネットワーク、PID、UID等々。

これが何を意味するかというと、新しくalpineコンテナを作成して…。

```
$ docker container run -it alpine /bin/ash
# echo "hello world" > hello.txt
# ls
```

```
bin        hello.txt  media      proc       sbin       tmp
dev        home       mnt        root       srv        usr
etc        lib        opt        run        sys        var
```

`hello.txt`を作る。その後、`hello.txt`を作成したalpineコンテナから抜けて、
Docker clientから`ls`。

```
$ docker container run alpine ls
```

```
bin
dev
etc
home
lib
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
```

`hello.txt`が存在しない。
これは、同じalpine imageから作成していても、`hello.txt`を作成したコンテナと、
`ls`を実行したコンテナが、違うシステムリソースを参照している（別の名前空間上で動作している）から。

`hello.txt`を作成したコンテナを再度起動するには、対象コンテナのCONTAINER IDを特定する必要がある。

```
$ docker container ls -a
```

```
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                         PORTS               NAMES
6863c5d371eb        alpine              "ls"                     11 minutes ago      Exited (0) 11 minutes ago                          flamboyant_hertz
e1d7ef17948e        alpine              "/bin/ash"               20 minutes ago      Exited (127) 11 minutes ago                        cool_leakey
b552d69dbb21        alpine              "/bin/sh"                About an hour ago   Exited (0) About an hour ago                       distracted_moser
f1bf62c070a3        alpine              "/bin/sh"                About an hour ago   Exited (0) About an hour ago                       keen_solomon
a512e55a5320        alpine              "echo 'hello from al…"   About an hour ago   Exited (0) About an hour ago                       quirky_payne
7a34aab5edca        alpine              "ls -l"                  About an hour ago   Exited (0) About an hour ago                       goofy_booth
164107ad610a        hello-world         "/hello"                 About an hour ago   Exited (0) About an hour ago                       magical_volhard
```

先ほど`/bin/ash`でログインしてコンテナを起動したので、`hello.txt`を作成したコンテナの
CONTAINER IDは`e1d7ef17948e`であることがわかる。

`docker container start <container ID>`で対象コンテナを起動する。

```
$ docker container start e1d7ef17948e
```

起動したか確認。

```
$ docker container ls
```

```
CONTAINER ID        IMAGE               COMMAND             CREATED    STATUS              PORTS               NAMES
e1d7ef17948e        alpine              "/bin/ash"          24 minutes ago    Up 35 seconds                           cool_leakey
```

今は、先ほど`hello.txt`を作成したコンテナ上で、`/bin/ash`がコマンド待ちしている状態。
このコンテナに、`docker container exec`を使用して、`ls`を渡してあげると…。

```
$ docker container exec e1d7ef17948e ls
```

```
bin
dev
etc
hello.txt
home
lib
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
```

`hello.txt`が確認できた。

# まとめ
1. `docker image pull <IMAGE>`でDocker registryからイメージをダウンロードする。
1. `docker image ls`でイメージを確認する。
1. `docker container run <IMAGE>`で**新規**コンテナを起動する。
起動したコンテナは**プログラム実行後にシャットダウンする。**
1. 起動したコンテナはDockerホストのカーネルに依存する。
1. `docker container ls`で起動中のコンテナを確認する。
`-a`をつけると、シャットダウン済みのコンテナも確認できる。
1. 各コンテナは、同じイメージから起動しても、別の名前空間を使用するため、異なるシステムリソースを
参照する。
1. 一度シャットダウンしたコンテナを再度起動するには、`docker container ls -a`でCONTAINER IDを
特定して、`docker container start <CONTAINER ID>`を実行する。