CLOVER🍀

That was when it all began.

負荷テストツールVegetaを試す

これは、なにをしたくて書いたもの?

Vegetaという、割と個性的な負荷テストツールがあり、コマンドラインで簡単に使えそうなので試してみようかということで。

GitHub - tsenart/vegeta: HTTP load testing tool and library. It's over 9000!

Apache Benchの代わりに使えたらどうでしょう?くらいの感覚で、見てみました。

Vegeta

Vegetaというのは、HTTPの負荷テストツールです。

Vegeta is a versatile HTTP load testing tool built out of a need to drill HTTP services with a constant request rate.

GitHub - tsenart/vegeta: HTTP load testing tool and library. It's over 9000!

名前がまさしく…ですが、リポジトリに載せられている画像もそのまんまですね。

https://camo.githubusercontent.com/417a39e5a142e0877be0a7a6d7a66cb77ea21e8c/687474703a2f2f666330392e64657669616e746172742e6e65742f667334392f692f323030392f3139382f632f632f73736a325f7665676574615f62795f7472756e6b7332342e6a7067

Go言語で作成されており、CLIとしても使えますし、ライブラリとしても利用することができます。

It can be used both as a command line utility and a library.

レポートは、テキスト、JSON、ヒストグラムやグラフで見れたりするようです。

report command

テスト対象は、デフォルトは標準入力で与えるようですが、ファイルで用意して複数のターゲットに対して負荷をかけることも
できるようです。

format

分散実行は直接はサポートしていなさそうですが、SSHを使って複数のサーバーでVegetaを実行し、結果のレポートを
統合することで分散実行を実現するようです。

Distributed attacks

途中経過をリアルタイムに見るためには、他のツールの助けが必要な模様。

Real-time Analysis

とまあ、前置きはこれくらいにして、実際に使ってみましょう。

環境

環境は、Ubuntu Linux 18.04 LTSで行いました。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.2 LTS
Release:    18.04
Codename:   bionic

インストール

CLIとしては、バイナリをダウンロードしてきて展開すれば使うことができます。

$ wget https://github.com/tsenart/vegeta/releases/download/cli%2Fv12.3.0/vegeta-12.3.0-linux-amd64.tar.gz
$ tar xf vegeta-12.3.0-linux-amd64.tar.gz

バージョン、12.3.0…。

$ ./vegeta -version
Version: 12.3.0
Commit: 7bf09f4fab4d852141935796455c17459d212098
Runtime: go1.12 linux/amd64
Date: 2019-04-14T13:29:49Z"

使い方は、ヘルプを見るとよいでしょう。

$ ./vegeta -h

最後の方に、コマンドの実行例が出力されます。

  echo "GET http://localhost/" | vegeta attack -duration=5s | tee results.bin | vegeta report
  vegeta report -type=json results.bin > metrics.json
  cat results.bin | vegeta plot > plot.html
  cat results.bin | vegeta report -type="hist[0,100ms,200ms,300ms]"

あとは、マニュアルを見ながらオプションの意味を確認しつつ…ですね。

manual

テスト対象サーバー

Vegetaは負荷テストツールなので、負荷をかける対象がいないと始まりません。

ここは、簡単にNode.js+Expressで用意しました。

$ node -v
v10.15.3


$ npm init
$ npm i express

利用したExpressのバージョンは、こちら。

  "dependencies": {
    "express": "^4.16.4"
  },

作成したアプリケーション。3つ、エンドポイントを作成しました。
※GET、POSTのパスに冗長感ありますが、今回は明確にパスを分けました
server.js

const express = require('express');
const app = express();
const bodyParser = require('body-parser');

app.use(bodyParser.urlencoded({ extended: true }));


app.get('/', (req, res) => res.send('Hello!!'));

app.get('/echo/get', (req, res) => res.send(req.query.message));

app.post('/echo/post', (req, res) => res.send(req.body.message));


app.listen(3000, () => console.log(`[${new Date()}] Server startup`));

