API負荷テスト:ツール、戦略、ベストプラクティス
はじめに
APIは開発環境では完璧に動作します。すべての機能テストに合格します。そしてローンチすると、トラフィックが急増してすべてが崩壊します。レスポンスの遅延、タイムアウト、500エラー。これがAPI負荷テストが防ごうとするシナリオです。
API負荷テストは、パフォーマンスのボトルネックを特定し、キャパシティの限界を決定し、ストレス下での信頼性を確保するために、APIエンドポイントに対してリアルワールドのトラフィックパターンをシミュレートします。重要な問いに答えます:数千人のユーザーが同時にアクセスした場合、APIはどのようなパフォーマンスを発揮するか?
このガイドでは、負荷テスト戦略、利用可能な最良のツール、実用的な例、そしてAPIテストの規模でのベストプラクティスを取り上げます。
API負荷テストがなぜ重要か
機能テストはAPIが正しいレスポンスを返すことを検証します。負荷テストはプレッシャー下でもそうすることを検証します。負荷テストが明らかにすることは次のとおりです:
- 最大スループット:APIは1秒あたり何リクエストを処理できるか?
- レスポンスタイムの劣化:どの時点でレスポンスタイムが許容できなくなるか?
- ブレークポイント:APIがエラーを返し始めるのはいつか?
- リソースボトルネック:CPU、メモリ、データベース接続、ネットワークのどれが制限要因か?
- リカバリー動作:トラフィックスパイク後にAPIは適切に回復するか?
実際の影響
Amazonは100msのレイテンシーが売上の1%のコストになることを発見しました。Googleは検索結果の0.5秒の遅延がトラフィックを20%減少させることを発見しました。APIの場合も同様に重要で、遅いAPIは遅いアプリケーション、フラストレートしたユーザー、失われた収益を意味します。
API負荷テストの種類
1. ベースラインテスト
1人のユーザー(または少数)で実行してベースラインのレスポンスタイムを確立します。これが比較のための参照ポイントになります。
2. 負荷テスト
期待される本番トラフィックレベルをシミュレートします。例えば、1,000人の同時ユーザーを期待する場合は1,000人の仮想ユーザーでテストします。レスポンスタイムが許容範囲内であることを確認します。
3. ストレステスト
期待されるトラフィックを超えてブレークポイントを見つけます。APIが失敗し始めるまで徐々に負荷を増やします。これによりキャパシティの上限がわかります。
4. スパイクテスト
突然のトラフィック急増をシミュレートします。例えば、フラッシュセールやバイラルイベントなど。通常トラフィックから10倍または50倍の正常値への急激なジャンプをAPIがどのように処理するかをテストします。
5. ソークテスト(耐久テスト)
長期間(時間または日数)にわたって中程度の負荷を実行し、メモリリーク、接続プールの枯渇、その他の時間依存の問題を発見します。
6. ブレークポイントテスト
各レベルで一定時間保持しながら段階的に負荷を増加させ、パフォーマンスが劣化またはシステムが失敗する正確なポイントを見つけます。
主要なAPI負荷テストツール
k6(Grafana Labs)
k6はAPI負荷テストの開発者のお気に入りです。テストスクリプトにJavaScriptを使用し、CLIから実行し、CI/CDパイプラインとネイティブに統合します。
// k6-load-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 100 },
{ duration: '5m', target: 100 },
{ duration: '2m', target: 200 },
{ duration: '5m', target: 200 },
{ duration: '2m', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<500'],
http_req_failed: ['rate<0.01'],
},
};
export default function () {
const listRes = http.get('https://api.example.com/users');
check(listRes, {
'list status is 200': (r) => r.status === 200,
'list response time < 500ms': (r) => r.timings.duration < 500,
});
const payload = JSON.stringify({
name: 'Load Test User',
email: `user${Math.random()}@test.com`,
});
const createRes = http.post('https://api.example.com/users', payload, {
headers: { 'Content-Type': 'application/json' },
});
check(createRes, {
'create status is 201': (r) => r.status === 201,
});
sleep(1);
}
テストの実行:
k6 run k6-load-test.js
k6を選ぶ理由? 開発者フレンドリーなJavaScriptスクリプト、軽量CLI(JVM不要)、しきい値を持つ組み込みメトリクス、ダッシュボード用のネイティブGrafana統合。
Apache JMeter
JMeterは負荷テストのエンタープライズ標準です。幅広いプロトコルをサポートし、テストプラン構築のためのGUIを提供します。
# Run a JMeter test plan from CLI
jmeter -n -t api-load-test.jmx -l results.jtl -e -o report/
JMeterを選ぶ理由? プロトコルの多様性(HTTP、JDBC、JMS、FTP)、ノンコーダー向けGUI、複数マシンでの分散テスト、広範なプラグインエコシステム。
Gatling
GatlingはロードテストスクリプトにScalaベースのDSLを使用します。詳細なHTMLレポートを生成し、高い並行性を効率的に処理します。
// Gatling simulation (Scala)
class ApiLoadTest extends Simulation {
val httpProtocol = http
.baseUrl("https://api.example.com")
.acceptHeader("application/json")
val scn = scenario("API Load Test")
.exec(
http("Get Users")
.get("/users")
.check(status.is(200))
)
.pause(1)
.exec(
http("Get Single User")
.get("/users/1")
.check(status.is(200))
)
setUp(
scn.inject(
rampUsers(200).during(120)
)
).protocols(httpProtocol)
.assertions(
global.responseTime.percentile3.lt(500),
global.successfulRequests.percent.gt(99)
)
}
Gatlingを選ぶ理由? 優れたHTMLレポート、効率的な非同期アーキテクチャ、表現力豊かなテストのためのScala DSL、CI/CDフレンドリーなCLI実行。
Locust(Python)
Locustはプレーンなシンプルなコードで負荷テストを書けます。Pythonチームに最適で、リアルタイムでテストを監視するWebベースUIを提供します。
# locustfile.py
from locust import HttpUser, task, between
class APIUser(HttpUser):
wait_time = between(1, 3)
@task(3)
def get_users(self):
self.client.get("/users", name="GET /users")
@task(1)
def create_user(self):
self.client.post("/users", json={
"name": "Load Test User",
"email": f"user{id(self)}@test.com"
}, name="POST /users")
@task(2)
def get_single_user(self):
self.client.get("/users/1", name="GET /users/:id")
# Run Locust
locust -f locustfile.py --host=https://api.example.com --users 200 --spawn-rate 10 --run-time 5m --headless
Locustを選ぶ理由? 純粋なPython(学習するDSLなし)、分散テストが組み込み、リアルタイムWebダッシュボード、カスタムロジックで簡単に拡張可能。
ツール比較
| ツール | 言語 | GUI | 分散 | レポート | 最適な用途 |
|---|---|---|---|---|---|
| k6 | JavaScript | なし | クラウドのみ | CLI + Grafana | 開発チーム、CI/CD |
| JMeter | XML/GUI | あり | あり | HTML + プラグイン | エンタープライズ、プロトコルの多様性 |
| Gatling | Scala | なし | エンタープライズ | 優れたHTML | 高並行性テスト |
| Locust | Python | Web UI | あり | Webダッシュボード | Pythonチーム |
負荷テスト戦略:ステップバイステップ
ステップ1:パフォーマンス要件の定義
テストを1つ書く前に、パフォーマンスSLAを定義してください:
- レスポンスタイム目標:例えばp95 < 500ms、p99 < 1s
- スループット目標:例えば1,000リクエスト/秒
- エラーレートの限界:例えば通常負荷下で < 0.1%
- 同時ユーザー目標:例えば5,000人の同時ユーザー
ステップ2:重要なAPIエンドポイントの特定
すべてのエンドポイントが負荷テストを必要とするわけではありません。次のものに集中してください:
- 高トラフィックエンドポイント(ログイン、検索、商品一覧)
- データ集約エンドポイント(レポート、エクスポート、集計)
- 決済とトランザクションエンドポイント
- データベース書き込みのあるエンドポイント
ステップ3:現実的なテストシナリオの作成
負荷テストは単一のエンドポイントを叩くだけでなく、実際のユーザー行動をシミュレートする必要があります:
- 読み取りと書き込みの操作の混合(通常80/20または90/10)
- リクエスト間の現実的な待ち時間(1-5秒)
- 多様なリクエストペイロード(すべてのリクエストに同一データを使用しない)
- 適切な認証フロー
ステップ4:本番に近い環境でのテスト実行
ローカルマシンまたはスケールダウンされたステージング環境に対する負荷テストは誤解を招く結果をもたらします。インフラ、データベースサイズ、設定の面で本番に合致する環境を使用してください。
ステップ5:すべてを監視する
負荷テスト中は、APIレスポンスだけでなく次のものも監視してください:
- サーバーCPUとメモリの使用量
- データベースクエリ時間と接続プール
- ネットワーク帯域幅とレイテンシー
- キュー深度とメッセージ処理レート
- キャッシュヒット率
ステップ6:結果の分析と対応
結果のパターンを探してください:
- レスポンスタイムは負荷とともに線形的に増加するか指数的に増加するか?
- どのエンドポイントが最初に劣化するか?
- エラーは特定のエンドポイントに集中しているか、分散しているか?
- リソースの枯渇(CPU、メモリ、接続)が見られるか?
CI/CDへの負荷テストの統合
負荷テストは1回限りのアクティビティであってはなりません。CI/CDパイプラインに統合して、パフォーマンスの回帰を早期にキャッチしてください。
# GitHub Actions - k6 load test
name: API Load Tests
on:
push:
branches: [main]
schedule:
- cron: '0 2 * * 1'
jobs:
load-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install k6
run: |
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D68
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update && sudo apt-get install k6
- name: Run load test
run: k6 run --out json=results.json load-tests/api-load.js
- name: Check thresholds
run: |
if grep -q '"thresholds".*"fail"' results.json; then
echo "Load test thresholds failed!"
exit 1
fi
よくあるAPIパフォーマンスの問題と修正方法
N+1クエリ問題
APIエンドポイントがリストの各アイテムに対して1回のデータベースクエリを実行する場合、パフォーマンスはデータサイズに比例して劣化します。修正方法:イーガーローディング、バッチクエリ、またはデータローダーを使用します。
データベースインデックスの欠如
遅いクエリはAPIレイテンシーの最も一般的な原因です。WHERE、JOIN、ORDER BY句で使用される列にインデックスが存在することを確認してください。
キャッシュなし
APIがデータベースから同じデータを繰り返し取得する場合は、キャッシュレイヤーを追加してください:インメモリキャッシュ(Redis)、HTTPキャッシュヘッダー、静的レスポンスのCDNキャッシング。
接続プールの枯渇
負荷下でAPIがデータベース接続を使い果たす可能性があります。接続プールを適切に設定し、タイムアウト処理を追加してください。
同期操作
長時間実行される操作(メール送信、ファイル処理、レポート生成)は、APIレスポンスをブロックするのではなく、バックグラウンドジョブキューに移動すべきです。
関連: APIレイテンシーとは?
負荷テストと機能テストの組み合わせ
負荷テストは機能APIテスト、セキュリティテスト、統合テストと組み合わせると最も効果的です。Qodex.aiで機能テストスイートを自動化し、k6またはJMeterで負荷テストを行うことで、包括的なカバレッジが得られます。
利用可能なツールの完全な概要については、APIテストツール比較をご覧ください。
よくある質問
負荷テストとストレステストの違いは何ですか?
負荷テストは期待される本番トラフィックをシミュレートして、パフォーマンスがSLAを満たすことを確認します。ストレステストは期待される限界を超えてブレークポイントを見つけます。両方とも重要で、負荷テストは通常の動作を検証し、ストレステストはシステムがどのように失敗して回復するかを明らかにします。
負荷テストで何人の仮想ユーザーを使用すべきですか?
期待されるピーク同時ユーザー数から始め、その2倍と5倍でテストします。例えば、ピーク時に1,000人の同時ユーザーを期待する場合は、キャパシティのヘッドルームを理解するために1,000、2,000、5,000人の仮想ユーザーでテストします。
API負荷テストはどのくらいの頻度で実行すべきですか?
すべてのデプロイメントで軽量な負荷テスト(ベースライン+標準負荷)を実行します。毎週または主要なリリース前にフルストレステストとソークテストを実行します。テストをCI/CDに統合して、パフォーマンスの回帰をすぐにキャッチします。
PostmanでREST APIを負荷テストできますか?
PostmanはCollection Runnerでパフォーマンステストを追加しましたが、専用ツールと比べて制限があります。本格的な負荷テストには、高い同時負荷の生成のために設計されたk6、JMeter、Gatling、またはLocustを使用してください。
API負荷テスト中にどのメトリクスを追跡すべきですか?
レスポンスタイム(p50、p95、p99)、スループット(リクエスト/秒)、エラーレート、リソース使用率(CPU、メモリ、DB接続)を追跡します。各メトリクスのしきい値を設定し、しきい値を超えた場合はテストを失敗させます。
GraphQL APIの負荷テストをどのように行いますか?
同じツール(k6、JMeter、Gatling)がGraphQL APIでも機能します。クエリペイロードをGraphQLエンドポイントにPOSTリクエストとして送信します。GraphQLクエリはコストが大きく異なる場合があることに注意し、シンプルなクエリと複数のJOINを伴う複雑なネストされたクエリの両方をテストしてください。





