ども、cloudpack の かっぱ (@inokara) です。
はじめに
負荷試験ツールで個人的に好きなのは Siege ですが、負荷試験ツールの代名詞 JMeter の影に隠れてイマイチ存在感が薄いようです。やっぱり JMeter 一択なのかなと思っていた矢先に Locust という Python 製の負荷試験ツールがあることを以下のような記事で知りました。
自分も百番煎じ位ですが boot2docker を使ってドカドカ Docker で Locust を試してみたいと思います。尚、オフィシャルサイトやドキュメントは下記の通りです。
ちなみに Locust とはワタリバッタ(いなご)のようです…
カロリーって…。
Locust って
前述の通り Python 製の負荷テストツールで以下のような機能があります。
- テストシナリオを Python で書くことが出来る
- 複数のノードを利用した分散テストを行うことが出来る
- Web UI
- Web サイト以外のテストも行うことが出来ます
また、Locust の重い処理は gevent にお任せすることで、Locust 自身のコアは小さく作らており今後もこれを維持していくとのことです。gevent とは libev を元にした並行ライブラリです。ネットワークや並行プログラミングのためのクリーンな API を提供しているとのことです。
インストール等の準備
Docker コンテナ
Ubuntu コンテナを利用します。
docker run -t -i ubuntu /bin/bash
利用する Ubuntu バージョンは以下のとおり。
root@f5148f9250fa:/tmp# cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=14.04 DISTRIB_CODENAME=trusty DISTRIB_DESCRIPTION="Ubuntu 14.04.1 LTS"
Locust のインストール
pip を利用してインストールしたいと思います。
apt-get update apt-get install python-pip apt-get install python-dev pip install locustio pyzmq
尚、pyzmq は Locust の複数のノードやプロセスを利用したテストを行うのであれば入れておいた方が良いよということなのでインストールしておきます。
トラブル
ocustio と pyzmq をインストールした後で pip を利用出来なく現象に遭遇…。以下のように IncompleteRead
がインポート出来ないとのこと。
Traceback (most recent call last): File "/usr/bin/pip", line 9, inload_entry_point('pip==1.5.4', 'console_scripts', 'pip')() File "/usr/lib/python2.7/dist-packages/pkg_resources.py", line 351, in load_entry_point return get_distribution(dist).load_entry_point(group, name) File "/usr/lib/python2.7/dist-packages/pkg_resources.py", line 2363, in load_entry_point return ep.load() File "/usr/lib/python2.7/dist-packages/pkg_resources.py", line 2088, in load entry = __import__(self.module_name, globals(),globals(), ['__name__']) File "/usr/lib/python2.7/dist-packages/pip/__init__.py", line 11, in from pip.vcs import git, mercurial, subversion, bazaar # noqa File "/usr/lib/python2.7/dist-packages/pip/vcs/mercurial.py", line 9, in from pip.download import path_to_url File "/usr/lib/python2.7/dist-packages/pip/download.py", line 25, in from requests.compat import IncompleteRead ImportError: cannot import name IncompleteRead
対処は以下を参考にして requests
をバージョン指定でインストールします。
sudo easy_install requests==2.3.0
焦りました…。
気を取り直して…
初めての Locust です。
ひとまずヘルプを確認します。
root@a858c220e16c:/# locust -h Usage: locust [options] [LocustClass [LocustClass2 ... ]] Options: -h, --help show this help message and exit -H HOST, --host=HOST Host to load test in the following format: http://10.21.32.33 --web-host=WEB_HOST Host to bind the web interface to. Defaults to '' (all interfaces) -P PORT, --port=PORT, --web-port=PORT Port on which to run web host -f LOCUSTFILE, --locustfile=LOCUSTFILE Python module file to import, e.g. '../other.py'. Default: locustfile --master Set locust to run in distributed mode with this process as master --slave Set locust to run in distributed mode with this process as slave --master-host=MASTER_HOST Host or IP address of locust master for distributed load testing. Only used when running with --slave. Defaults to 127.0.0.1. --master-port=MASTER_PORT The port to connect to that is used by the locust master for distributed load testing. Only used when running with --slave. Defaults to 5557. Note that slaves will also connect to the master node on this port + 1. --master-bind-host=MASTER_BIND_HOST Interfaces (hostname, ip) that locust master should bind to. Only used when running with --master. Defaults to * (all available interfaces). --master-bind-port=MASTER_BIND_PORT Port that locust master should bind to. Only used when running with --master. Defaults to 5557. Note that Locust will also use this port + 1, so by default the master node will bind to 5557 and 5558. --no-web Disable the web interface, and instead start running the test immediately. Requires -c and -r to be specified. -c NUM_CLIENTS, --clients=NUM_CLIENTS Number of concurrent clients. Only used together with --no-web -r HATCH_RATE, --hatch-rate=HATCH_RATE The rate per second in which clients are spawned. Only used together with --no-web -n NUM_REQUESTS, --num-request=NUM_REQUESTS Number of requests to perform. Only used together with --no-web -L LOGLEVEL, --loglevel=LOGLEVEL Choose between DEBUG/INFO/WARNING/ERROR/CRITICAL. Default is INFO. --logfile=LOGFILE Path to log file. If not set, log will go to stdout/stderr --print-stats Print stats in the console --only-summary Only print the summary stats -l, --list Show list of possible locust classes and exit --show-task-ratio print table of the locust classes' task execution ratio --show-task-ratio-json print json data of the locust classes' task execution ratio -V, --version show program's version number and exit
--master
や --slave
オプションがありますな。また、分散テストを実施する際にはマスターノードとスレーブノード間では 5557
と 5558
番ポートを利用するので開けておく必要があるようです。
初めての Locust
シナリオを Python で…
残念ながら Python も初心者枠なのでドキュメントを見よう、見まねで以下のようにテストシナリオを書いてみました。
from locust import HttpLocust, TaskSet, task class MyTaskSet(TaskSet): @task(2) def index(self): self.client.get("/") @task(1) def about(self): self.client.get("/about/about.html") class MyLocust(HttpLocust): task_set = MyTaskSet min_wait = 5000 max_wait = 15000
せっかくなので自分なりに上記のコードを読み解いていきます。
MyTaskSet
で TaskSet クラス を利用してタスクを定義HttpLocust
のclient
インスタンスメソッドで HTTP リクエストを任意の名前の関数オブジェクトにて定義(例えばself.client.get("/")
は HTTP の GET メソッドで/
にアクセス )@task
はタスクの実行率を指定するために使用できるオプションで重み付けの引数を設定(例では@task(2)
で@task(1)
の倍実行される)MyLocust(HttpLocust)
はHTTPLocust
を利用して定義したタスクを利用して HTTP アクセスを定義
はじめてのテスト
作ったシナリオファイルを以下のように指定して Locust を起動したいと思いますが、事前にテスト対象となる Web サーバーコンテナを起動しておきます。
locust -f test.py -H http://172.17.0.7
上記の各オプションの意味は以下の通りです。
オプション | パラメータ | 意味 |
---|---|---|
-f | test.py | シナリオファイルを指定 |
-H | http://172.17.0.7 | テスト対象のホストを指定(今回は別で起動している Docker コンテナ) |
起動すると以下のように表示されます。
# locust -f test.py -H http://172.17.0.7 [2015-01-17 03:26:22,849] f5718236c996/INFO/locust.main: Starting web monitor at *:8089 [2015-01-17 03:26:22,850] f5718236c996/INFO/locust.main: Starting Locust 0.7.2
Locust の Web UI が 8089
ポート待機しているようですのでブラウザからアクセスします。今回は boot2docker を利用しているので事前に VirtualBox の設定で 8089
ポートにアクセス出来るようにしておきましょう。
Number of users to simulate
と Hatch rate
にそれぞれ数値を入れて Start swarming
をクリックします。尚、Number of users to simulate
と Hatch rate
の意味は以下のとおりです。
オプション | パラメータ例 | 意味 |
---|---|---|
Number of users to simulate | 300 | 想定する(シュミレート)するユーザー数 |
Hatch rate | 10 | 毎秒で生成するユーザー数 |
かなり訳が怪しい感じですが、上記例の場合には最大 300 ユーザーを想定し、毎秒 10 ユーザーを生成するという設定になります。このパラメータを設定した状態でテストを開始してみると以下のように表示されました。
おお、ちゃんとテストが行われているようですね。また、シナリオで定義したように /
と /about/about.html
へのアクセスが 2:1 になっていることも解ります。尚、ストップボタンをクリックして結果を csv で出力することも可能です。今回は細かいテスト結果の見方については割愛しますが、結果はリクエスト数、レスポンスタイムの最小、最大、平均、中央値が出力されています。
最初に Locust を起動したコンソールで Ctrl+c
を押してフォアグランドで起動している Locust を停止すると以下のように CUI ベースの結果が出力されます。
Name # reqs # fails Avg Min Max | Median req/s -------------------------------------------------------------------------------------------------------------------------------------------- GET / 5848 0(0.00%) 16 2 283 | 14 20.70 GET /about/about.html 2842 0(0.00%) 13 2 102 | 11 9.70 -------------------------------------------------------------------------------------------------------------------------------------------- Total 8690 0(0.00%) 30.40 Percentage of the requests completed within given times Name # reqs 50% 66% 75% 80% 90% 95% 98% 99% 100% -------------------------------------------------------------------------------------------------------------------------------------------- GET / 5848 14 17 19 21 27 33 42 50 283 GET /about/about.html 2842 11 14 16 18 24 31 39 45 102 --------------------------------------------------------------------------------------------------------------------------------------------
上記のように CUI での結果出力も可能ということは…そうですね、Web UI を動かさないモードもあるようです。起動時に --no-web
で Locust を起動することで CUI モードでの起動が可能となります。
分散テストを試す
Locust の醍醐味(かも)
Locust はシナリオが Python で書けたり Web UI があったりと魅力十分ですが、もうひとつの魅力として分散テストが行えるということが挙げられるかと思いますので試してみたいと思います。
Master / Slave
Locust の Master / Slave の通信は 5557
と 5558
ポートを利用して行われるようですので、それぞれのポートを開放した状態のコンテナを用意した上で試してみたいと思います。(事前に Locust をセットアップしたコンテナを inokappa/locust という名前で作っておきます)
docker run -t -i -p 8089:8089 -p 5557 -p 5558 --name locust-master inokappa/locust /bin/bash docker run -t -i -p 5557 -p 5558 --name locust-slave01 inokappa/locust /bin/bash
それぞれコンテナが起動したら、以下のように Locust を起動します。
# Master Locust locust -f test.py -H http://172.17.0.7 --master # Slave Locust(マスターの IP は 172.17.0.8 となる) locust -f test.py --slave --master-host=172.17.0.8
上記の各オプションの意味は以下の通りです。
オプション | パラメータ | 意味 |
---|---|---|
–master | なし | Locust を Master モードで起動します |
–slave | なし | Locust を Slave モードで起動します |
–master-host | 172.17.0.8 | マスターとなるホストを指定 |
それぞれの Locust が起動した状態でマスターの Locust にブラウザでアクセスしてみます。
おお。Slave が追加されているのが解ります。あとはシングルノードの時と同じように負荷試験を行うことが可能です。
今日の知見
以下のような知見を得ました。
- Locust という Python 製の負荷試験ツールがある
- シナリオが Python で書けるのでちょっと嬉しい(JMeter の XML との格闘から少し開放される)
- 意外に簡単に分散テスト環境を構築することが出来るので Docker との相性も良さそうな気がするけど…どうだろう
今回は超簡単なシナリオで試してみましたが、引き続き、シナリオの書き方を勉強しつつ弄っていきたいと思います。
元記事はこちらです。
「Python 製負荷テストツール Locust を Docker コンテナで試す」