`wrk` を使用して vllm OpenAI API インターフェースの負荷テストを実施：POST リクエストの性能をテスト結果と共に分析


Web サービスの開発とデプロイにおいて、負荷テストは高負荷状況下での安定性を確保するための重要なステップです。`wrk` は、マルチスレッドと Lua スクリプト拡張をサポートする効率的な HTTP 負荷テストツールで、負荷テストに最適です。本記事では、実際のテスト結果を交えながら、`wrk` を使用して POST リクエストの負荷テストを実施する方法と、テストデータの分析について詳しく説明します。

## 1. `wrk` のインストール

まず、システムに `wrk` をインストールします。Debian ベースのシステム（例: Ubuntu）では、以下のコマンドでインストールできます。

```bash
sudo apt update
sudo apt install wrk
```

インストールが完了したら、`wrk --version` コマンドでインストールが成功したか確認できます。

## 2. POST リクエストの Body データを準備する

POST リクエストの負荷テストを行う際、通常はリクエストの Body データを指定する必要があります。Lua スクリプトを使用してこれらのデータを定義できます。例えば、`./post.lua` という Lua ファイルを作成し、以下の内容を記述します。

```lua
wrk.method = "POST"
wrk.headers["Content-Type"] = "application/json"
wrk.headers["Authorization"] = "Bearer YOUR_API_KEY"  -- YOUR_API_KEY を実際の API キーに置き換える
wrk.body = [[
    {
       "model": "gpt-4",
       "messages": [
           {"role": "system", "content": "あなたはアシスタントです。"},
           {"role": "user", "content": "2008年北京オリンピックで、中国は金メダルを何個獲得しましたか？"}
       ]
   }
]]
```

このスクリプトでは、リクエストメソッドを `POST` に設定し、`Content-Type` を `application/json` に指定し、リクエストの Body データを定義しています。

## 3. 負荷テストの実行

次に、`wrk` を使用して負荷テストを実行します。サービスが `http://localhost:8000/v1/chat/completions` で動作していると仮定し、以下のコマンドでテストを実施します。3 つの異なる設定でのテスト結果を記録しました。

### テスト 1：シングルスレッド、シングルコネクション

まず、シングルスレッドとシングルコネクションでテストを行います。コマンドは以下の通りです。

```bash
wrk -t1 -c1 -d10s -s ./post.lua http://localhost:8000/v1/chat/completions
```

**テスト結果：**

```
Running 10s test @ http://localhost:8000/v1/chat/completions
  1 threads and 1 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   246.58ms  274.18ms   1.43s    88.71%
    Req/Sec     7.65      3.12    10.00     85.19%
  54 requests in 10.00s, 30.83KB read
Requests/sec:      5.40
Transfer/sec:      3.08KB
```

**分析：**
- 平均レイテンシ（Latency）は 246.58ms、最大レイテンシは 1.43s。
- 1 秒あたりのリクエスト数（Requests/sec）は 5.40。
- 合計 54 リクエストが完了。

### テスト 2：シングルスレッド、10 コネクション

次に、コネクション数を 10 に増やしてテストを行います。コマンドは以下の通りです。

```bash
wrk -t1 -c10 -d10s -s ./resources/2_9/post.lua http://localhost:8000/v1/chat/completions
```

**テスト結果：**

```
Running 10s test @ http://localhost:8000/v1/chat/completions
  1 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   259.59ms  146.24ms   1.09s    85.20%
    Req/Sec    40.76     13.56    80.00     75.00%
  397 requests in 10.01s, 229.39KB read
Requests/sec:     39.66
Transfer/sec:     22.92KB
```

**分析：**
- 平均レイテンシは 259.59ms、最大レイテンシは 1.09s。
- 1 秒あたりのリクエスト数は 39.66。
- 合計 397 リクエストが完了。

### テスト 3：シングルスレッド、20 コネクション

最後に、コネクション数を 20 に増やしてテストを行います。コマンドは以下の通りです。

```bash
wrk -t1 -c20 -d10s -s ./resources/2_9/post.lua http://localhost:8000/v1/chat/completions
```

**テスト結果：**

```
Running 10s test @ http://localhost:8000/v1/chat/completions
  1 threads and 20 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   314.60ms  164.85ms   1.74s    83.87%
    Req/Sec    59.45     17.00   100.00     64.58%
  582 requests in 10.01s, 332.09KB read
  Socket errors: connect 0, read 0, write 0, timeout 2
Requests/sec:     58.15
Transfer/sec:     33.18KB
```

**分析：**
- 平均レイテンシは 314.60ms、最大レイテンシは 1.74s。
- 1 秒あたりのリクエスト数は 58.15。
- 合計 582 リクエストが完了しましたが、2 リクエストがタイムアウトしました。

## 4. テスト結果のまとめ

上記の 3 つのテストから、以下の結論が得られます。

