DEV Community

Cover image for wrk: コマンドラインでAPIの負荷テストを行う方法
Akira
Akira

Posted on • Originally published at apidog.com

wrk: コマンドラインでAPIの負荷テストを行う方法

エンドポイントをリリースし、ブラウザでは正常に動作しているとします。次に確認すべきなのは、400人が同時にアクセスしたときの挙動です。レイテンシの99パーセンタイルは跳ね上がるのか、1台のサーバーで毎秒1,000リクエストを処理できるのか、それとも300あたりで詰まるのかを測定します。

今すぐApidogを試す

wrkは、その確認に使える小さなコマンドラインHTTPベンチマークツールです。指定したURLに大量のHTTPトラフィックを流し、負荷下でのスループットとレイテンシをレポートします。

wrkとは何か、いつ使うべきか

wrkはHTTPベンチマークツールです。単一のマルチコアマシンから負荷を生成し、サーバーのレイテンシとリクエストレートを測定します。

wrkはマルチスレッドとスケーラブルなイベントループを使います。Linuxではepoll、macOSではkqueueを利用するため、1台の負荷生成マシンからでも高い同時接続数を扱えます。

wrkで確認できる代表的な項目は次のとおりです。

  • エンドポイントが毎秒何リクエスト処理できるか
  • 中央値、90パーセンタイル、99パーセンタイルのレイテンシ
  • 一定時間の負荷をかけたときに性能が維持されるか
  • 接続数を増やしたときにどこで頭打ちになるか

ただし、wrkはベンチマークツールであり、機能テストツールではありません。JSONレスポンスが正しいか、ステータスコードが期待どおりか、API契約が守られているかは検証しません。

負荷テストの考え方を先に整理したい場合は、API負荷テストガイドも参考になります。

wrkをインストールする

macOS

Homebrewを使うのが最も簡単です。

brew install wrk
Enter fullscreen mode Exit fullscreen mode

Apple Silicon環境では、ソースからビルドするとLuaJIT ARM64まわりで問題に遭遇する場合があります。まずはHomebrew版を使うのがおすすめです。

Linux

公式のaptパッケージはないため、ソースからビルドします。

まず、ビルドに必要なツールとOpenSSLヘッダーをインストールします。

sudo apt-get install build-essential libssl-dev git -y
Enter fullscreen mode Exit fullscreen mode

リポジトリをクローンしてコンパイルします。

git clone https://github.com/wg/wrk.git
cd wrk
make
Enter fullscreen mode Exit fullscreen mode

wrkバイナリが生成されます。どこからでも実行できるようにPATH配下へコピーします。

sudo cp wrk /usr/local/bin
Enter fullscreen mode Exit fullscreen mode

動作確認します。

wrk --version
Enter fullscreen mode Exit fullscreen mode

基本コマンド

wrkの基本形は次のとおりです。

wrk -t12 -c400 -d30s http://127.0.0.1:8080/index.html
Enter fullscreen mode Exit fullscreen mode

主なオプションは次の3つです。

  • -t, --threads: 起動するOSスレッド数。CPUコア数に合わせるのが初期値として扱いやすいです。
  • -c, --connections: 全スレッド合計のHTTP接続数。同時クライアント数の目安になります。
  • -d, --duration: テスト時間。30s2m2hのように指定できます。

実運用では、次の2つもほぼ必ず付けます。

  • --latency: レイテンシのパーセンタイルを出力します。平均値だけではテールレイテンシを見落とします。
  • --timeout: 指定時間内に応答がないリクエストをタイムアウトとして扱います。例: --timeout 2s

例:

wrk -t8 -c200 -d30s --latency http://localhost:3000/api/users
Enter fullscreen mode Exit fullscreen mode

これは、8スレッド、200接続で30秒間負荷をかけ、レイテンシ分布を出力します。

出力の読み方

wrkの出力例です。

Running 5s test @ http://10.135.232.163:3000
  2 threads and 5 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     3.82ms    2.64ms  26.68ms   85.81%
    Req/Sec   550.90    202.40     0.98k    68.00%
  5494 requests in 5.01s, 1.05MB read
Requests/sec:   1096.54
Transfer/sec:    215.24KB
Enter fullscreen mode Exit fullscreen mode

まず見るべきは最後の2行です。

Requests/sec はスループットです。サーバーが1秒あたり平均何リクエストを完了したかを示します。この例では約1,096 req/secです。コード変更前後やインフラ変更前後の比較に使います。

Transfer/sec は1秒あたりの転送量です。レスポンスサイズが大きいAPIや、帯域幅がボトルネックになっている可能性がある場合に確認します。

