負荷検証ツール「curl-loader」を、コンテナで動かしてみる

こんにちはみなさん

Webシステムを構築する際にどうしても考えなきゃならないのが、負荷の問題です。
強負荷がかかっている状態で、システムがどのような挙動をとるかは、実際にそのような状況にならなければわからない場合が多いです。

負荷検証ツールで有名なのはjmeterですが、今回はちょっとマイナーなcurl-loaderを使うことにします。
これはコマンドラインで動かせるので、ちょちょっと負荷試験やりたい場合に便利です。

というわけで、せっかくだからcurl-loaderをコンテナで動かしてみました。

# curl-loader
curl-loaderはWebアプリの負荷検証ツールの一つですね。
http://curl-loader.sourceforge.net/
(ソースフォージにあるのに今更驚いた)

設定ファイルを食わせてコマンドライン上で動作させる負荷検証ツールで、設定ファイル上でシナリオを設定したり、同時接続数や繰り返し回数を指定したりできます。
日本語化はしていないので、統計情報の見方が若干大変ですが、設定ファイルの例がソースコードにたくさんあるので、導入自体の敷居はそれほど高くないかと。

## curl-loaderの特徴

- コマンドラインで実行する
- confファイルにシナリオを書く
- POSTなども可能で、ファイルなども送信できる
- Cookieを保持しているようで、ログインしてからのテストも可能

# 準備
## コンテナイメージを取得する

例によって人のを借りましょう。
alpineで動かそうとしたら、コンパイルできなかったのだ...

```
$ docker pull kintoandar/curl-loader 
```
## 対象の系を用意する

対象の系がないと、そもそも負荷試験できないので、適当なwebサイトを生成しましょう。
なお、**他人の環境に負荷試験をすると、犯罪になる**ので、必ず自分が用意した負荷検証用の系に負荷をかける用にしましょう。

今回はログインが存在する系をdockerで用意しました。
コードをここに記載しないのは、モノホンの運用コードなんで、載せられないだけです。
laradockのようなdocker-composeの系を利用していると、別ネットワークを使っている可能性があるので、リンクを張りたい場合は注意してください。

## curl-loader 起動
今回はコンテナ名を`docker-compose ps`, ネットワーク名を`docker network ls`で取得し、それらを使ってcurl-loaderコンテナを起動しましょう。

```
$ docker run --rm -it -v `pwd`:/srv --net=laradock_default --link laradock_nginx_1:webapp kintoandar/curl-loader bash
```
# 負荷試験を始める
## 設定ファイルを作る
ちょっと面倒ですが、curl-loaderのコンテナ自身のIPをまず取得しておきます。

```
$ ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:12:00:09
          inet addr:172.18.0.9  Bcast:0.0.0.0  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:6 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:508 (508.0 b)  TX bytes:0 (0.0 b)
```
次に、事前にボリューム設定しておいたディレクトリで以下のファイルを作ります。

```test.conf
# General
BATCH_NAME= test
CLIENTS_NUM_MAX=2
INTERFACE   =eth0
CLIENTS_RAMPUP_INC = 1
CYCLES_NUM = 3
URLS_NUM=2
IP_ADDR_MIN=172.18.0.9
IP_ADDR_MAX=172.18.0.9
NETMASK=32

# Login
URL="http://webapp/login"
URL_SHORT_NAME="Login-POST"
URL_DONT_CYCLE = 1
USERNAME= username
PASSWORD= password
FORM_USAGE_TYPE= SINGLE_USER
FORM_STRING= username=%s&password=%s
REQUEST_TYPE=POST
TIMER_AFTER_URL_SLEEP =1000

# Many Access
URL="http://webapp/something/"
URL_SHORT_NAME="get-something"
REQUEST_TYPE=GET
```
一見するとナンノコッチャって感じですが、１つずつ見ていきましょう。

### General
まずはGeneralとコメントした部分です。
負荷試験全体の設定をしています。
箇条書きだと見づらいので、表形式にしました。

| タグ名 | 意味 | 値 |
|:-----|:----|:--:|
|BATCH_NAME|負荷試験の名前。ログファイルの命名に使用される|test|
|CLIENTS_NUM_MAX|同時接続数の最大値。100だったら、最大100小同時接続する状況になる|2|
|CLIENTS_RAMPUP_INC|一秒間にどれだけ接続数(= クライアント) を増やすかの定義。|1|
|CYCLES_NUM|一接続あたりのアクセス試行回数。-1で無限回|4|
|URLS_NUM|負荷試験に使用するURLの数|2|
|INTERFACE|おそらく使用するネットワークインターフェース。|eth0|
|IP_ADDR_MIN|各接続が使用できる接続元IPアドレスの最小値。自分で検証したところ、先にifconfigで取得したIPアドレスを使用すると動いた|172.18.0.9|
|IP_ADDR_MAX|各接続が使用できる接続元IPアドレスの最大値だと思うが、IP_ADDR_MINと同じ値を使わなければうまくいかなかった|172.18.0.9|
|NETMASK|cider表記のときのネットマスクだと思われる。IPアドレスを一つしか使わないので、32にしている|32|

