Dockerのリソースの制限（CPU/メモリ）とCPUの動きについてまとめ

#Dockerリソースの制限について

##環境情報

* Red Hat Enterprise Linux 7.1
* Docker 1.6.2.el7

##リソース管理を実現する機能

### cgroups
***
Linuxの標準機能cgroupsを使用して、リソース管理を実現しています。詳しくは以下のリンクなどをご覧ください。

* [Control Groups cgroups](http://enakai00.hatenablog.com/entry/20110605/1307240161)


## *CPU*

### *--cpuset-cpus*
***
CPUの割り当てを決定するオプションです

* --cpuset-cpus 0　　#CPUコア0を使用
* --cpuset-cpus 0-2　#CPUコア0～2を使用
* --cpuset-cpus 1,2　#CPUコア1と2を使用

`/sys/fs/cgroup/cpuset/system.slice/docker-xxx.scope/cpuset.cpus`　に割り当てられたCPU情報が管理されています。
 
　

```:CPUの割当数
[root@master]# cat /sys/fs/cgroup/cpuset/system.slice/docker-xxx.scope/cpuset.cpus
0-1
```

　
 
#### CPUが故障した場合の動作を確認してみる
***
CPUを2ソケット搭載した仮想サーバを用意し、`--cpuset-cpus 1`で2つ目のCPUにアサイン。
ログインもcurlも可能。

```
[root@master ~]# docker run -d -p 80 --cpuset-cpus 1 apache-plugin:2.2.9

[root@master ~]# cat /sys/fs/cgroup/cpuset/system.slice/docker-xxx.scope/cpuset.cpus
1

[root@master ~]# docker exec -it xxx /bin/bash
root@xxx:/usr/local/apache2# exit

[root@master ~]# curl 0.0.0.0:32768
<HTML><HEAD><TITLE>Weblogic Bridge Message</TITLE></HEAD> <BODY><H2>Failure of Web Server bridge:</H2><P><hr><PRE>Invalid configuration details. Cannot continue.</PRE><P>
<hr> </BODY></HTML>
```

　

* cpu1を停止


```
[root@master ~]# echo 0 > /sys/devices/system/cpu/cpu1/online
```

すると、`cpuset.cpus`の表示が消え、`curl`は帰ってくるのに、`docker exec`は失敗する

```
[root@master ~]# cat /sys/fs/cgroup/cpuset/system.slice/docker-57a0bbf56f71a022c2b8a2b4bac5d381cb3115ea87fcae1cb8b2b3f2f1402e5e.scope/cpuset.cpus


[root@master ~]# docker exec -it xxx /bin/bash
Cannot run exec command xxx in container xxx: [8] System error: write /sys/fs/cgroup/cpuset/system.slice/docker-xxx.scope/cgroup.procs: no space left on device
                        Error starting exec command in container xxx: Cannot run exec command xxx in container xxx: [8] System error: write /sys/fs/cgroup/cpuset/system.slice/docker-xxx.scope/cgroup.procs: no space left on device

#なぜかcurlは成功する
[root@master ~]# curl 0.0.0.0:32768
<HTML><HEAD><TITLE>Weblogic Bridge Message</TITLE></HEAD> <BODY><H2>Failure of Web Server bridge:</H2><P><hr><PRE>Invalid configuration details. Cannot continue.</PRE><P>
<hr> </BODY></HTML>
```
　

* どういうことか検証してみる

起動したdockerコンテナ上で`yes`を実行します。`top`で確認するとこんな感じ。Pが使用しているCPU番号です、1番になってますね。

```
PID  USER  PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+   COMMAND  P
7221 root  20   0    4220    348    272 R 100.0  0.0     6:18.70 yes      1
```
　

* CPU1番を停止


```
[root@master ~]# echo 0 > /sys/devices/system/cpu/cpu1/online
```

　
なんと、Pが0になりました。つまり、`yes`プロセスはCPU0番で通信します。

```
PID  USER  PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+   COMMAND  P
7221 root  20   0    4220    348    272 R 100.0  0.0     6:18.70 yes      0
```

先ほど`curl`が成功したのはこれが理由ですね。

Dockerのパラメータで指定しても、障害時にはCPUがスケールするようです。ただし、`docker exec`や`docker restart`などdocker経由のコマンドについては弾かれましたので注意が必要ですね。

　

#### 復旧方法

```
#CPUをOnlineにします
[root@master ~]# echo 1 > /sys/devices/system/cpu/cpu1/online

#使用可能なCPUを指定します
[root@master ~]# echo 0-1 > /sys/fs/cgroup/cpuset/system.slice/cpuset.cpus

#コンテナに割り当てるCPUをセットします
[root@master ~]# echo 1 >  /sys/fs/cgroup/cpuset/system.slice/docker-xxx.scope/cpuset.cpus
```

万が一割り当てているCPUが故障した場合は、対象のファイルにechoするとそちらのCPUを参照するようになります。

```
[root@master ~]# echo [割り当てたいCPU番号] >  /sys/fs/cgroup/cpuset/system.slice/docker-xxx.scope/cpuset.cpus
```

### *-c,--cpu-shares* 
***
CPU使用の優先度を決定するオプションです（デフォルトは1024）

* -c 2048　（通常の2倍の優先度）
* --cpu-shares=0

デフォルトの設定では、どのコンテナも同一の設定（1024）が付与されているためCPU割り当ての優先度は同一です。

公式のmanでは以下のように記載されていました。
>コンテナが3つあり、それぞれの優先度が1024,512,512と割り当てられていた場合には、CPUの割り当てが50%,25%,25%になります。ここに優先度1024のコンテナを追加すると、33%,16.5%,16.5%,33%となります。


なお、設定したCPUの優先度情報はsystemd（RHEL7の場合）にて管理されています。

```:CPUの優先度
[root@master system.slice]# systemctl show docker-ad712da4d702e4f8307def0f9b160a556565c2d8dea80ecc27160c19297d06ba.scope --property=CPUShares
CPUShares=1024
```

もしくは、こちらで確認。

```:CPUの優先度
[root@master]# cat /sys/fs/cgroup/cpuset/system.slice/docker-xxx.scope/cpuset.shares
2048
```

##メモリ

### *-m,--memory*
***
メモリ使用量を決定するオプションです（b,k,m,gが使用可能）

* -m 128b
* -m 128k
* -m 128m
* -m 128G
* --memory="128g"

-m 128mとした場合に使用できるメモリサイズは128M。このときのSwapのサイズも同じく128Mとなる。

これらの合計256Mを使い切るとLinux機能である「OOM Killer」によってコンテナ内のプロセスが強制停止させられる。

* [OOM Killer](http://qiita.com/konpyu/items/20d1989d1251d805cf3b)の詳細はこちら。

なお、設定したメモリ情報はsystemd（RHEL7の場合）にて管理されています。

```:メモリの使用量上限
[root@master system.slice]# systemctl show docker-ad712da4d702e4f8307def0f9b160a556565c2d8dea80ecc27160c19297d06ba.scope --property=MemoryLimit
MemoryLimit=18446744073709551615
```

もしくは、こちらで確認。

```:メモリの使用量上限
[root@master]# cat /sys/fs/cgroup/memory/system.slice/docker-xxx.scope/memory.limit_in_bytes
9223372036854775807
```

```:メモリ＋Swapの使用量上限
[root@master]# cat /sys/fs/cgroup/memory/system.slice/docker-xxx.scope/memory.memsw.limit_in_bytes
9223372036854775807
```

### *--memory-swap* 
***
メモリ＋Swapサイズを決定するオプションです（b,k,m,gが使用可能）

* --memory-swap="128b"
* --memory-swap="128k"
* --memory-swap="128m"
* --memory-swap="128g"
* --memory-swap="-1"

~~-1を設定するとSwapを無効にすることが出来ます。このオプションは`-m`で指定した値よりも大きくなるため（Swapを使用する場合）、`-m`と組み合わせて使用しましょう。~~

（追記）
-1を設定するとswapを無制限で使用できるようになります。

※古いバージョンのマニュアルやmanにはswapの制限無効化とありますが、ドキュメントの誤りだったようです。
https://github.com/docker/docker/issues/18894