次に、Thread Statsを見ます。

  • Latency: 平均レイテンシ、標準偏差、最大値を示します。この例では平均3.82msですが、最大26.68msまで跳ねています。
  • Req/Sec: スレッドごとのリクエストレートです。ばらつきが大きい場合、負荷生成側またはサーバー側に偏りがある可能性があります。
  • +/- Stdev: サンプルのうち1標準偏差内に収まった割合です。低いほどばらつきが大きいことを示します。

--latencyを付けると、次のようなパーセンタイルが出力されます。

  Latency Distribution
     50%    3.21ms
     75%    4.86ms
     90%    7.09ms
     99%   14.13ms
Enter fullscreen mode Exit fullscreen mode

特に見るべきは99パーセンタイルです。平均が低くても、99パーセンタイルが高い場合、一部のユーザーはかなり遅いレスポンスを受けています。

POSTリクエストとカスタムヘッダーを送信する

デフォルトでは、wrkはGETリクエストを送信します。

POST、JSONボディ、カスタムヘッダーを使う場合は、Luaスクリプトを作成して-sで指定します。

post.luaを作成します。

wrk.method = "POST"
wrk.body   = '{"name": "Ada", "role": "engineer"}'
wrk.headers["Content-Type"] = "application/json"
Enter fullscreen mode Exit fullscreen mode

実行します。

wrk -t4 -c100 -d30s -s post.lua --latency http://localhost:3000/api/users
Enter fullscreen mode Exit fullscreen mode

フォームエンコードされたPOSTを送る場合は、次のようにします。

wrk.method = "POST"
wrk.body   = "foo=bar&baz=quux"
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"
Enter fullscreen mode Exit fullscreen mode

ヘッダーだけを追加したい場合は、Luaスクリプトではなく-Hでも指定できます。

wrk -t4 -c100 -d30s \
  -H "Authorization: Bearer TOKEN123" \
  --latency \
  http://localhost:3000/api/protected
Enter fullscreen mode Exit fullscreen mode

使い分けは次のとおりです。

  • ヘッダーを1〜2個追加するだけ: -H
  • POST/PUT/PATCHを使う: Luaスクリプト
  • リクエストボディを送る: Luaスクリプト
  • リクエストごとに値を変える: Luaスクリプト

負荷を段階的に上げる

いきなり大きな同時接続数で実行するより、段階的に上げるほうがボトルネックを見つけやすくなります。

例:

wrk -t4 -c50  -d30s --latency http://localhost:3000/api/users
wrk -t4 -c100 -d30s --latency http://localhost:3000/api/users
wrk -t4 -c200 -d30s --latency http://localhost:3000/api/users
wrk -t4 -c400 -d30s --latency http://localhost:3000/api/users
Enter fullscreen mode Exit fullscreen mode

見るべき変化は次のとおりです。

  • Requests/secがどこで頭打ちになるか
  • 99パーセンタイルがどこから急上昇するか
  • タイムアウトや接続エラーが出始めるか
  • サーバーCPU、メモリ、DB接続数がどこで限界に近づくか
  • wrkを実行しているクライアント側がCPUボトルネックになっていないか

負荷生成側が限界に達していると、サーバーの限界ではなくwrk実行マシンの限界を測ってしまいます。

制限事項:wrkは正しさを検証しない

wrkは、サーバーがどれだけ速く応答したかを測定します。しかし、その応答が正しいかどうかは検証しません。

たとえば、すべてのリクエストにHTTP 500を返すエンドポイントにwrkを向けても、きれいなRequests/secが出る場合があります。wrkは完了したHTTP交換を数えるだけで、次のような検証はしません。

  • ステータスコードが200か
  • JSONフィールドが期待どおりか
  • レスポンススキーマが正しいか
  • ビジネスロジックが成立しているか
  • API契約が壊れていないか

そのため、wrkは「負荷下で十分に速いか?」には答えられますが、「正しく動いているか?」には答えられません。

壊れたエンドポイントの負荷テスト結果は信頼できません。実際のワークフローでは、ベンチマークツールと機能テストスイートを組み合わせます。

Apidogと機能テストを組み合わせる

実装時の流れは、次の2段階に分けると安全です。

  1. 機能テストで正しさを確認する
  2. wrkでスループットとレイテンシを測定する

1. まず機能テストを実行する

エンドポイントの速度を測る前に、正しく動作していることを確認します。

Apidogでは、実際のリクエストを送信し、ステータスコード、JSONフィールド、レスポンススキーマ、ビジネスロジックを検証するテストシナリオを作成できます。

たとえば、次のような確認を先に行います。

  • POST /api/usersが201を返す
  • レスポンスにidが含まれる
  • GET /api/users/{id}で作成したユーザーを取得できる
  • 必須フィールドがない場合は400を返す
  • 認証なしの場合は401を返す

この層で、wrkだけでは見つけられない壊れたレスポンスを検出します。

2. 次にwrkでベンチマークする

