めもめも

このブログに記載の内容は個人の見解であり、必ずしも所属組織の立場、戦略、意見を代表するものではありません。

RHEL7のDockerとsystemdとcgroups

Dockerでコンテナを起動する際に、次のようにcpu-sharesとmemory-limitを指定することができます。

# docker run -c 256 -m 512m hogehoge

これは内部的にはcgroupsを使っていますが、RHEL7のDockerでは、systemdと連携してcgroupsの制御を行っています。この辺りの解説です。cgroupsそのもの説明は下記を参照下さい。

Control Groups (cgroups)

コンテナから生成されるUnit

まず、テスト用にContOS6のコンテナを起動して、中でtopコマンドでも実行しておきます。

# docker run -it -c 256 -m 512m centos /bin/bash
bash-4.1# top

別の端末からログインして、コンテナIDを確認します。

# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
d572763fd9f7        centos:centos6      /bin/bash           19 seconds ago      Up 18 seconds                           evil_yonath 

この時、systemdの稼働中のUnitで、「docker-<コンテナID>.scope」という名前のUnitができています。

# systemctl --full | grep docker-d572763fd9f7
docker-d572763fd9f7aec8fdfa04225f93c3ecc2223a721f278a648e294b0a1efef993.scope                          loaded active running   docker container d572763fd9f7aec8fdfa04225f93c3ecc2223a721f278a648e294b0a1efef993

Dockerはコンテナを起動する際に、このUnitを作成した上で、cgroupsの制御をUnitの設定として追加しています。これにより、cgroupsの制御は、systemdが面倒を見てくれます。

Unit名が長いので変数「unitname」にセットしておいて、まずは、Unitのstatusを確認します。

# unitname=docker-d572763fd9f7aec8fdfa04225f93c3ecc2223a721f278a648e294b0a1efef993.scope
# systemctl status $unitname -l
docker-d572763fd9f7aec8fdfa04225f93c3ecc2223a721f278a648e294b0a1efef993.scope - docker container d572763fd9f7aec8fdfa04225f93c3ecc2223a721f278a648e294b0a1efef993
   Loaded: loaded (/run/systemd/system/docker-d572763fd9f7aec8fdfa04225f93c3ecc2223a721f278a648e294b0a1efef993.scope; static)
  Drop-In: /run/systemd/system/docker-d572763fd9f7aec8fdfa04225f93c3ecc2223a721f278a648e294b0a1efef993.scope.d
           └─90-CPUShares.conf, 90-Description.conf, 90-DeviceAllow.conf, 90-DevicePolicy.conf, 90-MemoryLimit.conf, 90-Slice.conf
   Active: active (running) since 日 2014-04-27 14:14:10 JST; 3min 32s ago
   CGroup: /system.slice/docker-d572763fd9f7aec8fdfa04225f93c3ecc2223a721f278a648e294b0a1efef993.scope
           ├─2385 /bin/bash
           └─2405 top

 4月 27 14:14:10 rhel7rc2 systemd[1]: Started docker container d572763fd9f7aec8fdfa04225f93c3ecc2223a721f278a648e294b0a1efef993.

コンテナ内のプロセス情報が丸見えですね。また、「/run/systemd/system/.d/」以下に追加のUnit設定があります。これらが、cgroupsの設定になります。

# head -100 /run/systemd/system/$unitname.d/*
==> /run/systemd/system/docker-d572763fd9f7aec8fdfa04225f93c3ecc2223a721f278a648e294b0a1efef993.scope.d/90-CPUShares.conf <==
[Scope]
CPUShares=256

==> /run/systemd/system/docker-d572763fd9f7aec8fdfa04225f93c3ecc2223a721f278a648e294b0a1efef993.scope.d/90-Description.conf <==
[Unit]
Description=docker container d572763fd9f7aec8fdfa04225f93c3ecc2223a721f278a648e294b0a1efef993

==> /run/systemd/system/docker-d572763fd9f7aec8fdfa04225f93c3ecc2223a721f278a648e294b0a1efef993.scope.d/90-DeviceAllow.conf <==
[Scope]
DeviceAllow=
DeviceAllow=/dev/pts/ptmx rwm
DeviceAllow=/dev/tty1 rwm
DeviceAllow=/dev/tty0 rwm
DeviceAllow=/dev/console rwm
DeviceAllow=/dev/tty rwm
DeviceAllow=/dev/urandom rwm
DeviceAllow=/dev/random rwm
DeviceAllow=/dev/full rwm
DeviceAllow=/dev/zero rwm
DeviceAllow=/dev/null rwm

==> /run/systemd/system/docker-d572763fd9f7aec8fdfa04225f93c3ecc2223a721f278a648e294b0a1efef993.scope.d/90-DevicePolicy.conf <==
[Scope]
DevicePolicy=strict

==> /run/systemd/system/docker-d572763fd9f7aec8fdfa04225f93c3ecc2223a721f278a648e294b0a1efef993.scope.d/90-MemoryLimit.conf <==
[Scope]
MemoryLimit=536870912

==> /run/systemd/system/docker-d572763fd9f7aec8fdfa04225f93c3ecc2223a721f278a648e294b0a1efef993.scope.d/90-Slice.conf <==
[Scope]
Slice=system.slice