1. **レイテンシの分析**：
   - コネクション数が増えるにつれ、平均レイテンシと最大レイテンシが上昇します。例えば、シングルコネクション時の平均レイテンシは 246.58ms でしたが、20 コネクション時には 314.60ms に増加しました。
   - これは、サービスがより多くの同時リクエストを処理する際に、応答時間が長くなることを示しています。

2. **スループットの分析**：
   - 1 秒あたりのリクエスト数は、コネクション数が増えるにつれて大幅に向上します。シングルコネクション時は 5.40、10 コネクション時は 39.66、20 コネクション時は 58.15 でした。
   - これは、サービスがより多くの同時リクエストを処理できることを示していますが、レイテンシの増加とのトレードオフが必要です。

3. **エラーの分析**：
   - 20 コネクションのテストでは、2 つのタイムアウトエラーが発生しました。これは、高負荷状況下でサービスの処理能力が限界に達した可能性を示しています。

## 5. 最適化の提案

テスト結果に基づき、以下の最適化対策を検討できます。

- **サービスインスタンスの増加**：ロードバランサーを使用して複数のサービスインスタンスにリクエストを分散させ、負荷を分担します。
- **コードロジックの最適化**：キャッシュや非同期処理などを通じて、リクエスト処理時間を短縮します。
- **サーバー設定の調整**：CPU やメモリなどのリソースを増強し、サービスの処理能力を向上させます。

## 6. まとめ

以下は、3 回のテスト結果をまとめた表です。比較と分析に役立ちます。

| **設定**         | **スレッド数 (`-t`)** | **コネクション数 (`-c`)** | **平均レイテンシ (Avg Latency)** | **最大レイテンシ (Max Latency)** | **1 秒あたりのリクエスト数 (Requests/sec)** | **総リクエスト数** | **総転送データ** | **転送速度 (Transfer/sec)** | **エラー数** |
|------------------|----------------------|--------------------------|----------------------------------|----------------------------------|---------------------------------------------|--------------------|------------------|-----------------------------|--------------|
| `wrk -t1 -c1`    | 1                    | 1                        | 246.58ms                         | 1.43s                            | 5.40                                        | 54                 | 30.83KB          | 3.08KB                      | 0            |
| `wrk -t1 -c10`   | 1                    | 10                       | 259.59ms                         | 1.09s                            | 39.66                                       | 397                | 229.39KB         | 22.92KB                     | 0            |
| `wrk -t1 -c20`   | 1                    | 20                       | 314.60ms                         | 1.74s                            | 58.15                                       | 582                | 332.09KB         | 33.18KB                     | 2 (timeout)  |

---

**表の分析**

1. **レイテンシ**：
   - コネクション数が増えるにつれ、平均レイテンシと最大レイテンシが徐々に上昇します。
     - シングルコネクション時、平均レイテンシは 246.58ms。
     - 10 コネクション時、平均レイテンシは 259.59ms。
     - 20 コネクション時、平均レイテンシは 314.60ms。
   - これは、サービスがより多くの同時リクエストを処理する際に、応答時間が長くなることを示しています。

2. **スループット**：
   - 1 秒あたりのリクエスト数（Requests/sec）は、コネクション数が増えるにつれて大幅に向上します。
     - シングルコネクション時、1 秒あたり 5.40 リクエスト。
     - 10 コネクション時、1 秒あたり 39.66 リクエスト。
     - 20 コネクション時、1 秒あたり 58.15 リクエスト。
   - これは、サービスがより多くの同時リクエストを処理できることを示していますが、レイテンシの増加とのトレードオフが必要です。

3. **エラー数**：
   - 20 コネクションのテストでは、2 つのタイムアウトエラーが発生しました。これは、高負荷状況下でサービスの処理能力が限界に達した可能性を示しています。

4. **転送データ**：
   - リクエスト数が増えるにつれ、総転送データと転送速度も向上します。
     - シングルコネクション時、転送速度は 3.08KB/s。
     - 10 コネクション時、転送速度は 22.92KB/s。
     - 20 コネクション時、転送速度は 33.18KB/s。

---

表から明らかなように：
- コネクション数を増やすことで、サービスのスループットが大幅に向上しますが、レイテンシも増加します。
- 高負荷状況下（例: 20 コネクション）では、タイムアウトエラーが発生する可能性があり、サービスがさらなる最適化を必要とすることを示しています。

この表形式の表示は、テストレポートや性能分析ドキュメントに非常に適しており、異なる設定下での性能を直感的に比較できます。

本記事では、`wrk` を使用して POST リクエストの負荷テストを実施し、コネクション数を増やすことで異なる同時接続シナリオをシミュレートする方法を紹介しました。テスト結果から、サービスが高負荷状況下でレイテンシが増加する一方で、スループットが大幅に向上することがわかりました。適切な最適化対策を講じることで、サービスの性能と安定性をさらに向上させることができます。

---

**参考リンク：**
- [wrk GitHub リポジトリ](https://github.com/wg/wrk)
- [Lua スクリプト作成ガイド](https://www.lua.org/pil/contents.html)