機能テストが通ったら、同じエンドポイントに対してwrkを実行します。

wrk -t8 -c200 -d30s --latency http://localhost:3000/api/users
Enter fullscreen mode Exit fullscreen mode

Apidogには組み込みのパフォーマンステストもあります。機能テストと負荷テストを同じ場所で管理したい場合に便利です。一方、wrkはCLIで素早く生のベンチマークを取りたい場合に向いています。

CIで機能テストを先に実行する

機能テストはローカルだけでなくCIでも実行します。Apidog CLIはヘッドレスで動作するため、Node.jsを実行できるCIステップに組み込めます。

インストールします。

npm install -g apidog-cli
Enter fullscreen mode Exit fullscreen mode

保存済みのシナリオまたはスイートをIDで実行します。

apidog run \
  --access-token "$APIDOG_ACCESS_TOKEN" \
  -t <scenarioOrSuiteId> \
  -e <environmentId> \
  -r cli,html,junit
Enter fullscreen mode Exit fullscreen mode

主なオプションは次のとおりです。

  • -t: 実行するシナリオ、フォルダ、またはスイートのID
  • -e: 環境ID
  • -r: レポート形式。clihtmljsonjunitなど
  • -dまたは--iteration-data: データ駆動テスト用のファイルパスまたはテストデータID

JUnit出力を使うと、多くのCIシステムでテスト結果をそのまま合否ゲートとして扱えます。

Apidog CLIは保存済みのApidogシナリオとスイートを実行するためのツールです。ロードジェネレーターではありません。役割は次のように分けます。

  • Apidog CLI: 正しさのゲート
  • wrk: スループットとレイテンシの測定

CI/CDへの組み込み例は、CLI CI/CDウォークスルーまたはGitHub Actionsガイドを参照してください。その他のオプションは完全なCLIリファレンスにまとまっています。

よくある質問

wrkとab(ApacheBench)の違いは何ですか?

どちらもHTTP負荷を発生させ、毎秒のリクエスト数を報告します。

wrkはマルチスレッドでイベントループを使うため、1台のマシンからより多くの負荷を生成しやすく、高い同時実行性を扱いやすいです。abはシングルスレッドです。

単一マシンから高負荷をかける場合は、通常wrkのほうがスケールしやすいです。ただし、どちらもレスポンスの正しさは検証しません。

いくつのスレッドと接続を使うべきですか?

まずはCPUコアあたり1スレッドから始めます。

8コアのマシンで200同時クライアントをシミュレートするなら、次のようにします。

wrk -t8 -c200 -d30s --latency http://localhost:3000/api/users
Enter fullscreen mode Exit fullscreen mode

接続数は段階的に増やします。

wrk -t8 -c100 -d30s --latency http://localhost:3000/api/users
wrk -t8 -c200 -d30s --latency http://localhost:3000/api/users
wrk -t8 -c400 -d30s --latency http://localhost:3000/api/users
Enter fullscreen mode Exit fullscreen mode

同時に、wrkを実行しているクライアントマシンのCPU使用率も確認してください。wrk側がCPUバウンドになると、サーバーではなく負荷生成器の限界を測ってしまいます。

wrkはHTTPSエンドポイントをテストできますか?

はい。https://のURLを指定すればTLSを処理します。

wrk -t4 -c100 -d30s --latency https://example.com/api/users
Enter fullscreen mode Exit fullscreen mode

Linuxでビルドするときにlibssl-devが必要なのはこのためです。TLSハンドシェイクにはCPUコストがあるため、HTTPよりHTTPSのほうがスループットは低くなることがあります。

wrkはレスポンスボディやステータスコードを検証しますか?

いいえ。wrkは完了したHTTP交換を数え、時間を測定するだけです。

ステータスコードやボディをアサートしないため、エラーを返すエンドポイントでも高いRequests/secを報告することがあります。

先にApidog CLIなどで機能テストを実行し、正しく動作していることを確認してからwrkで測定してください。

負荷テストはどれくらいの時間実行すべきですか?

簡単な確認なら数秒でも傾向は見えます。ただし、安定した数値を見るには30秒から数分程度実行するほうがよいです。

まずは次をデフォルトにします。

wrk -t8 -c200 -d30s --latency http://localhost:3000/api/users
Enter fullscreen mode Exit fullscreen mode

JITコンパイル、コールドキャッシュ、接続プールの初期化などの影響を避けたい場合は、ウォームアップ実行をしてから本測定します。

# ウォームアップ
wrk -t8 -c200 -d15s http://localhost:3000/api/users

# 本測定
wrk -t8 -c200 -d60s --latency http://localhost:3000/api/users
Enter fullscreen mode Exit fullscreen mode

メモリリークや長時間稼働時の劣化を調べる場合は、さらに長く実行します。

Top comments (0)