zaki work log

作業ログやら生活ログやらなんやら

[Docker] コンテナで動かすプロセスのCPU使用率を--cpusで、メモリ使用量を--memoryで制限する

あくまで一例です。
開発環境とかで一時的に使うのでなく、サービスとして永続的に使う場合は、ホストのリソースを食い尽くさないように制限をかけると良い。

※ ログサイズについてはこちら

zaki-hmkc.hatenablog.com

CPU

制限無し

[zaki@cloud-dev ~]$ docker run --rm -it centos:7 bash
[root@fb3e8ba23a4d /]# 
top - 20:00:32 up 6 days, 11:09,  4 users,  load average: 0.02, 0.05, 0.05
Tasks: 334 total,   1 running, 333 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.1 us,  0.1 sy,  0.0 ni, 99.8 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  7990068 total,   334524 free,  4077584 used,  3577960 buff/cache
KiB Swap:  3670012 total,  3663100 free,     6912 used.  3565080 avail Mem

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 32689 zaki      20   0  162256   2480   1576 R   0.7  0.0   0:00.08 top
     1 root      20   0  193880   6468   3812 S   0.0  0.1   0:05.94 systemd
     2 root      20   0       0      0      0 S   0.0  0.0   0:00.05 kthreadd
     4 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/0:0H
     6 root      20   0       0      0      0 S   0.0  0.0   0:10.00 ksoftirqd/0

こんな感じ。

ここで負荷試験ツールである(負荷試験ツールでは無い)yesを実行する。(/dev/nullに捨てるのを忘れずに)

[root@fb3e8ba23a4d /]# yes > /dev/null

topはこんな感じ。(CPUは1押下してコア毎の表示)

top - 20:08:41 up 6 days, 11:17,  4 users,  load average: 0.08, 0.09, 0.07
Tasks: 336 total,   2 running, 334 sleeping,   0 stopped,   0 zombie
%Cpu0  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  :  0.3 us,  0.0 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu2  : 98.7 us,  1.3 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu3  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  7990068 total,   232872 free,  4103872 used,  3653324 buff/cache
KiB Swap:  3670012 total,  3663100 free,     6912 used.  3538792 avail Mem

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 33045 root      20   0    4368    356    280 R 100.0  0.0   0:06.16 yes
 32689 zaki      20   0  162256   2480   1576 R   0.3  0.0   0:01.10 top
     1 root      20   0  193880   6468   3812 S   0.0  0.1   0:05.98 systemd
     2 root      20   0       0      0      0 S   0.0  0.0   0:00.05 kthreadd
     4 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/0:0H
     6 root      20   0       0      0      0 S   0.0  0.0   0:10.01 ksoftirqd/0
     7 root      rt   0       0      0      0 S   0.0  0.0   0:00.49 migration/0
     8 root      20   0       0      0      0 S   0.0  0.0   0:00.00 rcu_bh
     9 root      20   0       0      0      0 S   0.0  0.0   1:01.89 rcu_sched

この通り、100%使い切っている。

制限あり

docker runのオプションの--cpuを使用する。

      --cpus decimal                   Number of CPUs

例として30%制限で実行してみる。
追加するオプションは--cpus 0.3

docker run --cpus 0.3 --rm -it centos:7 bash

[zaki@cloud-dev ~]$ docker run --cpus 0.3 --rm -it centos:7 bash
[root@9d2afc703344 /]#

これで同じように負荷試験ツール(負荷試験ツールではない)を実行する。

[root@9d2afc703344 /]# yes > /dev/null
top - 20:12:57 up 6 days, 11:21,  4 users,  load average: 0.01, 0.07, 0.07
Tasks: 334 total,   2 running, 332 sleeping,   0 stopped,   0 zombie
%Cpu0  : 29.8 us,  0.7 sy,  0.0 ni, 69.5 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  :  0.0 us,  0.3 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu2  :  0.3 us,  0.3 sy,  0.0 ni, 99.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu3  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  7990068 total,   233236 free,  4103396 used,  3653436 buff/cache
KiB Swap:  3670012 total,  3663100 free,     6912 used.  3539264 avail Mem

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 33182 root      20   0    4368    352    280 R  29.9  0.0   0:03.66 yes
  1054 root      20   0  902308  46228  13440 S   0.3  0.6   8:48.25 containerd
 22511 zaki      20   0 1248016  72048  16856 S   0.3  0.9   2:45.80 node
 33054 zaki      20   0  162256   2476   1576 R   0.3  0.0   0:00.20 top
     1 root      20   0  193880   6500   3844 S   0.0  0.1   0:05.99 systemd
     2 root      20   0       0      0      0 S   0.0  0.0   0:00.05 kthreadd
     4 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/0:0H

この通り、CPU使用率30%に制限できました。

メモリ

制限無し

[root@4f061ab0d609 /]# /dev/null < $(yes)
top - 20:32:15 up 6 days, 11:41,  5 users,  load average: 0.54, 0.20, 0.10
Tasks: 341 total,   3 running, 338 sleeping,   0 stopped,   0 zombie
%Cpu(s): 27.1 us, 14.5 sy,  0.0 ni, 58.4 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  7990068 total,   135848 free,  6148588 used,  1705632 buff/cache
KiB Swap:  3670012 total,  3650556 free,    19456 used.  1494248 avail Mem

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 33771 root      20   0 2091740   2.0g    472 R  97.0 26.0   0:17.83 bash
 33772 root      20   0    4368    352    280 R  69.1  0.0   0:12.94 yes
    45 root      20   0       0      0      0 S   0.7  0.0   0:03.09 kswapd0
 33416 zaki      20   0  162256   2472   1576 R   0.3  0.0   0:00.55 top
     1 root      20   0  193880   6168   3512 S   0.0  0.1   0:06.07 systemd
     2 root      20   0       0      0      0 S   0.0  0.0   0:00.05 kthreadd
     4 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/0:0H
