ECSのCPU・メモリ設定とDockerコマンドの対応

## 概要
- AWS ECSでのタスク定義を行う際のCPU、メモリについてメモ
- ECSでのCPU・メモリの設定と、Dockerコマンドの対応のメモ

## 公式ドキュメント

https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_definitions.html
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_definition_parameters.html
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/memory-management.html


## タスクサイズでの設定
CPU ユニット、メモリ共にハード制限の設定を行うことが可能。下記のようにFargateでは必須、EC2起動タイプではオプションとなっており、コンテナ定義での設定の省略が可能。

>タスクサイズにより、タスクの固定サイズを指定できます。Fargate 起動タイプを使用したタスクにはタスクサイズが必須で、EC2 起動タイプではオプションです。タスクサイズが設定されている場合、コンテナレベルのメモリ設定はオプションです。

https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_definition_parameters.html#task_size

### タスクメモリ
タスクに適用されるメモリのハード制限。コンテナ定義のハード制限と同義。

>EC2 起動タイプを使用する場合、このフィールドはオプションです。クラスターに、リクエストされたメモリが利用できる登録されたコンテナインスタンスがない場合、タスクは失敗します。

### タスクCPU
タスクに適用されるCPU ユニットのハード制限。 ドキュメント上ではハード制限と記述されているが、コンテナ定義のCPU ユニット数と同義か。

> EC2 起動タイプを使用する場合、このフィールドはオプションです。クラスターに、リクエストされた CPU ユニットが利用できる登録されたコンテナインスタンスがない場合、タスクは失敗します。

## コンテナ定義

### メモリ制限：ソフト制限（memoryReservation）

--memory-reservation オプションを docker run にマッピングする。ホストのメモリに余裕がある時は制限以上のメモリを使用する場合もあり。制限を超過しないことを保証する訳ではない。ECSではこの値を見て、コンテナを追加できるか判断している。

>コンテナ用に予約するメモリのソフト制限 (MiB 単位)。システムメモリが競合している場合、Docker はコンテナメモリをこのソフト制限に維持しようとします。ただし、コンテナは必要に応じて、memory パラメータで指定したハード制限 (該当する場合)、またはコンテナインスタンスの使用可能なメモリの、いずれか先に達するまで、追加のメモリを消費できます。このパラメータは、Docker Remote API の コンテナを作成する セクションの MemoryReservation にマッピングし、--memory-reservation オプションを docker run にマッピングします。

### メモリ制限：ハード制限（memory）

--memory オプションを docker run にマッピングする。コンテナが指定した以上のメモリを使用する場合は OOMKiller が走り、コンテナが停止される。

>コンテナに適用されるメモリの量 (MiB 単位)。コンテナは、ここで指定したメモリを超えようとすると、強制終了されます。タスク内のすべてのコンテナ用に予約されるメモリの合計量は、タスクの memory 値より小さくする必要があります (指定されている場合)。このパラメータは、Docker Remote API の コンテナを作成する セクションの Memory にマッピングし、--memory オプションを docker run にマッピングします。

>コンテナが Fargate 起動タイプを使用したタスクの一部である場合、このフィールドはオプションです。

### CPU ユニット数

--cpu-shares オプションを docker run にマッピングする。

>Amazon ECS コンテナエージェントがコンテナ用に予約した cpu ユニットの数です。このパラメータは、Docker Remote API の コンテナを作成する セクションの CpuShares にマッピングし、--cpu-shares オプションを docker run にマッピングします。

>Fargate 起動タイプを使用するタスクでは、このフィールドはオプションです。その場合、タスク内のすべてのコンテナ用に予約される CPU の合計量が、タスクレベルの cpu の値を下回ることが唯一の要件となります。

## 対応するDockerコマンド
ECSでのリソース制限では、docker run にオプションをマッピングしているので、そもそもdockerがどのようにリソースを管理しているか、dockerコマンドでメモリ制限・ハード制限を行うためのコマンドをメモ。

### 公式ドキュメント
http://docs.docker.jp/engine/reference/run.html?highlight=docker%20run#runtime-constraints-on-resources

### CPU制限（--cpu-shares）
共有するCPUの割合を相対的な値として設定するもの。

