docker run の --memory オプションについて調べた

既に類似記事がありましたが、確認のためにメモ。
もし、間違っている部分などあればご指摘ください。

# 検証環境

```bash
$docker info
Containers: 5
 Running: 5
 Paused: 0
 Stopped: 0
Images: 13
Server Version: 17.12.1-ce
Storage Driver: devicemapper
 Pool Name: docker-docker--pool
 Pool Blocksize: 524.3kB
 Base Device Size: 10.74GB
 Backing Filesystem: ext4
 Udev Sync Supported: true
 Data Space Used: 2.259GB
 Data Space Total: 23.33GB
 Data Space Available: 21.07GB
 Metadata Space Used: 1.507MB
 Metadata Space Total: 25.17MB
 Metadata Space Available: 23.66MB
 Thin Pool Minimum Free Space: 2.333GB
 Deferred Removal Enabled: true
 Deferred Deletion Enabled: true
 Deferred Deleted Device Count: 0
 Library Version: 1.02.135-RHEL7 (2016-11-16)
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
 Volume: local
 Network: bridge host macvlan null overlay
 Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 9b55aab90508bd389d7654c4baf173a981477d55
runc version: 9f9c96235cc97674e935002fc3d78361b696a69e
init version: 949e6fa
Security Options:
 seccomp
  Profile: default
Kernel Version: 4.9.85-38.58.amzn1.x86_64
Operating System: Amazon Linux AMI 2017.09
OSType: linux
Architecture: x86_64
CPUs: 2
Total Memory: 3.86GiB
Name: ip-172-31-16-119
ID: 7BRW:LUN6:5ZPM:CNL7:YVW3:MV6V:MGJ7:WY6X:CCVF:7PIT:F73P:LHEH
Docker Root Dir: /var/lib/docker
Debug Mode (client): false
Debug Mode (server): false
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
 127.0.0.0/8
Live Restore Enabled: false

```

# --memory オプションの概要について

以下に記載のように `--memory` オプションによってコンテナのメモリのハードリミットを設定できます。
また、こちらは cgroup を利用しており、 **memory.limit_in_bytes** として確認が出来る点が記載されております。

>
Specify hard limits on memory available to containers (-m, --memory)

>These parameters always set an upper limit on the memory available to the container. On Linux, this is set on the cgroup and applications in a container can query it at /sys/fs/cgroup/memory/memory.limit_in_bytes

