Dockerでコンテナを起動する際に、次のようにcpu-sharesとmemory-limitを指定することができます。
# docker run -c 256 -m 512m hogehoge
これは内部的にはcgroupsを使っていますが、RHEL7のDockerでは、systemdと連携してcgroupsの制御を行っています。この辺りの解説です。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/
# 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/
# 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のさらなるインテグレーションに期待しておきましょう。