Docker container コマンドのメモ（コマンドフロー図付き）

# これは何？
- docker container コマンドについて書いたメモです。
  - 操作対象が明確になるよう `container` は省略しない。
  - 例えば `docker container run` は `docker run` でもよい。
  - 各コマンド毎に、Docker公式のドキュメント(英語)、及び Docker-docs-ja(日本語) へのリンクも付けておきます。
- 気になった部分は、`[補足]` として詳しく書きました。
- なお、環境は Vagrant Ubuntu18.04(ubuntu/bionic64)、Docker version 18.06.1-ce, build e68fc7a を使用しました。


# 要約
- [コンテナのライフサイクル](#コンテナのライフサイクル)
- [docker container コマンドフロー](#docker-container-コマンドフロー)
- [docker container run IMAGE](#docker-container-run-image)
  - [[補足] コンテナは停止しても、削除はされません。](#補足-コンテナは停止しても削除はされません)
- [docker container attach CONTAINER](#docker-container-attach-container)
- [docker container exec CONTAINER](#docker-container-exec-container)
- [docker container logs CONTAINER](#docker-container-logs-container)
- [docker container stop CONTAINER](#docker-container-stop-container)
  - [[補足] docker container stop ですぐに停止しない理由](#補足-docker-container-stop-ですぐに停止しない理由)
  - [[補足] --init について](#補足---init-について)
- [docker container kill CONTAINER](#docker-container-kill-container)
- [docker container rm CONTAINER](#docker-container-rm-container)
- [docker container start CONTAINER](#docker-container-start-container)
- [docker container pause CONTAINER](#docker-container-pause-container)
- [docker container unpause CONTAINER](#docker-container-unpause-container)
- [docker container prune](#docker-container-prune)
- [docker container ls](#docker-container-ls)
  - [[補足] コンテナの状態(created|restarting|removing|running|paused|exited)を取得する方法は？](#補足-コンテナの状態createdrestartingremovingrunningpausedexitedを取得する方法は)
- [docker container top CONTAINER](#docker-container-top-container)
  - [[補足] コンテナ内部で実行したプロセスがどのように見えるか？](#補足-コンテナ内部で実行したプロセスがどのように見えるか)
- [docker container stats](#docker-container-stats) 

# コンテナのライフサイクル
- 主なものに「`実行中` -> `停止` -> `破棄`」がある。
- `実行中`
    - 指定されたイメージをもとにコンテナが作成され、DockerfileのCMDやENTRYPOINTで定義されているアプリが実行される。
    - ここで起動されたアプリが実行中なら、コンテナの状態としても `実行中` となる。
- `停止`
    - Webアプリなど、HTTPリクエストを受けるコンテナは、`実行中` の状態が継続するので、起動している限り勝手に `停止` にはならない。
    - コマンドラインツールなど、一度の実行で終了し `停止` になる。
- `破棄`
    - `停止` してもコンテナの内部データは保持されており、勝手に `破棄` はされない。
    - 従って、頻繁にコンテナの開始、停止を繰り返すと、ディスクをどんどん消費するので注意
    - なお、コンテナ開始時に `--rm` オプションを付けておけば、`停止` と同時に `破棄` することも可能

# docker container コマンドフロー
![docker-container-command-flow.png](https://qiita-image-store.s3.amazonaws.com/0/275337/263b659e-0793-baa7-4178-6ad8f1540ad5.png)


# docker container run IMAGE
- Docker ドキュメント
  - http://docs.docker.jp/engine/reference/commandline/run.html
  - https://docs.docker.com/engine/reference/commandline/container_run/

```shell-session
# 指定イメージからコンテナを生成実行＆
# 指定コマンド(/bin/bash)を実行（CMDの上書き実行）
# 指定コマンド終了と伴にコンテナを自動的に破棄(--rm)
$ docker container run -it --rm --name ubuntu ubuntu /bin/bash

# バックグラウンドで起動
$ docker container run -d -p 8080:80 nginx

# ホストの9000を、コンテナの8080へポートフォワードする。
#   `curl http://localhost:9000/` で コンテナの8080へつながる。
$ docker container run -d -p 9000:8080 --name echo example/echo:latest`
```

- `-d`(--detach) : バックグラウンドで実行
- `-t`(--tty) : ホストのシェルの入出力を実行するコンテナの PID=1 につなぐ
- `-i`(--interactive) : ホストの入力をコンテナにつなぎ続ける
  - `ctrl+p` `ctrl+q` : コンテナ終了せずホストに戻る
  - 再度コンテナにつなぐ場合、`docker container attach` する。
- `--rm` : コンテナが停止したときに、自動でコンテナを削除する
- `--name` : 任意のコンテナ名を付ける(これが無いと適当な名前が付けられる)

#### [補足] コンテナは停止しても、削除はされません。

- 通常、コンテナが正常であれ異常であれ停止(**exited**)しても、コンテナは自動で削除されない。
- コンテナを削除するときは、明示的に `docker container rm` または `docker container prune` コマンドを実行しないと、削除されることはない。
- ただし、自動削除したい場合、`docker container run` で `--rm` を付与すれば、コンテナ停止(**exited**)と同時に削除することもできる。

# docker container attach CONTAINER
- Docker ドキュメント
  - http://docs.docker.jp/engine/reference/commandline/attach.html
  - https://docs.docker.com/engine/reference/commandline/container_attach/

- 再度、ホストのシェルの入出力をコンテナにつなぐ(PID=1 につなぐ)
- PID=1 が `bash` であっても `docker container exec` で新たな `bash` の起動がおすすめ
  - 理由）PID=1 の `bash` で `ctrl+d` や `exit` でシェルを抜けると、コンテナ自身が終了してしまう為

# docker container exec CONTAINER
- Docker ドキュメント
  - http://docs.docker.jp/engine/reference/commandline/exec.html
  - https://docs.docker.com/engine/reference/commandline/container_exec/

```shell-session
$ docker container exec -it ubuntu /bin/bash  # コンテナ内で新しいbashを起動する。
$ docker container exec ubuntu ls -al /
```

- running コンテナ内で任意のコマンドを実行する。
- PIDは新たに採番される(つまり、`docker container attach` のように **PID=1** にはつながらない)
- `-it` : ホストのシェルの入出力を実行するコマンドにつなぐ
  - `ctrl+p` `ctrl+q` : 実行コマンドを終了せずホストに戻る

# docker container logs CONTAINER
- Docker ドキュメント
  - http://docs.docker.jp/engine/reference/commandline/logs.html
  - https://docs.docker.com/engine/reference/commandline/container_logs/

- コンテナのログを表示する。
- コンテナの **PID=1** の標準出力(エラー出力も)が logs になるので、例えば **PID=1** が `bash` の場合、ここで操作した内容も、logs コマンドから見ることが出来る。

- `-f`(--follow) : 出力を追いかける(出力を待ち続ける)

# docker container stop CONTAINER
- Docker ドキュメント
  - http://docs.docker.jp/engine/reference/commandline/stop.html
  - https://docs.docker.com/engine/reference/commandline/container_logs/

- コンテナを停止させる。(**running** -> **exited**)
- デフォルトでは、コンテナの **PID=1** へ SIGTERM を送信し停止させる。ただし、10秒経っても停止しない場合、SIGKILL を送信し強制終了になる。
  - **つまり、すぐに停止しない場合、強制終了になった可能性が高いので要注意**


#### [補足] `docker container stop` ですぐに停止しない理由

- コンテナが停止するまで。
  - 1) `docker container stop` は 、コンテナ内の **PID=1** に SIGTERM を送信する。
  - 2) SIGTERM を受けた **PID=1** のプロセスが終了処理を行い停止する。

- **ところが、PID=1 で実行されたプロセスは特殊で、デフォルトで全てのシグナルを無視する。**

- Docker run リファレンス &mdash; Docker-docs-ja 17.06.Beta ドキュメント
 http://docs.docker.jp/engine/reference/run.html
 > コンテナ内で PID 1 として実行しているプロセスは、Linux が特別に扱います。
 > デフォルトの操作では、あらゆるシグナルを無視します。
 > そのため、プロセスは SIGINT か SIGTERM で停止するようにコードを書かない限り、停止できません。

- つまり、SIGINT、SIGTERM を適切に処理するコードを書いたプログラムを **PID=1** で実行させないと `docker container stop` してもすぐには停止しない。
- `--init` で解決するようだ。おそらく表面上は・・・

#### [補足] `--init` について

- 恐らく**『コンテナがすぐに停止しない問題を、安易に --init で解決してしまう』**と、実害として**『kubernetes などで、オートスケーリングの都度、正体不明のアプリ不具合が生じる可能性』**がある。
- なぜなら、`--init` はシグナルを受信できるプロセスを **PID=1** で動かすだけで、これの子として実行するアプリレベルで見れば、適切な終了になっている保証は無い。
- 本来なら、アプリに応じた適切な終了処理をシグナル受信時に行うべき。行っていない場合、シグナルを受けると即終了してしまう為、負荷の急増/急減で大量のコンテナが停止すると、まさに処理中(通信中、ファイルアクセス中など)にシグナル受信し、強制終了するケースが頻発するはず。

# docker container kill CONTAINER
- Docker ドキュメント
  - http://docs.docker.jp/engine/reference/commandline/kill.html
  - https://docs.docker.com/engine/reference/commandline/container_kill/

- 普通は使わない。
- コンテナを強制停止させる。(**running** -> **exited**)
- デフォルトで SIGKILL を送信する。
- `docker container stop` を使うべき
  - 理由）SIGTERM を送信することで、アプリの適切な終了処理を促せる。（終了処理があるならですが）

# docker container rm CONTAINER
- Docker ドキュメント
  - http://docs.docker.jp/engine/reference/commandline/rm.html
  - https://docs.docker.com/engine/reference/commandline/container_rm/

```sh
# 指定イメージで起動されたコンテナを全て強制削除
$ docker container rm -f $(docker container list --filter "ancestor=<イメージ名>" -q)`
```

- コンテナを削除する。
  - 誤って削除しても二度と復活しないので注意
  - 操作を誤って running コンテナ を削除しようとしても、できない安心設計になっている。
- '-f'(--force) : running コンテナも強制削除したいとき

# docker container start CONTAINER
- Docker ドキュメント
  - http://docs.docker.jp/engine/reference/commandline/start.html
  - https://docs.docker.com/engine/reference/commandline/container_start/

- コンテナを開始する。**exited** を 再度 **running** へ

# docker container pause CONTAINER
- Docker ドキュメント
  - http://docs.docker.jp/engine/reference/commandline/pause.html
  - https://docs.docker.com/engine/reference/commandline/container_pause/

- コンテナを一時停止する。**running** を **paused** へ

# docker container unpause CONTAINER
- Docker ドキュメント
  - http://docs.docker.jp/engine/reference/commandline/unpause.html
  - https://docs.docker.com/engine/reference/commandline/container_unpause/

- コンテナの一時停止を解除する。**paused** を **running** へ

# docker container prune
- Docker ドキュメント
  - https://docs.docker.com/engine/reference/commandline/container_prune/

- 停止中(**exited**)のコンテナを完全に削除する。
  - 誤って削除しても二度と復活しないので注意
- １コンテナ毎に `docker container rm` する手間を省ける。


# docker container ls
- Docker ドキュメント
  - http://docs.docker.jp/engine/reference/commandline/ps.html
  - https://docs.docker.com/engine/reference/commandline/container_ls/

```shell-session
$ docker container ls

# コンテナの状態でフィルタ status=(created|restarting|removing|running|paused|exited)
$ docker container ls -a --filter 'status=running'

# コンテナ名でフィルタ name=<コンテナ名> 
$ docker container ls -a --filter 'name=ubuntu'
```
- コンテナの一覧を表示する。
- `ls` は `list` `ps` でもいい
- `-a`(--all) : 全状態のコンテナの一覧(default runningコンテナのみ)
- `-f`(--filter) : フィルタ条件

```
$ man docker-container-ls
:
       Filter output based on these conditions:
          - ancestor=(<image-name>[:tag]|<image-id>| ⟨image@digest⟩)
            containers created from an image or a descendant.
          - before=(<container-name>|<container-id>)
          - expose=(<port>[/<proto>]|<startport-endport>/[<proto>])
          - exited=<int> an exit code of <int>
          - health=(starting|healthy|unhealthy|none)
          - id=<ID> a container's ID
          - isolation=(default|process|hyperv) (Windows daemon only)
          - is-task=(true|false)
          - label=<key> or label=<key>=<value>
          - name=<string> a container's name
          - network=(<network-id>|<network-name>)
          - publish=(<port>[/<proto>]|<startport-endport>/[<proto>])
          - since=(<container-name>|<container-id>)
          - status=(created|restarting|removing|running|paused|exited)
          - volume=(<volume name>|<mount point destination>)
```

#### [補足] コンテナの状態(created|restarting|removing|running|paused|exited)を取得する方法は？

- コンテナの状態には、(created|restarting|removing|running|paused|exited) があるらしいが、`docker container ls` で表示される `STATUS`(ex. "Up 25 minutes") は加工されているので、加工されていないものが見たい。

- `docker container ls` の `--format` でやってみる → 失敗

```shell-session
$ FMT="table {{.ID}}\t{{.Image}}\t{{.Names}}\t{{.Command}}\t{{.Status}}\t{{.Size}}\t{{.CreatedAt}}\t{{.RunningFor}}\t{{.Ports}}\t{{.Networks}}\t{{.Labels}}"
$ docker container ls -a --format "$FMT"
CONTAINER ID        IMAGE               NAMES                COMMAND                  STATUS              SIZE                  CREATED AT                      CREATED             PORTS                  NETWORKS            LABELS
1816cfc302a8        nginx               tender_nobel         "nginx -g 'daemon of…"   Up About a minute   2B (virtual 109MB)    2018-09-09 17:55:17 +0900 JST   3 minutes ago       0.0.0.0:8080->80/tcp   bridge              maintainer=NGINX Docker Maintainers <docker-maint@nginx.com>
e234e67b68d0        ubuntu              boring_stonebraker   "--name ubuntu"          Created             0B (virtual 84.1MB)   2018-09-09 17:26:02 +0900 JST   33 minutes ago                             bridge
```

- Docker API でやってみる → 成功

```shell-session
$ # API経由で全コンテナのリストを得る
$ curl -s --unix-socket /var/run/docker.sock http:/v1.37/containers/json?all=true \
  | python3 -mjson.tool \
  | head -25
[
    {
        "Id": "1816cfc302a8149a94cb60eb13bf0570262ac61ca951deb898835440fe517597",
        "Names": [
            "/tender_nobel"
        ],
        "Image": "nginx",
        "ImageID": "sha256:06144b2878448774e55577ae7d66b5f43a87c2e44322b3884e4e6c70d070b262",
        "Command": "nginx -g 'daemon off;'",
        "Created": 1536483317,
        "Ports": [
            {
                "IP": "0.0.0.0",
                "PrivatePort": 80,
                "PublicPort": 8080,
                "Type": "tcp"
            }
        ],
        "Labels": {
            "maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"
        },
        "State": "running", # <------------------------- 状態
        "Status": "Up 3 minutes",
        "HostConfig": {
            "NetworkMode": "default"
$
$ # jqを使って、コンテナ名と状態のみ抽出
$ curl -s --unix-socket /var/run/docker.sock http:/v1.37/containers/json?all=true \
    | jq -r '[ .[] | { Name: .Names[0], State: .State } ]' \
    | jq -r '["Name","State"], (.[] | [.Name,.State]) | @csv' \
    | column -t -s ,
"Name"                 "State"
"/tender_nobel"        "running"
"/boring_stonebraker"  "created"
"/ubuntu"              "exited"
```

# docker container top CONTAINER
- Docker ドキュメント
  - http://docs.docker.jp/engine/reference/commandline/top.html
  - https://docs.docker.com/engine/reference/commandline/container_top/

- コンテナ内のプロセス一覧を表示
- コンテナ内部で ps するのと同じイメージ

#### [補足] コンテナ内部で実行したプロセスがどのように見えるか？

- ubuntuコンテナ内部で、sleep を２つをバックグラウンドで動かして、`docker container top` や 普通に `ps` してみる。

- まず、コンテナから

```shell-session
$ # コンテナを起動し、コンテナ内部で、sleep を動かしておく
$ docker container run -it --name ubuntu ubuntu /bin/bash
root@fc659b390b7f:/# sleep 1000000 &
[1] 10
root@fc659b390b7f:/# sleep 1000000 &
[2] 11
root@fc659b390b7f:/# ps -alxf
F   UID   PID  PPID PRI  NI    VSZ   RSS WCHAN  STAT TTY        TIME COMMAND
4     0     1     0  20   0  18508  3448 wait   Ss   pts/0      0:00 /bin/bash
4     0    10     1  20   0   4532   780 hrtime S    pts/0      0:00 sleep 1000000
0     0    11     1  20   0   4532   776 hrtime S    pts/0      0:00 sleep 1000000
4     0    12     1  20   0  25944  1412 -      R+   pts/0      0:00 ps -alxf
root@fc659b390b7f:/# read escape sequence ---- ### ctrl+p ctrl+q でコンテナを抜けホストに戻る
```

- 次に、ホストから

```shell-session
$ # docker container top してみる → コンテナから見たPID、PPIDとは違って見えている(プロセス空間が隔離されている為)
$ docker container top ubuntu
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                20808               20775               0                   01:10               pts/0               00:00:00            /bin/bash
root                21200               20808               0                   01:16               pts/0               00:00:00            sleep 1000000
root                21203               20808               0                   01:16               pts/0               00:00:00            sleep 1000000
$
$ # docker container top で、オプション指定して、詳細を見る
$ docker container top ubuntu -alxf
F                   UID                 PID                 PPID                PRI                 NI                  VSZ                 RSS                 WCHAN               STAT                TTY                 TIME                COMMAND
4                   0                   20808               20775               20                  0                   18508               3444                poll_s              Ss+                 pts/0               0:00                | \_ /bin/bash
4                   0                   21200               20808               20                  0                   4532                780                 hrtime              S                   pts/0               0:00                | \_ sleep 1000000
0                   0                   21203               20808               20                  0                   4532                776                 hrtime              S                   pts/0               0:00                | \_ sleep 1000000
$
$ # ホストで普通に ps してみる → コンテナ内部のプロセスも見えている(コンテナはホストのkernelを使っている為)
$ ps -alxf
F   UID   PID  PPID PRI  NI    VSZ   RSS WCHAN  STAT TTY        TIME COMMAND
:
4     0  1286     1  20   0 1113408 54580 -     Ssl  ?         10:40 /usr/bin/dockerd -H fd://
4     0  1435  1286  20   0 1024084 23248 -     Ssl  ?          8:22  \_ docker-containerd --config /var/run/docker/containerd/containerd.toml
4     0 20775  1435  20   0   8908  4412 -      Sl   ?          0:00      \_ docker-containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/fc659b390b7f525d6be7e69d0f2610887fffc05f75
4     0 20808 20775  20   0  18508  3444 -      Ss+  pts/0      0:00          \_ /bin/bash
4     0 21200 20808  20   0   4532   780 -      S    pts/0      0:00              \_ sleep 1000000
0     0 21203 20808  20   0   4532   776 -      S    pts/0      0:00              \_ sleep 1000000
$
```

# docker container stats
- top コマンドのようなもの

```sh
$ docker container stats
CONTAINER ID    NAME     CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O      PIDS
c4fd37367595    echo     0.00%     6.887MiB / 1.947GiB   0.35%     6.71kB / 890B    149MB / 0B     14
```