[docker run](https://docs.docker.com/engine/reference/commandline/run/)

実際に検証環境でも上記動作となっている事が確認出来ました。

```bash
# 20M をハードリミットとして指定
$docker run -d -m "20M" --name wp20m wordpress
822a4003bb46009215c0013a7031b81b8af491c76eff65b303f00161d78e3a45

# 対象コンテナの memory.limit_in_bytes を確認
$cat /cgroup/memory/docker/822a4003bb46009215c0013a7031b81b8af491c76eff65b303f00161d78e3a45/memory.limit_in_bytes
20971520

# docker inspect コマンドでも設定値は確認できる
$ docker inspect --format='{{.HostConfig.Memory}}' wp20m
20971520

```

# --memory に指定した値を超過した場合どうなるのか

`docker run` には `--oom-kill-disable` オプションがあります。
こちらの説明に書いてあるようにデフォルトでは out-of-memory の場合には OOM が発生してカーネルがコンテナ内のプロセスを kill する旨が書いてあります。

>--oom-kill-disable 	By default, if an out-of-memory (OOM) error occurs, the kernel kills processes in a container. To change this behavior, use the --oom-kill-disable option. Only disable the OOM killer on containers where you have also set the -m/--memory option. If the -m flag is not set, the host can run out of memory and the kernel may need to kill the host system’s processes to free memory.

[Limit a container's resources](https://docs.docker.com/config/containers/resource_constraints/)

上記より、デフォルトでは OOM が発生し、コンテナが停止となると考えられますが、試しにやってみます。

```bash
# メモリは 10M を指定
$docker run -d -m "10m" --name wp10m wordpress
b5f60e8379207df79fa9fbe3ea09ec003380192d40c7fc79227b142b830bd570

# --oom-kill-disable オプションを指定しない場合、デフォルトで disable が false な事を確認
$docker inspect --format='{{.HostConfig.OomKillDisable}}' wp10m
false

# 起動している
$docker ps
CONTAINER ID        IMAGE                            COMMAND                   CREATED              STATUS              PORTS                NAMES
b5f60e837920       wordpress                        "docker-entrypoint.s…"    About a minute ago   Up About a minute   80/tcp               wp10m

# 10分ほど経過後終了ステータスコード 137 によって終了
$docker ps -a
CONTAINER ID        IMAGE                            COMMAND                   CREATED             STATUS                            PORTS                NAMES
b5f60e837920        wordpress                        "docker-entrypoint.s…"    5 minutes ago       Exited (137) About a minute ago                        wp10m

# docker inspect 一部抜粋。OOMkilled されたのが分かる
$docker inspect wp10m
        "State": {
            "Status": "exited",
            "Running": false,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": true,
            "Dead": false,
            "Pid": 0,
            "ExitCode": 137,
            "Error": "",
            "StartedAt": "2018-05-26T21:26:23.199697785Z",
            "FinishedAt": "2018-05-26T21:30:20.351086551Z"
        },
        
# /var/log/messages からも OOM によって kill されたことが分かる。コンテナIDも記載されているので特定も出来そう
$cat /var/log/messages  |grep -i kill        
        
May 26 21:30:20 ip-172-31-16-119 kernel: [247319.777361]  [<ffffffff81187f5b>] oom_kill_process+0x20b/0x3e0
May 26 21:30:20 ip-172-31-16-119 kernel: [247319.853629] Task in /docker/b5f60e8379207df79fa9fbe3ea09ec003380192d40c7fc79227b142b830bd570 killed as a result of limit of /docker/b5f60e8379207df79fa9fbe3ea09ec003380192d40c7fc79227b142b830bd570
May 26 21:30:20 ip-172-31-16-119 kernel: [247319.909426] Memory cgroup out of memory: Kill process 19299 (apache2) score 831 or sacrifice child
May 26 21:30:20 ip-172-31-16-119 kernel: [247319.914894] Killed process 19299 (apache2) total-vm:389804kB, anon-rss:7656kB, file-rss:0kB, shmem-rss:376kB
```

終了スタータスコードが137で終了する点についてはいくつかの Github issue に記載がありました。

+ [Container crashes with code 137 when given high load ](https://github.com/moby/moby/issues/22211)
+ [What is Docker container exit code 137?](https://github.com/moby/moby/issues/21083)

後者のGtihub issueのコメントにあるようにこの終了コードはコンテナのプロセスが返したコードと考えると分かりやすい気がします。
流れとしては以下と考えています。

+ OOM がシグナルSIGKILL(9)を対象のコンテナ内のプロセスに送信
+ 上記シグナルを受けてコンテナのプロセスが終了。SIGKILL(9)のため、128+9で137 を返却。参考記事。[Appendix E. Exit Codes With Special Meanings](http://tldp.org/LDP/abs/html/exitcodes.html)

cgroup の `memory.limit_in_bytes`を超過した場合に OOM が終了させるテストについては Redhat のページにも記載がありました。

>5.ステップ 2 で設定した上限を超える大容量のメモリーを割り当てようとするテストプログラムを起動します。blue cgroup の空きメモリーがなくなるとすぐに OOM Killer がテストプログラムを強制終了し、標準出力に Killed をレポートします。

[3.7. memory](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/6/html/resource_management_guide/sec-memory)


# 参考記事

+ [Dockerのリソースの制限（CPU/メモリ）とCPUの動きについてまとめ](https://qiita.com/Gin/items/450c664ef51f38c0a520)
+ [SIGNAL](https://linuxjm.osdn.jp/html/LDP_man-pages/man7/signal.7.html)