2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Vegetaで自社サービスを攻撃する(負荷テスト)

Last updated at Posted at 2023-03-16

はじめに

この記事はVegetaという負荷テストツールに興味がある方向けの記事となっています。この記事を読むと、CLIでの実行方法とVegetaをライブラリとして利用して好きな処理に組み込むところまでがわかります。ギャリック砲から始まりビッグバンアタックを放つまでくらいの感じ?です。(すいません、うまく例えようと思いましたが思いつきませんでした)

vegeta attackコマンドで負荷をかけれるのですが、不思議とエンターキーを押すときに力が入ります。

経緯

Vantiqという会社で働いているのですが、そのままの名前のVantiqというサービスを扱っています。この記事の本筋とズレるので細かい話は省きますが、簡単に言うとストリームデータを好きなように統合・加工・集計しながらその結果を好きな方法で別システムに連携したりして使うようなサービスです。

そもそも大量のストリームデータを捌く前提のサービスなので負荷をかけようとしても負荷テストツール側が先にダウンすることが良くあるため、なんとかテストできそうなものがないか探していました。後はシンプルに使えるものが良いなと思っていました。

そんな中、今回Vegetaを使ってみたのは以下の理由からです。

  • 送信内容を動的に変えることができること
    • 特にIDなど連番にしたりユニークにしたりしたくなると思います
  • テスト期間と、1秒あたりのリクエスト数が指定できること
  • 分散して実行できること
    • vegetaは複数のマシンにインストールして一斉に攻撃vegeta attackできる
    • 詳細はこちら→Distributed Attack
  • セットアップから結果の表示まで簡単にできること

この辺りが叶います。
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も例がないとわかりにくいので書いてみます。

targets.txt
POST https://your-site.com
Content-Type: application/json
Authorization: Bearer xxxxxx
@events.json

メソッド、送信先、ヘッダーの中身などリクエストに必要な情報をこのフォーマットで記載します。JSONでも書けますがその方法はここでは割愛します。

送信時のBodyの内容がここでは@events.jsonとなります。今回用意したevents.jsonの中身はこちらです。このJSONオブジェクトをBodyに含んだリクエストが送信されることになります。

events.json
{
    "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_SECDURATIONは適宜、必要な値にしてください。

送信先、送信量の設定はくれぐれもご注意ください

attack_vantiq.sh

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は先ほどと同じ内容です。

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:

plot.html
vegeta-plot.png

ライブラリとして使う

ライブラリとしても使えます。Goで実装されてますのでGoで書いてみます。

送る内容はこちら↓

{
   "sensor_id": 1 ←ここは連番になる
}

ソースコードはこちらです。

attack_vantiq.go
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秒のリクエストを受け切ったようです。

まとめ

今回は基本的な使い方を書いた時点で長くなってしまったのでここまでにします。この記事の内容はほんの一部で分散アタックなど他にもできることがたくさんあります。また試していきたいと思います。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?