6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Dockerコンテナが消費するCPUリソース制限の仕組み

Last updated at Posted at 2023-05-08

結論として、Dockerコンテナが消費するCPUリソースはホストマシンのcgroupによって制御されています。
今回はあえてcgroupの設定ファイルを直接操作することでDockerのリソース制御の仕組みを検証していきます。
簡略化のために、無限ループ処理するだけのプログラムをコンテナ内で動かした状態で検証を進めていきます。

検証に使用するコンテナの設定

FROM ruby:3.2.2-slim-bullseye

WORKDIR /usr/src/app

COPY . .
docker-compose.yml
version: "3.9"
services:
  ruby:
    build: .
    container_name: ruby
    volumes:
      - .:/usr/src/app
    tty: true
    stdin_open: true

検証

まずdocker-compose up -dでコンテナを立ち上げた状態で、docker-compose exec ruby bashでコンテナのシェルに入り、topでリソースを確認してみます。

$ top
(一部省略)
PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
  1 root      20   0  102396  23212   7328 S   0.0   0.2   0:00.15 irb
  7 root      20   0    6048   3756   3316 S   0.0   0.0   0:00.01 bash
 13 root      20   0    8880   3540   3072 R   0.0   0.0   0:00.00 top

当然CPUには十分空きがあります。
ではCPUリソースを消費するために、無限ループするだけの以下のプログラムを実行してみましょう。

test.rb
while true
end

ruby test.rb &でバックグラウンド実行した状態で再度topでリソースを確認すると以下のようにtest.rbがCPUを100%消費していることがわかります。

$ ruby test.rb &
# [1] 14

# PIDを指定して無限ループのプロセスだけ表示
$ top -p 14
PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
 14 root      20   0   95820  16408   6936 R 100.0   0.1   0:03.11 ruby

Dockerコンテナはデフォルトでは消費できるリソースに制限がかかっていないので、このように闇雲にCPUを消費することができてしまいます。

ではcgroupによってコンテナが消費できるCPUリソースを制限してみましょう。
まずは、当該コンテナがどのコントロールグループに属しているか、systemd-cgtopで確認します。

Control Group                                                                    Procs   %CPU   Memory  Input/s Output/s
/                                                                                    -  107.4     8.8G        -        -
/docker                                                                              -   99.7    27.5M        -        -
/docker/f1af8ea44304a30abe393bc4937e76de3fe8f8c749a8fea297620db709aaf3a8             -   99.7    26.7M        -        -
/podruntime                                                                          -    1.5     6.1G        -        -
/podruntime/docker                                                                   -    1.5     6.1G        -        -
/volume-contents                                                                     -    0.1    25.4M        -        -

~~~ (省略) ~~~

docker/f1af8ea44304a30abe393bc4937e76de3fe8f8c749a8fea297620db709aaf3a8というコントロールグループに属しているようなので、実際にcgroupの設定ファイルを確認しましょう。

$ cat /sys/fs/cgroup/cpu/docker/f1af8ea44304a30abe393bc4937e76de3fe8f8c749a8fea297620db709aaf3a8/cpu.cfs_period_us
# 100000

$ cat /sys/fs/cgroup/cpu/docker/f1af8ea44304a30abe393bc4937e76de3fe8f8c749a8fea297620db709aaf3a8/cpu.cfs_quota_us
# -1

以上の設定ファイルの内容の意味は、100ミリ秒あたり無制限にCPUを使えるという意味です。
(単位がマイクロ秒なので、100,000マイクロ秒 = 100ミリ秒となります。)

検証(リソース制限あり)

では、100ミリ秒あたり70ミリ秒までしか使えないように制限してみましょう。

$ echo 70000 | sudo tee  /sys/fs/cgroup/cpu/docker/f1af8ea44304a30abe393bc4937e76de3fe8f8c749a8fea297620db709aaf3a8/cpu.cfs_quota_us
# 70000

