docker-compose におけるメモリ使用量の制限方法

Written by @ryysud

Aug 29, 2018 23:36 · 1696 words · 4 minutes read #docker

まえおき

Docker を利用して、アプリケーションをコンテナとして稼働させていたのですが、コンテナのメモリ使用率が高まり OS がハングアップしてしまう事象が発生しました。その際の対応として docker-compose.yml でのメモリ使用量の制限を行ったので、その設定を備忘録的に残しておきます。

Docker のバージョン

Docker for Mac の 18.06.0-ce を利用していきます。

$ docker version
Client:
 Version:           18.06.0-ce
 API version:       1.38
 Go version:        go1.10.3
 Git commit:        0ffa825
 Built:             Wed Jul 18 19:05:26 2018
 OS/Arch:           darwin/amd64
 Experimental:      false

Server:
 Engine:
  Version:          18.06.0-ce
  API version:      1.38 (minimum version 1.12)
  Go version:       go1.10.3
  Git commit:       0ffa825
  Built:            Wed Jul 18 19:13:46 2018
  OS/Arch:          linux/amd64
  Experimental:     true

docker-compose は v1.22.0 となります。

$ docker-compose -v
docker-compose version 1.22.0, build f46880f

Docker Engine が利用可能なメモリは、デフォルト値の 2.0GiB としておきます。画質が悪い…。

docker-available-mem.png

指定しないとどうなるか?

Docker Engine に割り当てられたメモリを制限なく使えるだけ使う形となる

以下のような docker-compose.yml を用意します。

version: '2'

services:
  nginx1:
    image: nginx:alpine
    container_name: my-nginx1
    ports:
      - "8080:80"
  nginx2:
    image: nginx:alpine
    container_name: my-nginx2
    ports:
      - "8081:80"

上記の docker-compose.yml を実行して、コンテナが利用可能なメモリを確認すると MEM LIMIT が 2GiB なのがわかります。各コンテナで Docker Engine に割り当てられたメモリを制限なく使えるだけ使う形となり、複数コンテナでメモリ使用率がスパイクした際には OS のメモリを食いつぶす形となってしまいます…。

$ docker-compose up -d

$ docker stats --no-stream my-nginx1
CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
229fd8a7137f        my-nginx1           0.00%               1.812MiB / 1.952GiB   0.09%               1.08kB / 0B         0B / 0B             2

$ docker stats --no-stream my-nginx2
CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
79036756b110        my-nginx2           0.00%               1.777MiB / 1.952GiB   0.09%               1.15kB / 0B         0B / 0B             2

ということで制限をかけることが非常に重要!

Compose file version 2 での指定方法

mem_limit でメモリの制限が可能となる

mem_limit には byte単位 で数値を設定することが可能です。
※ 1m や 1g のように MB/GB 単位でも OK
※ 内部実装で integer 型で制御しており少数点で刻むとエラーになるのでご注意を

version: '2'

services:
  nginx:
    image: nginx:alpine
    container_name: my-nginx
    ports:
      - "8080:80"
    mem_limit: 1g

上記の docker-compose.yml を実行して、コンテナの MEM LIMIT を確認してみると、設定どおりの 1GiB が適用されていることがわかります。

$ docker-compose up -d

$ docker stats --no-stream my-nginx
CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT   MEM %               NET I/O             BLOCK I/O           PIDS
e314966600da        my-nginx            0.00%               1.859MiB / 1GiB     0.18%               688B / 0B           0B / 0B             2

Compose file version 3 での指定方法

結論から言うと Compose file version 3 は Swarm mode での利用を想定しているため mem_limit を指定することでのメモリ使用量の制限は実現できない

※ 注意:ここからは自分の拙い英語力での解釈なので間違った解釈をしている可能性があります

How to specify Memory & CPU limit in version 3 #4513 で結構な議論がなされていて、この issue を眺めれば一連の流れを掴むことが出来ます。「Compose file version 3 は Swarm mode での利用を想定したものなので、mem_limit などを利用する際には Compose file version 2 を使用してください。」と主張する maintainer? と「互換性がないのはあれかな」という利用者の意見が衝突しています…。

The v3 format is specifically designed to run with Swarm mode and the docker stack features.

多くの意見が飛び交った末に docker-compose#5684 で maintainer? が Compose file version 3 を Compose file version 2 のように稼働させるオプション –compatibility を実装して issue はクローズとなります。

ここからは実際に Compose file version 3 形式の docker-compose.yml を用意して挙動を確認していきます

Compose file version 3 で mem_limit でメモリの制限は不可能。

# version を 2 から 3 に変更
version: '3'

services:
  nginx:
    image: nginx:alpine
    container_name: my-nginx
    ports:
      - "8080:80"
    mem_limit: 1g

上記の docker-compose.yml を実行するとエラーとなる…。

$ docker-compose up -d
ERROR: The Compose file './docker-compose.yml' is invalid because:
Unsupported config option for services.nginx: 'mem_limit'

Swarm mode で使うための deploy options を利用してメモリの制限を行う。
https://docs.docker.com/compose/compose-file/#deploy

version: '3'

services:
  nginx:
    image: nginx:alpine
    container_name: my-nginx
    ports:
      - "8080:80"
    deploy:
      resources:
        limits:
          memory: 1g

上記の docker-compose.yml を実行すると、docker-compose では deploy はサポートしていない旨の WARN が出力され、結果的にメモリの制限は実現できない…。

$ docker-compose up -d
WARNING: Some services (nginx) use the 'deploy' key, which will be ignored. Compose does not support 'deploy' configuration - use `docker stack deploy` to deploy to a swarm.

$ docker stats --no-stream my-nginx
CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
fd5f6370dafa        my-nginx            0.00%               1.836MiB / 1.952GiB   0.09%               828B / 0B           0B / 0B             2

ここで –compatibility オプションの登場となります。–compatibility を使用することで Compose file version 3 フォーマットでも Compose file version 2 のように稼働させることが可能になります。

$ docker-compose --compatibility up -d

$ docker stats --no-stream my-nginx
CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT   MEM %               NET I/O             BLOCK I/O           PIDS
fffcee84aff4        my-nginx            0.00%               1.832MiB / 1GiB     0.18%               1.02kB / 0B         0B / 0B             2

但し –compatibility を本番では使うことは非推奨となっておりますのでご注意を!
https://docs.docker.com/compose/compose-file/compose-versioning/#compatibility-mode

まとめ

とりあえず docker-compose でメモリ使用量の制限をしたかったら Compose file version 2 で mem_limit を指定すれば OK!!!

mem_limit を利用して docker-compose におけるメモリ使用量の制限方法を理解することが出来ました。副産物として Compose file version の設計思想を少しでも理解できたのは良かったなと思っています。(やる気が出れば)次回は CPU 周りの制限方法をまとめたいと思いますー。以上!