Docker Linux

systemdでのCPU制限方法~serviceファイルでの設定編

投稿日:

systemd に悩まされる皆様、進捗どうですか(?
標題の件、仕事で必要に迫られたため、備忘録として。

まえがき

systemdを使っている以上、リソース制御は誰しもが通る道である。しかし、基本的に日本語の文献では、systemctl setpropertyにより、Unit起動後に動的に制御しようと試みるものが多い。
サーバのような常時駆動しているシステムならば、最初の一回のみ手動で(いや、手動はやはりおかしいだろうが)コマンドを打てばよいが、組み込み等の再起動を繰り返すシステムについては、unitファイルの記載によりsystemd起動時に自動的に設定を反映させる必要がある。

海外の解説ページをあさりながら、systemctlコマンドではなくunitファイルの記載によりリソース制御を行う方法について、実際に動作させて動きを確認し、unitファイルの有効な記載を見つけていく。

なお、unitファイルの記載内容についてはsystemdのversionに大いに依存することを留意いただきたい。

動作環境

docker上でsystemdが動く環境を構築した。
Dockerfile及びdocker-compose.ymlをgithubに上げたので、詳しくはそちらを参照されたし。

[CPU]
processor       : 0
model name      : Intel(R) Atom(TM) CPU  C2750  @ 2.40GHz
processor       : 1
model name      : Intel(R) Atom(TM) CPU  C2750  @ 2.40GHz

[MEMORY]
MemTotal:        2049756 kB

[KERNEL]
Linux hostname 4.4.88-mainline-rev1 #1 SMP Wed Sep 13 23:49:03 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

[DISTRIBUTION]
Ubuntu 16.04.1 LTS \n \l

[LANGUAGES]
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609
Python 2.7.12
Python 3.5.2
GNU Make 4.1
cmake version 3.5.1
Docker version 17.09.0-ce, build afdb6d4
# systemctl --version
systemd 229
+PAM +AUDIT +SELINUX +IMA +APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ -LZ4 +SECCOMP +BLKID +ELFUTILS +KMOD -IDN

 

確認内容

systemdから起動するserviceのデフォルトのsliceの設定

まず、systemdから普通に起動する場合にどのようなsliceにひも付きどうリソース管理されるか調べる。
下記serviceファイルを作成した。

root@24d73e4b6423:/# cat /etc/systemd/system/stress1.service
[Unit]
Description="Stress 1"

[Service]
ExecStart=/bin/dd if=/dev/zero of=/dev/null
Type=simple

[Install]
WantedBy=multi-user.target
root@24d73e4b6423:/# cat /etc/systemd/system/stress2.service
[Unit]
Description="Stress 2"

[Service]
ExecStart=/bin/dd if=/dev/zero of=/dev/null
Type=simple

[Install]
WantedBy=multi-user.target

 

これらを起動し、systemctl statusを確認すると下記のようになる。

root@24d73e4b6423:/# systemctl start stress1
root@24d73e4b6423:/# systemctl start stress2
root@24d73e4b6423:/# systemctl status
● 24d73e4b6423
State: running
Jobs: 0 queued
Failed: 0 units
Since: Mon 2019-07-15 07:34:03 UTC; 3 months 25 days ago
CGroup: /docker/24d73e4b6423a157af025a752cd0363efec382af152dacd501dc437941dd943f
├─ 70 /bin/bash
├─444 /bin/bash
├─462 systemctl status
├─463 systemctl status
├─init.scope
│ └─1 /sbin/init noplymouth nosplash verbose
└─system.slice
├─stress1.service
│ └─458 /bin/dd if=/dev/zero of=/dev/null
├─systemd-journald.service
│ └─27 /lib/systemd/systemd-journald
└─stress2.service
└─461 /bin/dd if=/dev/zero of=/dev/null

 

このように、systemctlで起動するserviceは、system.sliceに紐付けられる。
また、multiuser-target等で自動で起こされる場合も同様にsystem.sliceに紐付けられる。

このときのcpu使用率はどうなっているのだろう。
systemdから起動させたserviceのリソース状況は、systemd-cgtopコマンドで確認できる。

root@24d73e4b6423:/# systemd-cgtop

Control Group Tasks %CPU Memory Input/s Output/s
/docker/24d73e4b6423a157af025a752cd0363efec382af152dacd501dc437941dd943f 7 198.7 110.5M - -
/docker/24d73e4b6423a157a...0363efec382af152dacd501dc437941dd943f/system.slice - 198.5 - - -
/docker/24d73e4b6423a157a...dacd501dc437941dd943f/system.slice/stress2.service - 100.2 - - -
/docker/24d73e4b6423a157a...dacd501dc437941dd943f/system.slice/stress1.service - 99.3 - - -

system.sliceは可能な限りのCPUをすべて使おうとしている。また、system.sliceに紐づく各serviceは、特に設定をしない限り均等にCPU資源が割り当てられるようだ。

同一slice内のservice間の資源の設定

system.sliceに属するserviceで資源の設定を変更する例として、試しに同一slice内のCPU時間の割合の配分を変更してみる。
まず、デフォルトのCPU割り当てを確認する。systemdはCPU資源の配分にcgroupを使用しているため、各serviceの内容は/sys/fs/cgroupから参照できる。今回は、control group 内のタスクのCPUの時間配分を決めるcpu.cfs_quota_usを確認してみる。
CPU資源について特に何も設定していないstress1.serviceを起動し、それに対応するcontrol groupのcpu.cfs_quota_usをsysfsから確認する(dockerコンテナ内で起動しているため、dockerグループ内にコンテナIDの名を持つグループが見え、そのなかにコンテナ内のserviceの情報が格納される)。

root@24d73e4b6423:/# systemctl start stress1
root@24d73e4b6423:/# cat /sys/fs/cgroup/cpu/docker/24d73e4b6423a157af025a752cd0363efec382af152dacd501dc437941dd943f/syst
em.slice/stress1.service/cpu.cfs_quota_us
-1

「-1」はデフォルト値で、CPU資源に関して何も制限がかかっていないことを示している。詳細は下記リンク先のkernelのスケジューラのドキュメントを参照

さて、次にstress2.serviceにCPU資源に制限をかけるように設定し起動してみる。。serviceファイル内の[Service]セクションにて、CPUQuotaという属性で値を指定する。

root@24d73e4b6423:/# cat /etc/systemd/system/stress2.service
[Unit]
Description="Stress 2"

[Service]
ExecStart=/bin/dd if=/dev/zero of=/dev/null
Type=simple
CPUQuota=20%

[Install]
WantedBy=multi-user.target

CPUQuotaは単一CPU内のCPU使用率に制限をかけるオプションだ(デフォルトは100%)。これにより、stress2.serviceは20%しかCPU使用率を食わないようになる。

この状態で再度2つのseviceを起動し、systemd-cgtopとcgroupの値を確認する。

root@24d73e4b6423:/# systemctl start stress1
root@24d73e4b6423:/# systemctl start stress2
root@24d73e4b6423:/# systemd-cgtop
Control Group                                                                    Tasks   %CPU   Memory  Input/s Output/s
/docker/24d73e4b6423a157af025a752cd0363efec382af152dacd501dc437941dd943f             7      -   112.8M        -        -
Control Group                                                                    Tasks   %CPU   Memory  Input/s Output/s
/docker/24d73e4b6423a157af025a752cd0363efec382af152dacd501dc437941dd943f             7  110.4   112.8M        -        -
/docker/24d73e4b6423a157a...0363efec382af152dacd501dc437941dd943f/system.slice       -  110.2        -        -        -
/docker/24d73e4b6423a157a...dacd501dc437941dd943f/system.slice/stress1.service       -   90.3        -        -        -
/docker/24d73e4b6423a157a...dacd501dc437941dd943f/system.slice/stress2.service       -   19.9        -        -        -
root@24d73e4b6423:/# cat /sys/fs/cgroup/cpu/docker/24d73e4b6423a157af025a752cd0363efec382af152dacd501dc437941dd943f/system.slice/stress2.service/cpu.cfs_quota_us
20000
root@24d73e4b6423:/# cat /sys/fs/cgroup/cpu/docker/24d73e4b6423a157af025a752cd0363efec382af152dacd501dc437941dd943f/system.slice/stress2.service/cpu.cfs_period_us
100000

systemd-cgtopでは、stress2.serviceがCPU使用率20%担っていることが確認できる。また、cgroupでは、cpu.cfs_quota_usに値が格納されている。cpu.cfs_quota_usに-1以外の値が入った場合、そのCPU使用率は単位CPU時間(cpu.cfs_period_us)当たりのタスクが使用するCPU時間(cpu.cfs_quota_us)で決まる。今回は単位時間100000(us)に対し20000(us)の時間をタスクが消費する、すなわち1/5のCPU使用率で動作する設定となっている。

注意

systemdを使ったリソース資源で使用できるオプションは、systemdのバージョンやkernelのバージョン・コンパイルオプションの設定に大きく依存する。
筆者が職場で使用した環境では、CPU使用率制御のためにserviceファイルでCPUQuotaを変更してもcpu.cfs_quota_usに反映されず大変困った。
結局、CPUWeightオプションを使用することで制御することができるようになった。どうもCPUQuotaはlegacyなシステム向けのようで、新しいバージョンではCPUWeightを推奨しているようだ。
自身のsystemdでどのオプションが使用できるかは、systemdのドキュメントを参照しながら検討する必要がある。

 

-Docker, Linux
-

執筆者:


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

関連記事

Mackerelでサーバ内ホスト・Dockerコンテナを監視する

サーバ監視ソフトを入れたいなと考えていたところ、ひょんなことからMackerelというサーバ監視サービスを発見したため、導入してみました。 環境は以下です。 OS:Ubuntu16.04 目次1 要件 …

Ubuntu 16.04 LTS でのDocker実行環境(docker-engine + docker-compose)の構築

今回は、Ubuntu 16.04 LTS でのDocker実行環境の構築を行います。 目次1 背景2 注意3 docker-engineのインストール4 docker-composeのインストール5 …

systemdからdockerコンテナを起動+timerで定期実行

今回はdockerコンテナをsystemdから起動し、かつtimerを使用して定期実行する方法を紹介します。 目次1 背景2 要求と仕様3 systemdについて4 設計・実装4.1 テストアプリとs …

cmake, googletestを利用したC++開発環境の構築テスト

自学用メモ。 業務で使用しているcmakeの勉強がてら、業務に近いソースツリーを作成した。 googletestを用いた単体テストも合わせて書けるような構成になっている。 テンプレートとして、今後のC …

Dockerを使用した簡単なC++実行環境の構築

今回は、C++の機能を調査するための簡単なテスト環境を、Dockerを用いて構築する手順を解説します。 目次1 要件2 仕様3 実装3.1 元になるイメージ3.2 Dockerfileの作成3.3 実 …