テストのためにサーバにCPU負荷をかけようと思い、Dockerを使った方法で実施しました
結論
CPU1コアを50%利用する場合は以下のコマンドを実行
$ sudo docker run -d --rm -it --cpus=0.5 alpine dd if=/dev/zero of=/dev/null
やりたいこと
簡単な確認に使いたいという程度だったのですが、要件は以下のとおりです
- メモリを圧迫したくない (スワップを発生させたくないため)
- 負荷をかけるCPU使用率を指定したい (10%、20%、50%、と指定できるようにしたい)
- 稼働時間の制限は不要 (コマンドで起動したら手動で止めるまで動き続けてOK)
方法
メモリを圧迫したくない
これは以下を参考にdd
コマンドを使った方法にしました。
linux - How to create a CPU spike with a bash command - Stack Overflow
$ dd if=/dev/zero of=/dev/null
よくある方法で、出力を/dev/null
に無限に流し続ける方法ですね。
止まることなく回し続けるのでCPUを消費し続けることができます。
メモリ消費量は以下の通りでした。読み込んだデータをそのまま流すだけなのでほとんどメモリは使わないんでしょうね。
- 物理メモリ:0〜4バイト
- 仮想メモリ:1520バイト
ちなみにyes > /dev/null
でも大体同じことができるんですが、こちらの方がメモリ消費量は高かったです。とは言っても無視できるレベルですが。
- 物理メモリ:760バイト
- 仮想メモリ:114684バイト
負荷をかけるCPU使用率を指定したい
次に負荷の調整についてです。
上記のddで無限に流す方法ではCPUを使い切ろうとしてしまうため、CPU使用率が100%になってしまいます。
ddに限った話ではなく、要するに無限ループの状態なので仕方がないのですが。
今回はCPUフル回転ではなく、ある程度の負荷をかけたかったのでちょっと考えました。
- bashで
sleep
とdd
を交互に実行する - pythonで適当なスクリプトを書く
その時思いついたのは↑ぐらいかなーって感じでしたが、うまいことやろうと思うと割と大変そうだし、ただのお試しレベルだったのでここを頑張りたくない。。。
ここで、そういえばDockerにリソースの制限機能あったじゃん。というのを思い出したのでCPUの制限はDockerに任せることにしました。
$ docker run --rm -it --cpus 0.5 ubuntu bash
とすると、起動したubuntuコンテナは1コアの50%までしかCPUを使えません。
ただ、helpによると↓のようにCPUの数を指定するオプションのため、0.5と書いてもマシンのコア数によって全体から見た利用率が変わってしまいます。
--cpus decimal Number of CPUs
コア数が2の場合は0.5 / 2 = 0.25ということで、25%の利用率となります。
この辺面倒だよね、ということでcpusにわたす値を動的に決めるワンライナーを書いている記事も見つかりました。
Docker コンテナの CPU 使用率を制限する Tips - Qiita
その他のDockerのリソース制限については以下の記事でまとめられていました。
Dockerのリソースの制限(CPU/メモリ)とCPUの動きについてまとめ - Qiita
実行
とりあえず部品がそろったので、CPUを消費するDockerコンテナを実行していきます。
環境
Amazon EC2上で実行しました
$ docker --version
Docker version 18.09.9-ce, build 039a7df
$ cat /etc/os-release
NAME="Amazon Linux"
VERSION="2"
ID="amzn"
ID_LIKE="centos rhel fedora"
VERSION_ID="2"
PRETTY_NAME="Amazon Linux 2"
ANSI_COLOR="0;33"
CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2"
HOME_URL="https://amazonlinux.com/"
コマンド
以下ワンライナーです
$ sudo docker run -d --rm -it --cpus=0.5 alpine dd if=/dev/zero of=/dev/null
CPU利用率確認
docker stats結果
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
b81b20fb93a6 naughty_bose 116.65% 680KiB / 479.7MiB 0.14% 15.5kB / 0B 36.9kB / 0B 2
コンテナ内の利用率で見ると100%ですね。
top結果
Linuxの場合はコンテナ内で実行したコマンドも一プロセスとしてモニタリングできるのでtop
コマンドで見ていきます。
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3805 root 20 0 1520 4 0 R 48.8 0.0 2884:13 dd
ちゃんと50%を上限とできてそうですね。
しばらく観察してみても、50%から上下5%程度を推移していたので狙い通りできたと思います。
備考
じゃあ50%のdockerを2つ起動したらどうなるか見てみました。
以下topの結果です
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
5851 root 20 0 1520 4 0 R 48.3 0.0 2886:45 dd
26736 root 20 0 1552 4 0 R 48.3 0.0 6:09.80 dd ```
50%を2つ起動したので、合計で100%の利用率になりました。
次にその状態でホストで同じddコマンド(dd if=/dev/zero of=/dev/null
)を実行してみました。
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
27300 ec2-user 20 0 114728 832 768 R 49.8 0.2 0:08.65 dd
26736 root 20 0 1552 4 0 R 26.0 0.0 6:59.15 dd
5851 root 20 0 1520 4 0 R 23.8 0.0 2887:34 dd
50%、25%、25%という割合で別れているように見えますね。
ホスト側との配分は特に設定してないのですが、これを見る感じではホストとコンテナ側で50%ずつを分けているように見えます。
もう少し確認しました。
次にコンテナ4つを、50%,10%,10%,10%
という制限で起動 + ホストでddコマンド実行した場合は↓のようになりました。
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
27694 ec2-user 20 0 114728 776 712 R 50.2 0.2 0:08.00 dd
5851 root 20 0 1520 4 0 R 26.0 0.0 2889:15 dd
27544 root 20 0 1552 4 0 R 8.0 0.0 0:04.08 dd
27649 root 20 0 1552 4 0 R 8.0 0.0 0:02.53 dd
27433 root 20 0 1552 4 0 R 7.7 0.0 0:10.44 dd
やっぱりホストとコンテナで50%分け合ってる感じですね。
コンテナ側では更にその中で使えるCPUに応じてCPUを使う、という感じですね。
dockerdのcgroups設定とかで決まってるんですかね?ここは気が向いたら調べます
おわりに
DockerでCPU利用率を指定して負荷をかける方法を試してみました。
Docker環境が入っている状態なら非常に簡単に実施できるので、もし使えそうなタイミングがあれば試してみてください。
ただし、ホスト側を圧迫するぐらい(CPU50%以上)の負荷をかけたい場合は設定変える必要がありそうですね。
この辺は必要になったら調べます。
記事を書いている時に、そもそもDockerではcgroupsを使ってたはずだから、直で使えばよかったのかと気づいた。
(参考 cgroups でプロセスの CPU 利用率を制御する. - Qiita)
参考
linux - How to create a CPU spike with a bash command - Stack Overflow
Docker コンテナの CPU 使用率を制限する Tips - Qiita
Dockerのリソースの制限(CPU/メモリ)とCPUの動きについてまとめ - Qiita