Dockerを使ってCPU利用率n%の負荷をかける

テストのためにサーバにCPU負荷をかけようと思い、Dockerを使った方法で実施しました


# 結論

CPU1コアを50%利用する場合は以下のコマンドを実行

```console
$ sudo docker run -d --rm -it --cpus=0.5 alpine dd if=/dev/zero of=/dev/null
```

# やりたいこと

簡単な確認に使いたいという程度だったのですが、要件は以下のとおりです

1. メモリを圧迫したくない (スワップを発生させたくないため)
2. 負荷をかけるCPU使用率を指定したい (10%、20%、50%、と指定できるようにしたい)
3. 稼働時間の制限は不要 (コマンドで起動したら手動で止めるまで動き続けてOK)

# 方法

## メモリを圧迫したくない

これは以下を参考に`dd`コマンドを使った方法にしました。

[linux - How to create a CPU spike with a bash command - Stack Overflow](https://stackoverflow.com/questions/2925606/how-to-create-a-cpu-spike-with-a-bash-command)

```
$ 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](https://qiita.com/hoto17296/items/43d8c2d695c8724b99aa)

その他のDockerのリソース制限については以下の記事でまとめられていました。
[Dockerのリソースの制限（CPU/メモリ）とCPUの動きについてまとめ - Qiita](https://qiita.com/Gin/items/450c664ef51f38c0a520)

## 実行

とりあえず部品がそろったので、CPUを消費するDockerコンテナを実行していきます。


### 環境

Amazon EC2上で実行しました

```console
$ 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を２つ起動したらどうなるか見てみました。

以下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](https://qiita.com/syoyo/items/faeb35b0d4f9f9d22480))

# 参考

[linux - How to create a CPU spike with a bash command - Stack Overflow](https://stackoverflow.com/questions/2925606/how-to-create-a-cpu-spike-with-a-bash-command)
[Docker コンテナの CPU 使用率を制限する Tips - Qiita](https://qiita.com/hoto17296/items/43d8c2d695c8724b99aa)
[Dockerのリソースの制限（CPU/メモリ）とCPUの動きについてまとめ - Qiita](https://qiita.com/Gin/items/450c664ef51f38c0a520)