はじめに
この記事はVegetaという負荷テストツールに興味がある方向けの記事となっています。この記事を読むと、CLIでの実行方法とVegetaをライブラリとして利用して好きな処理に組み込むところまでがわかります。ギャリック砲から始まりビッグバンアタックを放つまでくらいの感じ?です。(すいません、うまく例えようと思いましたが思いつきませんでした)
vegeta attack
コマンドで負荷をかけれるのですが、不思議とエンターキーを押すときに力が入ります。
経緯
Vantiqという会社で働いているのですが、そのままの名前のVantiqというサービスを扱っています。この記事の本筋とズレるので細かい話は省きますが、簡単に言うとストリームデータを好きなように統合・加工・集計しながらその結果を好きな方法で別システムに連携したりして使うようなサービスです。
そもそも大量のストリームデータを捌く前提のサービスなので負荷をかけようとしても負荷テストツール側が先にダウンすることが良くあるため、なんとかテストできそうなものがないか探していました。後はシンプルに使えるものが良いなと思っていました。
そんな中、今回Vegeta
を使ってみたのは以下の理由からです。
-
送信内容を動的に変えることができること
- 特にIDなど連番にしたりユニークにしたりしたくなると思います
- テスト期間と、1秒あたりのリクエスト数が指定できること
-
分散して実行できること
- vegetaは複数のマシンにインストールして一斉に攻撃
vegeta attack
できる - 詳細はこちら→Distributed Attack
- vegetaは複数のマシンにインストールして一斉に攻撃
- セットアップから結果の表示まで簡単にできること
この辺りが叶います。
VegetaはGoで書かれており、ライブラリとしても使用できます。複雑なことをやりたい時は、Goを書く必要がありますが、IDを連番にするくらいだったらGoの経験がない私でも簡単にできました。
VegetaはCLIで使う方法
と上記のライブラリとして使う方法
がありますのでそれぞれ見ていきます。
M1 Mac(Ventra ver. 13.1)で作業してます
CLIで使う
まずは、CLIの方から。インストールはbrew update && brew install vegeta
。
基本形はこんな感じです。vegeta attack
という放ちたくなるコマンドです。
vegeta attack -rate=10 -duration=5s -targets=targets.txt > result.bin | vegeta report
オプション | 内容 |
---|---|
-rate | 秒間あたりのリクエスト数 |
-duration | リクエストし続ける時間(テストする時間) |
-targets | 負荷をかける相手と送信内容の情報(詳細は後述) |
上の例では、1秒に10リクエストを5秒間続けて行う、ということになります。
result.bin
にはテストの結果が入っています。vegeta report
でその結果を表示します。
targets.txt
も例がないとわかりにくいので書いてみます。
POST https://your-site.com
Content-Type: application/json
Authorization: Bearer xxxxxx
@events.json
メソッド、送信先、ヘッダーの中身などリクエストに必要な情報をこのフォーマットで記載します。JSONでも書けますがその方法はここでは割愛します。
送信時のBodyの内容がここでは@events.json
となります。今回用意したevents.json
の中身はこちらです。このJSONオブジェクトをBodyに含んだリクエストが送信されることになります。
{
"id": 1
}
ここまでできたら後はコマンドを放つのみです。
vegeta attack -rate=10 -duration=5s -targets=targets.txt > result.bin | vegeta report
Requests [total, rate, throughput] 50, 10.20, 9.95
Duration [total, attack, wait] 5.027s, 4.9s, 127.398ms
Latencies [min, mean, 50, 90, 95, 99, max] 126.304ms, 145.723ms, 128.136ms, 163.354ms, 266.097ms, 466.085ms, 466.085ms
Bytes In [total, mean] 200, 4.00
Bytes Out [total, mean] 750, 15.00
Success [ratio] 100.00%
Status Codes [code:count] 200:50
Error Set:
こんな形でテスト結果がわかります。この表示方法だけでなくHTMLやJSONで出したり色々できますので興味のある方はチェックしてみてください。
CLI編の最後にシェルスクリプトを用意してみました。使う場合は先のtargets.txt、events.jsonと合わせて使ってください。REQUEST_RATE_SEC
とDURATION
は適宜、必要な値にしてください。
送信先、送信量の設定はくれぐれもご注意ください
REQUEST_RATE_SEC=1000
DURATION=5s
RESULT_DIR="results/"
if [ ! -d $RESULT_DIR ]; then
mkdir $RESULT_DIR
fi
echo "Attack Vantiq with $REQUEST_RATE_SEC request/sec for $DURATION"
vegeta attack \
-rate=$REQUEST_RATE_SEC \
-duration=$DURATION \
-targets=targets.txt \
> "${RESULT_DIR}result.bin"
echo "Done"
cat "${RESULT_DIR}result.bin" | vegeta report
cat "${RESULT_DIR}result.bin" | vegeta report -type=text > "${RESULT_DIR}result.txt"
cat "${RESULT_DIR}result.bin" | vegeta plot > "${RESULT_DIR}plot.html"
results/
ディレクトリに結果(result.txt, plot.html)が格納されます。
実物も貼っておきます。result.txtは先ほどと同じ内容です。
Requests [total, rate, throughput] 5000, 1000.25, 816.11
Duration [total, attack, wait] 6.127s, 4.999s, 1.128s
Latencies [min, mean, 50, 90, 95, 99, max] 104.173ms, 255.733ms, 190.052ms, 430.111ms, 559.268ms, 1.551s, 3.489s
Bytes In [total, mean] 20000, 4.00
Bytes Out [total, mean] 75000, 15.00
Success [ratio] 100.00%
Status Codes [code:count] 200:5000
Error Set:
ライブラリとして使う
ライブラリとしても使えます。Goで実装されてますのでGoで書いてみます。
送る内容はこちら↓
{
"sensor_id": 1 ←ここは連番になる
}
ソースコードはこちらです。
package main
import (
"encoding/json"
"fmt"
"net/http"
"sync/atomic"
"time"
vegeta "github.com/tsenart/vegeta/lib"
)
// Configurations
const (
HOST = "https://your-site.com"
TOKEN = "Bearer xxxxxx"
REQUEST_FREQ_SEC = 2000
LOAD_DURATION_SEC = 10
)
func main() {
// Define target
target := func(id uint64) vegeta.Targeter {
type entity struct {
ID uint64 `json:"sensor_id"`
}
return func(t *vegeta.Target) (err error) {
t.Method = "POST"
t.URL = HOST
t.Body, err = json.Marshal(&entity{
ID: atomic.AddUint64(&id, 1),
})
t.Header = http.Header{
"Content-Type": []string{"application/json"},
"Authorization": []string{TOKEN},
}
return err
}
}(0)
// Create attacker
rate := vegeta.Rate{Freq: REQUEST_FREQ_SEC, Per: time.Second}
duration := LOAD_DURATION_SEC * time.Second
var metrics vegeta.Metrics
attacker := vegeta.NewAttacker()
// Attack
fmt.Print("Attacking Vantiq...\n")
for res := range attacker.Attack(target, rate, duration, "Big Bang!") {
metrics.Add(res)
}
fmt.Print("Done!\n")
metrics.Close()
// Show results
fmt.Print("- Show results -\n")
fmt.Printf("Requests [total, rate, throughput]: [%d, %0.2f, %0.2f]\n",
metrics.Requests, metrics.Rate, metrics.Throughput)
fmt.Printf("Duration [total, attack, wait]: [%s, %s, %s]\n",
metrics.Duration.String(), metrics.Duration-metrics.Wait, metrics.Wait)
fmt.Printf("Latency [mean, P50, P95, P99, max]: [%s, %s, %s, %s, %s]\n",
metrics.Latencies.Mean, metrics.Latencies.P50, metrics.Latencies.P95, metrics.Latencies.P99, metrics.Latencies.Max)
fmt.Printf("Status Codes [code:count]: ")
for code, count := range metrics.StatusCodes {
fmt.Printf("[%s:%d]", code, count)
}
fmt.Println()
}
繰り返しですが、もし使う場合は送信先と送信量の設定にご注意ください。
(上のソースコードでは秒間2000リクエスト送るようになっています)
ここの部分は好きに変更してください、
const (
HOST = "https://your-site.com"
TOKEN = "Bearer xxxxxx"
REQUEST_FREQ_SEC = 2000
LOAD_DURATION_SEC = 10
)
このソースコードはこちらを参考にして書いてみました
Usage (Library)
[QUESTION] Sending requests with dynamic body
実行までの手順はこちらです。
go mod init <YOUR DIR>
go get -u github.com/tsenart/vegeta
go run ./attack_vantiq.go
実行結果
Attacking Vantiq...
Done!
- Show results -
Requests [total, rate, throughput]: [20000, 2000.02, 1973.39]
Duration [total, attack, wait]: [9.999898917s, 9.864941084s, 134.957833ms]
Latency [mean, P50, P95, P99, max]: [232.234749ms, 131.723067ms, 715.269472ms, 1.070837017s, 1.841236917s]
Status Codes [code:count]: [200:20000]
Status Codes [code:count]: [200:20000]
はステータスコード200が20000回ということですので、特にエラーもなく2000リクエスト * 10秒のリクエストを受け切ったようです。
まとめ
今回は基本的な使い方を書いた時点で長くなってしまったのでここまでにします。この記事の内容はほんの一部で分散アタックなど他にもできることがたくさんあります。また試していきたいと思います。