APIが「1秒あたり500リクエスト」でどう振る舞うかを知りたいのであって、「Nスレッドでどれだけ速く叩けるか」を知りたいわけではありません。多くの負荷テストツールは同時実行数を固定し、実際のリクエストレートはサーバーの応答速度に左右されます。一方、Vegetaはリクエストレートを固定します。指定したRPSを、サーバーの応答に関係なく送信し続けます。この違いは、目標負荷でのスループット、SLOで約束するレイテンシ、容量限界を正しく測るうえで重要です。
Vegetaとは
Vegetaは、Goで書かれたCLIベースのHTTP負荷テストツールです。Goライブラリとしても使えますが、この記事ではCLIでの使い方に絞ります。
Vegetaの中心的な特徴は、一定のリクエストレートです。
たとえば「30秒間、1秒あたり100リクエストを送る」と指定すると、Vegetaはそのペースを維持するために必要なワーカーを使ってリクエストを送信します。サーバーが遅くなっても、前のリクエストの完了を待たず、スケジュールどおりに次のリクエストを発行します。
これはオープンモデルの負荷テストです。実際の本番トラフィックでも、次のユーザーは前のユーザーの遅いレスポンスを待ってくれません。レートベースの負荷テストは、その到着パターンに近い形でAPIを測定できます。
レートベースの負荷 vs 同時実行数ベースの負荷
負荷テストでは、まず「何を固定するのか」を決める必要があります。
同時実行数ベース
同時実行数ベースのツールでは、固定されたスレッド、仮想ユーザー、またはワーカーがリクエストとレスポンスをループします。これはクローズドモデルです。
サーバーが遅くなると、各ワーカーはレスポンス待ちで止まります。その結果、実際に送られるリクエストレートが下がります。つまり、負荷をかけているつもりでも、ツール側が自然に送信を控えてしまう場合があります。
この挙動は、本番で発生するボトルネックを隠すことがあります。
レートベース
Vegetaのようなレートベースのツールでは、到着レートを固定します。サーバーが追いつけない場合、リクエストは滞留し、レイテンシが上がり、エラーが発生する可能性があります。
そのため、次のような問いに向いています。
- 500 RPSでp95レイテンシはどれくらいか
- 1,000 RPSを超えたときに5xxが出るか
- どのレートからテールレイテンシが悪化するか
- 同じRPSで新旧ビルドを比較するとどちらが速いか
どちらのモデルも間違いではありません。
「同時に何人のユーザーを維持できるか」を見たいなら同時実行数ベース、「1秒あたり2,000リクエストで何が起こるか」を見たいならレートベースを使います。
ツール全体の比較については、主要なAPI負荷テストツールも参考になります。
Vegetaのインストール
環境に合わせてインストールします。
macOSでHomebrewを使う場合:
brew update && brew install vegeta
その他のパッケージマネージャー:
# MacPorts
port install vegeta
# Arch Linux
pacman -S vegeta
# FreeBSD
pkg install vegeta
Goがインストールされている場合は、ソースからビルドできます。
git clone https://github.com/tsenart/vegeta
cd vegeta
make vegeta
GitHubのリリースページからビルド済みバイナリをダウンロードすることもできます。
インストール確認:
vegeta --help
基本パイプライン
VegetaはUnixパイプで扱いやすい設計です。
基本形は次のとおりです。
echo "GET http://localhost:8080/" | vegeta attack -duration=5s -rate=100 | vegeta report
このコマンドは以下を実行します。
-
echoでターゲットを1件出力する -
vegeta attackが5秒間、100 RPSでリクエストを送る -
vegeta reportが結果を集計して表示する
この例では合計約500リクエストを送ります。
-rateには時間単位あたりのリクエスト数を指定します。
-rate=100 # 1秒あたり100リクエスト
-rate=100/1s # 同じ意味
-rate=50/500ms # 500ミリ秒あたり50リクエスト
-rate=0を指定するとレート制限が解除され、可能な限り高速に送信されます。これは一定レートの容量測定とは別のテストになるため、再現性を重視する場合は明示的なレートを指定してください。
レポートの読み方
テキストレポートは次のような形式です。
Requests [total, rate, throughput] 500, 100.20, 100.18
Duration [total, attack, wait] 4.991s, 4.990s, 1.2ms
Latencies [min, mean, 50, 90, 95, 99, max] 412us, 1.3ms, 1.1ms, 1.9ms, 2.4ms, 5.1ms, 12ms
Bytes In [total, mean] 128500, 257.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:500
Error Set:
まず見るべき項目は以下です。
| 項目 | 見るポイント |
|---|---|
Requests |
総リクエスト数、実際の送信レート、スループット |
Latencies |
p50、p90、p95、p99、最大値 |
Success |
成功率 |
Status Codes |
2xx、4xx、5xxの内訳 |
Error Set |
タイムアウトや接続エラーなど |
特に重要なのはp95とp99です。平均値が良くても、p99が悪ければ一部のユーザーは遅いレスポンスを体験しています。
結果を保存して再利用する
一度きりの確認なら、attackからreportへ直接パイプして問題ありません。
ただし、比較やCIで使う場合は、生の結果をファイルに保存してからレポートを生成します。
echo "GET http://localhost:8080/" | \
vegeta attack -duration=10s -rate=200 -output=results.bin
テキストレポートを生成:
vegeta report results.bin
JSONで出力:
vegeta report -type=json results.bin > metrics.json
ヒストグラムを出力:
vegeta report -type='hist[0,2ms,5ms,10ms,25ms,100ms]' results.bin
ヒストグラムを使うと、レイテンシがどの範囲に集中しているかを確認できます。p95やp99だけでなく、分布全体を把握したいときに便利です。
ターゲットファイルを使う
実際のAPIでは、1つのGETだけでなく、複数のエンドポイントやPOSTボディを含むリクエストを扱います。
その場合はターゲットファイルを作ります。
targets.txt:
GET http://localhost:8080/api/users
POST http://localhost:8080/api/users
Content-Type: application/json
@./payload.json
GET http://localhost:8080/api/users/42
ルールは以下です。
- 各ターゲットの最初の行は
METHOD URL - ヘッダーはメソッド行の直下に書く
- ヘッダーは1行に1つの
Key: Value -
@で始まる行はリクエストボディ用ファイルを指定する - 空行でターゲットを区切る
-
#で始まる行はコメント
POSTボディを用意します。
payload.json:
{ "name": "Ada", "role": "engineer" }
実行:
vegeta attack -targets=targets.txt -rate=50 -duration=30s | vegeta report
Vegetaはターゲットを順番に巡回し、指定した時間が終わるまで繰り返します。
認証ヘッダーのように全リクエストへ共通で付けたいヘッダーは、-headerで指定できます。
vegeta attack -targets=targets.txt -rate=50 -duration=30s \
-header="Authorization: Bearer $TOKEN" | vegeta report
JSONターゲット形式を使う
プログラムでターゲットを生成する場合や、大量のリクエスト定義を扱う場合はJSON形式が便利です。
各行に1つのJSONオブジェクトを書き、-format=jsonを指定します。
{"method": "GET", "url": "http://localhost:8080/api/users"}
{"method": "POST", "url": "http://localhost:8080/api/users", "header": {"Content-Type": ["application/json"]}, "body": "eyJuYW1lIjoiQWRhIn0="}
bodyはbase64エンコードされた値です。
実行例:
vegeta attack -format=json -targets=targets.json -rate=100 -duration=30s | vegeta report
ターゲットを標準出力で生成してパイプする場合は、-lazyを使うとメモリ効率よく処理できます。
generate-targets | vegeta attack -format=json -lazy -rate=100 -duration=30s | vegeta report
プロットでレイテンシ推移を見る
単一の数値だけでは、テスト中盤での悪化やスパイクを見落とすことがあります。
Vegetaのplotを使うと、結果をHTMLで可視化できます。
vegeta attack -targets=targets.txt -rate=100 -duration=60s | \
vegeta plot > plot.html
ブラウザでplot.htmlを開くと、レイテンシの時系列を確認できます。
複数の負荷レベルを比較する場合:
vegeta attack -rate=50 -duration=30s -targets=targets.txt -output=50qps.bin
vegeta attack -rate=100 -duration=30s -targets=targets.txt -output=100qps.bin
vegeta plot 50qps.bin 100qps.bin > compare.html
ヘッドレス環境やCIでは、CSVに変換して保存する方法もあります。
vegeta encode -to=csv results.bin > results.csv
Vegetaを使うべきケース
Vegetaは、レートと容量に関する検証に向いています。
使うべきケース:
- 特定RPSでのスループットとレイテンシを測りたい
- エラー率やテールレイテンシが悪化し始めるレートを見つけたい
- 同じ負荷条件で2つのビルドを比較したい
- インフラ構成変更の前後で性能差を見たい
- GUIなしでCIやシェルスクリプトに組み込みたい
一方で、Vegetaは複雑なユーザーシナリオの分岐や、レスポンスボディの詳細な検証には向いていません。一定レートでHTTPリクエストを送り、レスポンス時間、ステータスコード、エラーを測ることに特化したツールです。
他の負荷テストツールも比較したい場合は、k6負荷テストやJMeter負荷テストも参考になります。
基本概念やメトリクスを整理したい場合は、APIパフォーマンステストチュートリアルを確認してください。
機能テストと負荷テストを分ける
負荷テストは「十分に速いか」を確認します。
しかし、「正しいレスポンスを返しているか」は確認しません。
たとえばVegetaの結果が100%成功で、すべて200番台だったとしても、APIが次のような誤った値を返している可能性はあります。
- 別ユーザーのデータ
- 古い価格
- 必須フィールドが欠けたJSON
- ビジネスルールに違反したレスポンス
Vegetaにとっては、これらも「高速に返った200レスポンス」です。
正しさを確認するには、レスポンスボディ、スキーマ、ヘッダー、ビジネスルールに対するアサーションが必要です。これは機能テストの役割です。
実運用では、次の順番にすると安全です。
- 機能テストでAPIの正しさを確認する
- 合格したエンドポイントに対してVegetaで負荷をかける
- レイテンシ、スループット、エラー率を評価する
ApidogとVegetaを組み合わせる
ApidogはVegetaの代替ではなく、補完として使えます。
Apidogでは、ステータスコード、ヘッダー、JSONフィールドなどに対するアサーションを含むテストシナリオを作成できます。複数リクエストを連結し、データファイルで駆動することもできます。
使い分けは次のとおりです。
| ツール | 役割 |
|---|---|
| Apidog | APIが正しいレスポンスを返すか確認する |
| Vegeta | 正しいAPIが指定RPSで高速に動くか測る |
Apidog CLIを使うと、機能テストをCIで実行できます。
インストール:
npm install -g apidog-cli
保存済みのシナリオまたはスイートを実行:
apidog run --access-token "$APIDOG_ACCESS_TOKEN" \
-t <scenarioOrSuiteId> \
-e <environmentId> \
-r cli,html,junit
主なオプション:
-
-t: シナリオ、フォルダー、またはスイートID -
-e: 環境ID -
-r: レポーター。cli、html、json、junitなど
junit出力は多くのCIダッシュボードに取り込めます。
詳しい手順は、Apidog CLIコマンドラインチュートリアルとCI/CDパイプラインガイドを参照してください。
CIでは、たとえば次の順番で実行します。
# 1. 機能テスト
apidog run --access-token "$APIDOG_ACCESS_TOKEN" \
-t <scenarioOrSuiteId> \
-e <environmentId> \
-r cli,junit
# 2. 負荷テスト
vegeta attack -targets=targets.txt -rate=100 -duration=60s -output=results.bin
# 3. レポート生成
vegeta report results.bin
vegeta report -type=json results.bin > metrics.json
よくある質問
Vegetaはボディ付きPOSTリクエストをサポートしていますか?
はい。HTTPターゲットファイルでは、メソッドとURLを書いたあとにContent-Typeヘッダーを追加し、@./payload.jsonでボディファイルを参照します。
POST http://localhost:8080/api/users
Content-Type: application/json
@./payload.json
JSONターゲット形式では、method、url、base64エンコード済みのbodyを指定します。
-rate=0は何をしますか?
レート制限を解除し、ワーカーと接続が許す限り高速にリクエストを送信します。
これは最大スループットテストであり、一定レートでの再現性ある容量測定とは別物です。SLOや特定RPSでの挙動を測る場合は、明示的に-rateを指定してください。
レイテンシのパーセンタイルはどう読めばよいですか?
Latencies行には、最小値、平均値、p50、p90、p95、p99、最大値が表示されます。
特にp95とp99を確認してください。平均値では見えない、一部ユーザーが体験する遅いレスポンスを把握できます。
VegetaはAPIが正しいデータを返すか確認できますか?
いいえ。Vegetaはスループット、レイテンシ、ステータスコード、エラーを測定します。レスポンスボディやスキーマに対するアサーションは行いません。
正確性は機能テストツールで確認し、性能測定にVegetaを使う構成が適しています。
CIでVegetaを実行するにはどうすればよいですか?
Vegetaは標準入力とファイル出力に対応した単一バイナリなので、通常のシェルステップに組み込めます。
例:
vegeta attack -targets=targets.txt -rate=200 -duration=60s -output=results.bin
vegeta report results.bin
vegeta report -type=json results.bin > metrics.json
負荷テストの前にApidog CLIなどで機能ゲートを実行し、アサーションに合格したAPIだけをVegetaで測定すると、より信頼できるパイプラインになります。
Top comments (0)