すると以下のように、無限ループを行っているプログラムのCPU使用率が約70%となりました。

$ top
PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
 14 root      20   0   95820  16408   6936 R  69.7   0.1   4:08.84 ruby

複数プロセスの場合

では無限ループプログラムをもう1つバックグラウンドで起動し、CPUコアを2つ使うようにするとどうなるでしょうか。

$ ruby test.rb &
# [2] 19

$ top
PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
 14 root      20   0   95820  16408   6936 R  31.4   0.1   6:59.76 ruby
 19 root      20   0   95804  16404   6936 R  38.0   0.1   0:19.69 ruby 

ruby test.rbのプロセスが2つ実行されており、合計でCPUを約70%使用しています。
3つ4つとプロセスを追加で実行しても、合計のCPU使用率は70%までに制限されます。
なぜなら、あるコンテナの中で複数のプロセスが実行されていても、Dockerホストのcgroupによってコンテナ単位でCPUリソースが制御されるからです。

2プロセスまではCPUを100%利用できるようにするには、コンテナが100ミリ秒あたり200ミリ秒CPUを利用できるようにcpu.cfs_quota_us200000を書き込みます。

$ echo 200000 | sudo tee /sys/fs/cgroup/cpu/docker/f1af8ea44304a30abe393bc4937e76de3fe8f8c749a8fea297620db709aaf3a8/c
pu.cfs_quota_us
# 200000
$ top
PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
 14 root      20   0   95820  16408   6936 R 100.0   0.1   9:05.90 ruby
 19 root      20   0   95804  16404   6936 R 100.0   0.1   2:25.83 ruby

各プロセスが100%CPUを利用できています。
100ミリ秒あたり200ミリ秒というと矛盾しているように感じますが、1プロセス100ミリ秒 * 2プロセス = 200ミリ秒と考えるとわかりやすいです。

この状態でdocker statsでコンテナのCPU使用率を確認すると合計で約200%となっています。

CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O       BLOCK I/O   PIDS
f1af8ea44304   ruby      199.52%    35.92MiB / 12.43GiB   0.28%     1.66kB / 0B   0B / 0B     3

リソース制御をDockerの仕組みを使って行う

cgroupによるコンテナのリソース制御を理解したところで、Dockerが提供している機能で同じことをより簡単に行ってみましょう。

docker-compose.yml
version: "3.9"
services:
  ruby:
    build: .
    container_name: ruby
    volumes:
      - .:/usr/src/app
    tty: true
    stdin_open: true
    cpu_quota: 70000 # この行を追加

ここまでの検証でこの意味は簡単に理解できますね。
CPUリソースの消費を100ミリ秒あたり70ミリ秒に制限するという意味です。

ではこの設定を反映した新しいコンテナを立ち上げて検証してみましょう。

$ docker-compose down
$ docker-compose up -d
$ docker-compose exec ruby bash
$ ruby test.rb &
# [1] 13

$ top -p 13
PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
 13 root      20   0   95812  16304   6840 R  69.7   0.1  14:36.17 ruby           

cgroupで直接操作した場合と同様にCPU使用率が約70%に制限されています。
実際にcgroupの設定ファイルにも反映されているか確認しましょう。

$ systemd-cgtop
Control Group                                                                    Procs   %CPU   Memory  Input/s Output/s
/                                                                                    -   70.9     8.8G        -        -
/docker                                                                              -   69.8    28.2M        -        -
/docker/f1f9e2f477364022641ecb5520aea8a7f2447e0b615c34f73b9363d1d15dfa3a             -   69.8    27.4M        -        -

$ cat /sys/fs/cgroup/cpu/docker/f1f9e2f477364022641ecb5520aea8a7f2447e0b615c34f73b9363d1d15dfa3a/cpu.cfs_quota_us
# 70000

cpu.cfs_quota_usに反映されています。
つまりDockerはリソース制御をcgroupで行っているということです。

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?