> デフォルトでは、全てのコンテナは同じ CPU サイクルの割合を持っています。この割合は変更可能なものであり、コンテナの CPU 共有ウエイトを、実行中の全てのコンテナに対する相対的な値として変更できます

>例えば、３つのコンテナがあるとしましょう。１つめの CPU 共有は 1024 で、残り２つの CPU 共有は 512 とします。もし３つのコンテナが CPU を 100% 使用している状態になれば、１つめのコンテナが合計 CPU 時間の 50% を扱えます。４つめのコンテナを CPU 共有 1024 として追加したら、１つめのコンテナが得られるのは CPU の 33% になります。そして、残りの２つめ以降のコンテナが得られる CPU 時間は、それぞれ 16.5%（２つめ）、16.5%（３つめ）、33% （４つめ）となります。

```
docker run -–cpu-shares [CPU] [IMAGE]
```

その他CPUに関するオプション

```
--cpu-period int                 Limit CPU CFS (Completely Fair Scheduler) period
--cpu-quota int                  Limit CPU CFS (Completely Fair Scheduler) quota
--cpu-rt-period int              Limit CPU real-time period in microseconds
--cpu-rt-runtime int             Limit CPU real-time runtime in microseconds
--cpu-shares int                 CPU shares (relative 
--cpus decimal                   Number of --cpuset-cpus string             CPUs in which to allow execution (0-3, --cpuset-mems string             MEMs in which to allow execution (0-3, 0,1)
```

--cpu-period、 --cpu-quota 等は今のところECSのタスク定義では設定することができない。

### メモリ制限

メモリ制限の種類は、ECSの項目にもあった通り、ハード制限とソフト制限が存在する。
ハード制限（memory） は ソフト制限（memoryReservation） より大きいことが必要。

#### ハード制限(--memory)

```
docker run --memory [メモリ] [IMAGE]
```

#### ソフト制限（--memoryReservation）

```
docker run --memory-reservation [メモリ] [IMAGE]
```

helpで確認できるメモリ関係のコマンドの一覧。memory は memoryReservation より大きいことが必要。

```
--memory bytes                   Memory limit
--memory-reservation bytes       Memory soft limit
--memory-swap bytes              Swap limit equal to memory plus swap: '-1' to enable unlimited swap
--memory-swappiness int          Tune container memory swappiness (0 to 100) (default -1)
```
実際にコマンドを実行してみる。ハード制限、ソフト制限でメモリを指定したコンテナをそれぞれ立ち上げてみる。

```
docker run -d --memory 4MB [IMAGE]
docker run -d --memory-reservation 4MB [IMAGE]
```

ハード制限での指定は、MEM USAGE / LIMIT で 設定されているのを確認できる。
ソフト制限を行った設定については、docker stats上では確認することができない・・・？

```
docker stats

CONTAINER ID        NAME                  CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
2468e6dbc5c6        beautiful_heyrovsky   0.00%               1.449MiB / 4MiB       36.23%              718B / 0B           0B / 0B             3
29acf48ef4d4        naughty_khorana       0.00%               1.492MiB / 3.855GiB   0.04%               928B / 0B           1.81MB / 0B         3
```

yes > /dev/null & 等で負荷を与えた時、ハード制限を行ったものに関して、制限以上のメモリを使用することはなかった。
ソフト制限をを行ったコンテナに関しては、ホストのメモリに余裕がある場合は、制限以上のメモリを使用する。

### その他オプション
#### --oom-kill-disable
docker run のデフォルトでは、OOMエラーが発生したら、コンテナ内のプロセスを停止（kill）する。このオプションでコンテナを OOM killer による停止を無効化するかどうか指定することができる。

```
docker run --oom-kill-disable [IMAGE]
```

## 参考

https://aws.amazon.com/jp/premiumsupport/knowledge-center/allocate-ecs-memory-tasks/
https://knowledge.sakura.ad.jp/5118/
https://qiita.com/irotoris/items/944aba5e448a8e723ff6
https://dev.classmethod.jp/cloud/aws/ecs-resources-knowledge-for-ecs-with-ec2/
https://qiita.com/naomichi-y/items/d933867127f27524686a