こんな感じですかね。
イマイチネットワーク関連は弱いので、理解が浅いですが、とりあえず動いたので。。。

### URLs
「URL」というタグがあると、そこからWebアプリにアクセスしたときの設定が開始します。
今回はURLタグが2つあるので、負荷試験対象URLが２つあります。

| タグ名 | 意味 | 値 |
|:-----|:---------|:--:|
|URL|試験対象のURLです。また、このタグが書かれると、そこから新しい設定ブロックが開始される。|"http://webapp/something/"|
|URL_SHORT_NAME|そのURLブロックに略称を付与する|"get-something"|
|REQUEST_TYPE|リクエストタイプで、GET, POST, PUTがある模様|GET|
|URL_DONT_CYCLE |繰り返し回数が1以上の場合、これが設定されているブロックの試験は、１回しか実行されない| 1|
|USERNAME|POSTでログインする場合に設定されるユーザーID| username|
|PASSWORD|POSTでログインする場合に設定されるパスワード| password|
|FORM_USAGE_TYPE|POSTの使用方法。SINGLE_USERだと、設定されたUSERNAME, PASSWORDを後ろのFORM_STRINGにて使用する| SINGLE_USER|
|FORM_STRING|form-data形式でサーバに送信するリクエストを記載する。| login_id=%s&password=%s|
|TIMER_AFTER_URL_SLEEP |アクセス後、次の処理に移る前の待機時間|1000|

## 起動
設定ファイルが完成したら、起動しましょう。

```
$ cd /srv
$ curl-loader -f test.conf -u
found form_str login_id=%s&password=%s
parse_config_file - loaded 1 batches

The configuration has been post-validated successfully.

add_secondary_ip_addrs - setting secondary IP 172.18.0.9
rtnl_talk(): RTNETLINK answers: Operation not permitted
add_secondary_ip_addrs - note: probably, the IP-address "172.18.0.9/32" already exists.
add_secondary_ip_addrs - setting secondary IP 172.18.0.9
rtnl_talk(): RTNETLINK answers: Operation not permitted
add_secondary_ip_addrs - note: probably, the IP-address "172.18.0.9/32" already exists.
main - added IP-addresses to the loading network interface.

RUNNING LOAD

....

============  loading batch is: test       ======================================
--------------------------------------------------------------------------------
Interval stats (latest:3 sec, clients:1, CAPS-curr:0):
H/F   Req:1,1xx:0,2xx:2,3xx:0,4xx:0,5xx:0,Err:0,T-Err:0,D:2635ms,D-2xx:2635ms,Ti:2272B/s,To:243B/s
H/F/S Req:0,1xx:0,2xx:0,3xx:0,4xx:0,5xx:0,Err:0,T-Err:0,D:0ms,D-2xx:0ms,Ti:0B/s,To:0B/s
--------------------------------------------------------------------------------
Summary stats (runs:12 secs, CAPS-average:0):
H/F   Req:8,1xx:0,2xx:7,3xx:0,4xx:0,5xx:0,Err:0,T-Err:0,D:2595ms,D-2xx:2595ms,Ti:1292B/s,To:396B/s
H/F/S Req:0,1xx:0,2xx:0,3xx:0,4xx:0,5xx:0,Err:0,T-Err:0,D:0ms,D-2xx:0ms,Ti:0B/s,To:0B/s
=================================================================================
 Manual: clients:max[2],curr[1]. Inc num: [+|*].
=================================================================================
H/F   Req:0,1xx:0,2xx:1,3xx:0,4xx:0,5xx:0,Err:0,T-Err:0,D:2639ms,D-2xx:2639ms,Ti:3797B/s,To:0B/s
H/F/S Req:0,1xx:0,2xx:0,3xx:0,4xx:0,5xx:0,Err:0,T-Err:0,D:0ms,D-2xx:0ms,Ti:0B/s,To:0B/s

=======================================================================================
End of the test for batch: test
=======================================================================================


Test total duration was 12 seconds and CAPS average 0:
H/F   Req:8,1xx:0,2xx:8,3xx:0,4xx:0,5xx:0,Err:0,T-Err:0,D:2600ms,D-2xx:2600ms,Ti:1609B/s,To:396B/s
H/F/S Req:0,1xx:0,2xx:0,3xx:0,4xx:0,5xx:0,Err:0,T-Err:0,D:0ms,D-2xx:0ms,Ti:0B/s,To:0B/s

Exited. For details look in the files:
- test.log for errors and traces;
- test.txt for loading statistics;
- test.ctx for virtual client based statistics.
- test.ops for operational statistics.
Add -v and -u options to the command line for verbose output to test.log file.
```
とまあ、こんな感じで、結果が出ます。
はじめのうちはエラーが出る可能性があるので、`*.log`ファイルを見て、トラブルシューティングするといいと思います。

# まとめ
というわけで、curl-loaderをコンテナで動かしてみました。
負荷検証したいけど、jmeter使うほどじゃないなぁとか思ったら、これを使うのもありだと思います。

今回はこんなところです。

# 参考
[【ウェブサイトのロードテストをする】 第2回 curl-loader編](https://www.adoc.co.jp/blog/e000201.html)