package.jsonのscriptsに登録して

  "scripts": {
    "start": "node server.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },

起動。

$ npm start

> target-server@1.0.0 start /path/to/target-server
> node server.js

[Sun Apr 28 2019 21:05:04 GMT+0900 (GMT+09:00)] Server startup

確認。

$ curl localhost:3000
Hello!!

$ curl localhost:3000/echo/get?message=hello
hello

$ curl -XPOST -H 'Content-Type: application/x-www-form-urlencoded' localhost:3000/echo/post -d 'message=hello'
hello

これで、準備完了です。

単一のURLに対して負荷をかけてみる

それでは、Vegetaを使って対象のサーバーに負荷をかけてみましょう。

以下のコマンドで、「http://localhost:3000」にHTTP GETで、5秒間負荷をかけて結果を表示します。レポートはデフォルトで
標準出力に出力されるので、それをさらに「vegeta report」で整形します。

$ echo 'GET http://localhost:3000' | ./vegeta attack -duration=5s | ./vegeta report

結果。

Requests      [total, rate]            250, 50.20
Duration      [total, attack, wait]    4.981730015s, 4.980160389s, 1.569626ms
Latencies     [mean, 50, 95, 99, max]  1.096255ms, 1.092932ms, 1.595155ms, 2.017918ms, 3.177145ms
Bytes In      [total, mean]            1750, 7.00
Bytes Out     [total, mean]            0, 0.00
Success       [ratio]                  100.00%
Status Codes  [code:count]             200:250  
Error Set:

結果の読み方は、レポートのフォーマットが「text」のところに書いてあります。

report -type=text

  • Requests
    • total … 全実行回数
    • rate … 秒間の実行回数
  • Duration
    • total … 負荷をかけるのに要した時間(attack+wait)
    • attack … 全リクエストを実行するのに要した時間(total - wait)
    • wait … レスポンスを待っている時間
  • Latencies … それぞれ、平均、50%、95%、99%パーセンタイル、最大値
  • Bytes In/Bytes Out … リクエスト/レスポンスの送受信のバイト数
  • Success … リクエストの成功率(なお、200と400がエラーとしてカウントされない)
  • Status Codes … ステータスコードのヒストグラム(0は、失敗)
  • Error Set … 失敗したリクエストとその内容

最初の例が、秒間50リクエストとなかなか激しいので、ちょっと下げてみましょう。「-rate」で指定することができます。
秒あたり、3にしてみましょう。

$ echo 'GET http://localhost:3000' | ./vegeta attack -duration=5s -rate=3 | ./vegeta report
Requests      [total, rate]            15, 3.21
Duration      [total, attack, wait]    4.668077623s, 4.666875044s, 1.202579ms
Latencies     [mean, 50, 95, 99, max]  1.061737ms, 935.134µs, 2.220665ms, 2.486884ms, 2.486884ms
Bytes In      [total, mean]            105, 7.00
Bytes Out     [total, mean]            0, 0.00
Success       [ratio]                  100.00%
Status Codes  [code:count]             200:15  
Error Set:

エラーの場合も見てみましょう。試しに、Not FoundになるURLで試してみます。

$ echo 'GET http://localhost:3000/notfound' | ./vegeta attack -duration=5s -rate=3 | ./vegeta report
Requests      [total, rate]            15, 3.21
Duration      [total, attack, wait]    4.667400657s, 4.666715559s, 685.098µs
Latencies     [mean, 50, 95, 99, max]  1.38848ms, 963.456µs, 4.354423ms, 4.627119ms, 4.627119ms
Bytes In      [total, mean]            2205, 147.00
Bytes Out     [total, mean]            0, 0.00
Success       [ratio]                  0.00%
Status Codes  [code:count]             404:15  
Error Set:
404 Not Found

次は、POSTしてみましょう。

HTTPボディの内容は、以下のようなテキストファイルを用意。
body.txt

message=test

URLの前に、今度は「POST」を指定して負荷を書けます。作成したHTTPボディ用のテキストファイルは、「-body」オプションで
指定します。あと、「-header」でHTTPヘッダーも追加しました。

$ echo 'POST http://localhost:3000/echo/post' | ./vegeta attack -duration=5s -body=body.txt -header='Content-Type: application/x-www-form-urlencoded' | ./vegeta report

結果。

Requests      [total, rate]            250, 50.20
Duration      [total, attack, wait]    4.981419329s, 4.980088019s, 1.33131ms
Latencies     [mean, 50, 95, 99, max]  1.399184ms, 1.358172ms, 1.962928ms, 3.146063ms, 5.236047ms
Bytes In      [total, mean]            1250, 5.00
Bytes Out     [total, mean]            3250, 13.00
Success       [ratio]                  100.00%
Status Codes  [code:count]             200:250  
Error Set:

「-body」オプションはあくまでファイルを指定するので、ファイルを用意するのは面倒と思うかもしれません。

そういう時は、JSONフォーマットを使うとよいかもしれません。こうすると、HTTPボディやヘッダーの内容を

$ jq -ncM '{method: "POST", url: "http://localhost:3000/echo/post", body: "message=test" | @base64, header: {"Content-Type": ["application/x-www-form-urlencoded"]}}' | ./vegeta attack -format=json -duration=5s | ./vegeta report

結果。

Requests      [total, rate]            250, 50.20
Duration      [total, attack, wait]    4.981231371s, 4.980126573s, 1.104798ms
Latencies     [mean, 50, 95, 99, max]  1.328564ms, 1.192952ms, 1.933122ms, 2.922471ms, 23.264626ms
Bytes In      [total, mean]            1000, 4.00
Bytes Out     [total, mean]            3000, 12.00
Success       [ratio]                  100.00%
Status Codes  [code:count]             200:250  
Error Set:

結果をグラフで見る

ここまでテキスト形式で結果を見てきましたが、「vegeta plot」を使うことでグラフ形式でも見ることができます。

$ echo 'GET http://localhost:3000' | ./vegeta attack -duration=5s | ./vegeta plot > report.html

結果のHTMLを表示すると、こんな感じになります。

f:id:Kazuhira:20190428214103p:plain

この例では5秒間負荷をかけていますが、その推移をグラフ表示した感じですね。

Requests      [total, rate]            250, 50.20
Duration      [total, attack, wait]    4.981240384s, 4.980150818s, 1.089566ms
Latencies     [mean, 50, 95, 99, max]  942.97µs, 880.2µs, 1.525036ms, 2.03296ms, 2.314226ms
Bytes In      [total, mean]            1750, 7.00
Bytes Out     [total, mean]            0, 0.00
Success       [ratio]                  100.00%
Status Codes  [code:count]             200:250  
Error Set:

複数のURLへ負荷をかける

複数のURLに対して負荷をかける場合は、以下のフォーマットに従って対象のURLを列挙します。

http format

test-scenario.txt

GET http://localhost:3000

GET http://localhost:3000/echo/get?message=test

POST http://localhost:3000/echo/post
Content-Type: application/x-www-form-urlencoded
@body.txt

HTTPヘッダも指定することができ、HTTPボディはファイルで指定します。

このファイルを、「-targets」オプションで指定します。

$ ./vegeta attack -targets=test-scenario.txt -duration=5s | ./vegeta report

結果。

Requests      [total, rate]            250, 50.20
Duration      [total, attack, wait]    4.980628214s, 4.980055554s, 572.66µs
Latencies     [mean, 50, 95, 99, max]  1.208511ms, 1.067828ms, 1.793813ms, 2.42757ms, 16.116178ms
Bytes In      [total, mean]            1335, 5.34
Bytes Out     [total, mean]            1079, 4.32
Success       [ratio]                  100.00%
Status Codes  [code:count]             200:250  
Error Set:

…個別のURLに対する結果はわからない感じ?

結果をグラフにしてみましょう。

$ ./vegeta attack -targets=test-scenario.txt -duration=5s | ./vegeta plot > report.html

こちらでも、わからないようで…。

f:id:Kazuhira:20190428215049p:plain

まあ、おおまかな使い方はわかったので、良しとしましょう。

もうちょっと凝りたい場合は、Apache JMeterやLocustを使うのが良いのでしょうね。