root      33715  0.0  0.0 107560  3240 ?        Sl   20:31   0:00  \_ containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/4f061ab0d60936997b8f90fd2155e3ca4843036f557ef6efb60967e2350a8268 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc -systemd-cgroup
root      33733  0.1  0.0  11828  1904 pts/0    Ss   20:31   0:00      \_ bash
root      33771 98.1 17.4 1403428 1392432 pts/0 R+   20:31   0:11          \_ bash
root      33772 71.2  0.0   4368   352 pts/0    R    20:31   0:08              \_ yes
[root@4f061ab0d609 /]# /dev/null < $(yes)
bash: xrealloc: cannot allocate 18446744071562067968 bytes (331776 bytes allocated)
[root@4f061ab0d609 /]#

2GBの時点でメモリアロケートに失敗して停止した。(コンテナではなくyesを実行しているプロセスが)
この制限は未調査。

ulimitで見える範囲ではとくに制限はないんだけど。

[zaki@cloud-dev ~]$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 31118
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 4096
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

追記: 2GB仕様について

2GB制限はおそらく確認に使用した/dev/null < $(yes)で使用されるbashの制限か仕様。
Cで「mallo(3)をforループでぶん回す」プログラムを書いてコンテナで動かすと、特に制限なく動作した。

制限あり

--memoryで制限を設定できる。

  -m, --memory bytes                   Memory limit

例として512MBで制限を掛けてみる。

[zaki@cloud-dev ~]$ docker run -m 512m --rm -it centos:7 bash
[root@e9787badb7c0 /]#
[zaki@cloud-dev ~]$ top
top - 20:38:04 up 6 days, 11:46,  5 users,  load average: 0.61, 0.28, 0.15
Tasks: 339 total,   2 running, 337 sleeping,   0 stopped,   0 zombie
%Cpu(s): 25.2 us, 15.6 sy,  0.0 ni, 58.4 id,  0.0 wa,  0.0 hi,  0.8 si,  0.0 st
KiB Mem :  7990068 total,  1693808 free,  4591392 used,  1704868 buff/cache
KiB Swap:  3670012 total,  3197180 free,   472832 used.  3050796 avail Mem

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 34133 root      20   0  985940 521512    472 R  98.3  6.5   0:08.44 bash
 34134 root      20   0    4368    280    280 S  64.8  0.0   0:05.75 yes
 34042 zaki      20   0  162256   2484   1576 R   0.3  0.0   0:00.09 top
 57182 root      20   0       0      0      0 S   0.3  0.0   0:39.54 kworker/0:1
     1 root      20   0  193880   6172   3516 S   0.0  0.1   0:06.11 systemd

この辺までは負荷がかかるが、RESの数値の通り512MBあたりで頭打ち。
そのまま停止する。

[root@18fd18524341 /]# /dev/null < $(yes)
Killed

お、出力が違う。。

出力メッセージの違いはbash周りの内部処理に依存していて詳細は未確認だが、設定している512MB程度以上のリソースを使用することはできないようになる。
(制限を超えたメモリをallocateしようとしてエラーになったらクラッシュするようになっているのだろう)

その他のオプション

--oom-kill-disableでOOMを無効にしたり、--memory-swapでswap込みのメモリ量を設定もできる模様。
詳しくはドキュメント参照。

参考

docs.docker.com

knowledge.sakura.ad.jp

環境

[zaki@cloud-dev ~]$ docker version
Client: Docker Engine - Community
 Version:           19.03.12
 API version:       1.40
 Go version:        go1.13.10
 Git commit:        48a66213fe
 Built:             Mon Jun 22 15:46:54 2020
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.12
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.13.10
  Git commit:       48a66213fe
  Built:            Mon Jun 22 15:45:28 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.2.13
  GitCommit:        7ad184331fa3e55e52b890ea95e65ba581ae3429
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
[zaki@cloud-dev ~]$ uname -r
3.10.0-1127.18.2.el7.x86_64
[zaki@cloud-dev ~]$ cat /etc/redhat-release
CentOS Linux release 7.8.2003 (Core)

制限という意味では、あとログファイルサイズも制限しておかないと、あふれる。(訳:あふれた)


追記: docker statsを使ったコンテナ毎のリソース使用状況

コンテナ毎のリソース使用状況はdocker statsでも見れる。(知らなかった!)

f:id:zaki-hmkc:20200908215651p:plain

topより断然見やすいです(笑)


追記: inspectで確認

docker inspectで制限をかけているコンテナを確認すると、HostConfig以下のMemoryNanoCpusに指定した数値が設定される。
docker run --cpus 0.3 -m 512m --rm -it centos:7 bashした場合は以下の通り。

        "HostConfig": {
            [...]
            "Memory": 536870912,
            "NanoCpus": 300000000,

制限を行っていない場合は、どちらも0になる。