CPUShares、MemoryLimit意外に、デバイスファイル「/dev/*」へのアクセス制限もcgroupsで行っていることが分かります。

Unitの実行時パラメータからも同じ情報が確認できます。

# systemctl show $unitname | grep -E "(CPUShares=|MemoryLimit=)"
CPUShares=512
MemoryLimit=536870912

また、systemdが管理しているcgroupsの情報は次のコマンドで確認できます。

# systemd-cgls
├─1 /usr/lib/systemd/systemd --switched-root --system --deserialize 23
├─user.slice
│ └─user-0.slice
│   ├─session-3.scope
│   │ ├─1980 sshd: root@pts/2
│   │ ├─1984 -bash
│   │ ├─2429 systemd-cgls
│   │ └─2430 head -20
│   └─session-2.scope
│     ├─1900 sshd: root@pts/0
│     ├─1904 -bash
│     └─2365 docker run -it -c 256 -m 512m centos /bin/bash
└─system.slice
  ├─docker-d572763fd9f7aec8fdfa04225f93c3ecc2223a721f278a648e294b0a1efef993.scope
  │ ├─2385 /bin/bash
  │ └─2405 top
...

「system.slice/」というグループがこのコンテナ用のグループの様です。次のように、cpu.sharesとmemory.limit_in_bytesに指定の値がセットされていることが分かります。

# cat /sys/fs/cgroup/cpu/system.slice/$unitname/cpu.shares 
256
# cat /sys/fs/cgroup/memory/system.slice/$unitname/memory.limit_in_bytes 
536870912

次のように、実行中に上書き変更することも可能です。

# echo "512" > /sys/fs/cgroup/cpu/system.slice/$unitname/cpu.shares
# cat /sys/fs/cgroup/cpu/system.slice/$unitname/cpu.shares 
512

が、次のように、systemdのインターフェースを通して変更する方がお行儀がよいでしょう。

# systemctl set-property $unitname CPUShares=512 --runtime
# systemctl show  $unitname | grep -E "(CPUShares=|MemoryLimit=)"
CPUShares=512
MemoryLimit=536870912

cgroupsで追加のリソース制御を行う

先の参考資料にあるように、cgroupsを駆使するともっと色々なリソース制御が可能です。たとえば、「cpusets」を使うとコンテナに割り当てるCPUコアを制限することも可能です。

はい。可能ならやってみましょう。

まず、「cpusets」コントローラーについて、このUnitに対応するグループは作られていないようなので・・・

# ls /sys/fs/cgroup/cpuset/system.slice/
ls: /sys/fs/cgroup/cpuset/system.slice/ にアクセスできません: そのようなファイルやディレクトリはありません

次のコマンドで作ります。

# if [[ ! -d /sys/fs/cgroup/cpuset/system.slice ]]; then
    mkdir /sys/fs/cgroup/cpuset/system.slice
    cat /sys/fs/cgroup/cpuset/cpuset.mems > /sys/fs/cgroup/cpuset/system.slice/cpuset.mems
    cat /sys/fs/cgroup/cpuset/cpuset.cpus > /sys/fs/cgroup/cpuset/system.slice/cpuset.cpus
fi
# mkdir -p /sys/fs/cgroup/cpuset/system.slice/$unitname
# cat /sys/fs/cgroup/cpuset/cpuset.mems > /sys/fs/cgroup/cpuset/system.slice/$unitname/cpuset.mems
# cat /sys/fs/cgroup/cpuset/cpuset.cpus > /sys/fs/cgroup/cpuset/system.slice/$unitname/cpuset.cpus
# for p in $(cat /sys/fs/cgroup/systemd/system.slice/$unitname/cgroup.procs);do echo $p > /sys/fs/cgroup/cpuset/system.slice/$unitname/tasks; done

ここで、コンテナ内でCPUをぶん回すコマンドを流して、topで観察しておきます。

bash-4.1# cat /dev/zero > /dev/null & top
top - 05:36:09 up  1:42,  0 users,  load average: 0.29, 0.08, 0.07
Tasks:   3 total,   2 running,   1 sleeping,   0 stopped,   0 zombie
Cpu0  :  2.7%us, 97.3%sy,  0.0%ni,  0.0%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
Mem:   1017796k total,   268628k used,   749168k free,     2952k buffers
Swap:  1679356k total,        0k used,  1679356k free,   130164k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND            
    7 root      20   0  4148  316  252 R 99.9  0.0   0:21.08 cat                
    1 root      20   0 11480 1620 1272 S  0.0  0.2   0:00.01 bash               
    8 root      20   0 14948 1076  876 R  0.0  0.1   0:00.00 top            

この例では、Cpu0が使われています。次のコマンドを叩くと、コンテナには、Cpu1のみが割り当てられます。

# echo "1" > /sys/fs/cgroup/cpuset/system.slice/$unitname/cpuset.cpus

topコマンドを見るとCpu1が使われていることが分かります。

top - 05:37:54 up  1:44,  0 users,  load average: 0.88, 0.35, 0.16
Tasks:   3 total,   2 running,   1 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  :  2.3%us, 97.7%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   1017796k total,   268876k used,   748920k free,     2952k buffers
Swap:  1679356k total,        0k used,  1679356k free,   130360k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND            
    7 root      20   0  4148  316  252 R 99.9  0.0   2:06.15 cat                
    1 root      20   0 11480 1620 1272 S  0.0  0.2   0:00.01 bash               
    8 root      20   0 14948 1076  876 R  0.0  0.1   0:00.00 top     

まとめ

最後の例では、/sys/fs/cgroupsを直接操作しましたが、systemdとcgroupsのインテグレーションが進むと、この辺りもsystemdを経由してコントロールできる可能性があります。Dockerとsystemdとcgroupsのさらなるインテグレーションに期待しておきましょう。