メインコンテンツまでスキップ

負荷テストシナリオ設計の原則

適切な負荷テストシナリオを設計するための考え方と原則を学びます。


なぜシナリオ設計が重要か

┌─────────────────────────────────────────────────────────────┐
│ シナリオ次第で結果が全く変わる │
├─────────────────────────────────────────────────────────────┤
│ │
│ 同じシステムでも: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 悪いシナリオ → 10,000 req/s 出る(本番では出ない) │ │
│ │ 良いシナリオ → 500 req/s で飽和(本番の実態に近い) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 差が生まれる原因: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ・キャッシュヒット率の違い │ │
│ │ ・DBアクセスパターンの違い │ │
│ │ ・リクエスト間隔の違い │ │
│ │ ・処理の重さの違い(軽いAPIだけ vs 重いAPIも含む) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 目的: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 本番環境の挙動を可能な限り再現し、 │ │
│ │ 信頼できる性能データを得ること │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

テスト種類と目的

シナリオを設計する前に、「何を知りたいのか」を明確にしましょう。

テスト種類目的シナリオ概要
Load想定負荷でSLOを満たせるか確認ランプアップ → 定常負荷 → クールダウン。現実的な Think Time、本番のエンドポイント比率を再現
Stress限界を探る(どこで壊れるか)段階的に負荷を上げ続ける、または限界付近で固定して観察。ボトルネック特定が目的なら Think Time 短めでもOK
Spike急激な負荷変動への耐性確認低負荷 → 急激に高負荷 → 低負荷。オートスケールの動作確認
Soak長時間でメモリリーク等を検出中程度の負荷を長時間(4〜24時間)継続。メモリ使用量、GC頻度をモニタリング

目的によってシナリオの作り方が変わる

  • Load テスト: 本番を忠実に再現することが重要
  • Stress テスト: ボトルネック特定が目的なら、シンプルなシナリオでもOK
  • Spike テスト: 負荷パターン(急激な変動)が重要
  • Soak テスト: 長時間安定して動くことが重要

4つの設計原則

原則1: 実際のユーザー行動を再現する

┌─────────────────────────────────────────────────────────────┐
│ ユーザー行動の再現 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 考え方: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 「このAPIは何のために呼ばれるか」を考える │ │
│ │ │ │
│ │ 単体で呼ばれることはない: │ │
│ │ ・トークン発行 → その後にAPIアクセスがある │ │
│ │ ・認可リクエスト → ログイン → 同意 → コード交換 │ │
│ │ ・イントロスペクション → リソースサーバーが検証用に │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 悪い例 vs 良い例: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ❌ /oauth/token を単独で連打 │ │
│ │ → キャッシュ効きすぎ、非現実的 │ │
│ │ │ │
│ │ ✅ 認可 → 認証 → 同意 → トークン → API呼出 │ │
│ │ → 実際のフローを再現 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ OIDCにおける典型的なフロー: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ [認可コードフロー] │ │
│ │ 認可リクエスト → ログイン → 同意 → トークン取得 │ │
│ │ ↓ │ │
│ │ [リソースアクセス] │ │
│ │ UserInfo取得 or API呼出 │ │
│ │ ↓ │ │
│ │ [トークン更新] │ │
│ │ リフレッシュトークンでアクセストークン再取得 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

原則2: Think Time を入れる

┌─────────────────────────────────────────────────────────────┐
│ Think Time(操作間隔) │
├─────────────────────────────────────────────────────────────┤
│ │
│ Think Time とは: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ユーザーが次の操作を行うまでの時間 │ │
│ │ ・画面を見る時間 │ │
│ │ ・フォームに入力する時間 │ │
│ │ ・考える時間 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ なぜ重要か: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Think Time なし(連続リクエスト): │ │
│ │ VU1: ●●●●●●●●●●●●●●●●●●●●→ │ │
│ │ → 1 VU で 20 req/s 以上出る │ │
│ │ → 20 VUs で 400 req/s(非現実的) │ │
│ │ │ │
│ │ Think Time あり(500ms間隔): │ │
│ │ VU1: ●───●───●───●───●───●→ │ │
│ │ → 1 VU で 約2 req/s │ │
│ │ → 20 VUs で 約40 req/s(現実的) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 操作種別ごとの目安: │
│ ┌──────────────────────┬─────────────────────────────┐ │
│ │ 操作 │ Think Time │ │
│ ├──────────────────────┼─────────────────────────────┤ │
│ │ フォーム入力 │ 2〜5秒 │ │
│ │ 画面確認 │ 0.5〜2秒 │ │
│ │ ボタンクリック │ 0.1〜0.5秒 │ │
│ │ API間処理(自動) │ 0.05〜0.2秒 │ │
│ └──────────────────────┴─────────────────────────────┘ │
│ │
│ ポイント: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ・固定値ではなくランダムな範囲で設定する │ │
│ │ ・人間の操作は一定間隔ではない │ │
│ │ ・例: 0.5〜2秒の範囲でランダム │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

原則3: テストデータに多様性を持たせる

┌─────────────────────────────────────────────────────────────┐
│ テストデータの多様性 │
├─────────────────────────────────────────────────────────────┤
│ │
│ なぜ多様性が必要か: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 同じデータへのアクセスが集中すると: │ │
│ │ ・キャッシュが100%ヒット(本番では起きない) │ │
│ │ ・DBの同じ行にロック集中 │ │
│ │ ・本番より良い結果が出てしまう │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 多様性を持たせる対象: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ・クライアントID(複数クライアントを用意) │ │
│ │ ・ユーザー(複数ユーザーを用意) │ │
│ │ ・リクエストパラメータ(scope, state等) │ │
│ │ ・アクセス対象リソース(異なるIDに分散) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ データプールサイズの目安: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ VUs数に対して: │ │
│ │ ・ユーザー数: VUs × 10 以上 │ │
│ │ ・クライアント数: VUs × 5 以上 │ │
│ │ │ │
│ │ 例: 20 VUs の場合 │ │
│ │ ・ユーザー: 200人以上 │ │
│ │ ・クライアント: 100個以上 │ │
│ │ │ │
│ │ 同じデータに当たる確率: │ │
│ │ ・プール100件 → 1%の確率で衝突 │ │
│ │ ・プール1000件 → 0.1%の確率で衝突 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ キャッシュヒット率の調整: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 本番のキャッシュヒット率が分かっている場合: │ │
│ │ ・本番60%ヒット → プールサイズを調整して同程度に │ │
│ │ ・完全ランダムだとヒット率が下がりすぎる場合は │ │
│ │ 一部のデータに重み付けする │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

原則4: エンドポイント比率を考慮する

┌─────────────────────────────────────────────────────────────┐
│ 本番のアクセス比率を再現する │
├─────────────────────────────────────────────────────────────┤
│ │
│ なぜ比率が重要か: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ エンドポイントごとに処理の重さが異なる: │ │
│ │ ・introspection → 軽い(キャッシュ効く) │ │
│ │ ・password grant → 重い(bcrypt処理) │ │
│ │ ・認可フロー → 中程度(複数ステップ) │ │
│ │ │ │
│ │ 軽いAPIだけテストしても意味がない │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 比率の決め方: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. 本番のアクセスログを分析 │ │
│ │ 2. エンドポイントごとのリクエスト数を集計 │ │
│ │ 3. 比率に変換してシナリオに反映 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ OIDCシステムの典型的な比率例: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ エンドポイント │ 比率 │ 特性 │ │
│ │ ────────────────────────┼───────┼─────────────────│ │
│ │ トークンリフレッシュ │ 40% │ 中程度 │ │
│ │ イントロスペクション │ 25% │ 軽い │ │
│ │ 認可フロー全体 │ 20% │ 重い │ │
│ │ client_credentials │ 10% │ 軽い │ │
│ │ JWKS取得 │ 5% │ キャッシュ効く │ │
│ │ │ │
│ │ ※ システムの用途によって大きく異なる │ │
│ │ ※ M2M中心なら client_credentials が多い │ │
│ │ ※ ユーザー認証中心なら認可フローが多い │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

負荷パターンの設計

テスト種類に応じて、適切な負荷パターンを選びましょう。

基本パターン(Load テスト向け)

┌─────────────────────────────────────────────────────────────┐
│ ランプアップ → 定常負荷 → クールダウン │
├─────────────────────────────────────────────────────────────┤
│ │
│ 負荷 ↑ │
│ │ ┌─────┐ │
│ │ ┌───┘ │ ← 定常負荷 │
│ │ ┌───┘ │ │
│ │───┘ └───┐ │
│ │ ↑ウォームアップ │↓クールダウン │
│ └─────────────────────────→ 時間 │
│ │
│ [ウォームアップ] JIT/プール温め(30秒〜1分) │
│ [ランプアップ] 段階的に増加(各段階1〜2分) │
│ [定常負荷] 目標負荷で安定稼働(2〜5分) │
│ [クールダウン] 徐々に減少、回復性確認(30秒〜1分) │
│ │
└─────────────────────────────────────────────────────────────┘

テスト種類別のパターン

テスト種類負荷パターン説明
Loadランプアップ → 定常 → クールダウンSLO確認。飽和点の70%程度で定常負荷
Stress段階的に増加し続ける5→10→20→30→40... と上げて限界を探る
Spike低→急激に高→低10秒で10倍など急激な変動
Soak中程度で長時間維持4〜24時間の継続負荷

なぜウォームアップが必要か

┌─────────────────────────────────────────────────────────────┐
│ いきなり高負荷をかけると │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ・JVMがウォームアップされていない(JIT未最適化) │ │
│ │ ・コネクションプールが温まっていない │ │
│ │ ・初期の悪い結果に引っ張られる │ │
│ │ │ │
│ │ → 本来の性能より悪く見えてしまう │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

ローカル環境での注意点

┌─────────────────────────────────────────────────────────────┐
│ ローカル環境の限界を理解する │
├─────────────────────────────────────────────────────────────┤
│ │
│ ローカル環境の制約: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ・Docker のリソース制限(CPU, メモリ) │ │
│ │ ・DB接続プールのデフォルト値が小さい │ │
│ │ ・ネットワークが理想的すぎる(レイテンシほぼゼロ) │ │
│ │ ・k6実行マシン自体がボトルネックになりうる │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ VUs 20〜50 で飽和するのは正常: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ローカル環境では: │ │
│ │ ・HikariCP デフォルト 10 接続 → 20 VUs で枯渇 │ │
│ │ ・Docker CPU 2コア → 並列処理に限界 │ │
│ │ ・bcrypt処理 → CPU 100% に張り付く │ │
│ │ │ │
│ │ これは「システムが遅い」ではなく │ │
│ │ 「環境の限界」であることを理解する │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ローカルで見るべきもの: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ✅ ボトルネックの箇所(CPU? DB接続? 外部API?) │ │
│ │ ✅ 処理の内訳(どのステップが遅いか) │ │
│ │ ✅ エラーの種類(何が原因で失敗するか) │ │
│ │ ✅ 負荷増加に対する傾向(線形? 急激に悪化?) │ │
│ │ │ │
│ │ ❌ 絶対的なスループット値 │ │
│ │ ❌ 本番でのキャパシティ予測 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ローカルと本番の使い分け: │
│ ┌──────────────────┬───────────────────────────────────┐ │
│ │ 環境 │ 目的 │ │
│ ├──────────────────┼───────────────────────────────────┤ │
│ │ ローカル │ ボトルネック箇所の特定 │ │
│ │ │ シナリオの妥当性検証 │ │
│ │ │ 改善前後の比較(相対評価) │ │
│ ├──────────────────┼───────────────────────────────────┤ │
│ │ 本番同等環境 │ 実際のキャパシティ測定 │ │
│ │ │ SLO達成可否の判断 │ │
│ │ │ スケーリング計画の策定 │ │
│ └──────────────────┴───────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

シナリオ設計チェックリスト

┌─────────────────────────────────────────────────────────────┐
│ 設計時に確認すること │
├─────────────────────────────────────────────────────────────┤
│ │
│ □ テストの目的 │
│ ├── 何を知りたいか明確か(Load/Stress/Spike/Soak) │
│ └── 目的に合ったシナリオになっているか │
│ │
│ □ ユーザー行動の再現 │
│ ├── 実際のユースケースに基づいたフローか │
│ ├── 単一API連打になっていないか │
│ └── 前後の関連する処理を含めているか │
│ │
│ □ Think Time │
│ ├── 適切な間隔を入れているか │
│ ├── 固定値ではなくランダム範囲か │
│ └── 操作種別に応じた値になっているか │
│ │
│ □ テストデータ │
│ ├── 十分な数のユーザー/クライアントを用意したか │
│ ├── 同じデータに偏っていないか │
│ └── 本番に近いキャッシュヒット率になるか │
│ │
│ □ エンドポイント比率 │
│ ├── 本番のアクセス比率を分析したか │
│ ├── 重い処理と軽い処理のバランスは適切か │
│ └── 特定のAPIに偏っていないか │
│ │
│ □ 負荷パターン │
│ ├── テスト種類に合ったパターンか │
│ ├── ウォームアップ期間を設けているか │
│ └── クールダウンで回復性を確認できるか │
│ │
│ □ 環境の理解 │
│ ├── テスト環境の制約を把握しているか │
│ └── 結果の解釈方法を決めているか │
│ │
└─────────────────────────────────────────────────────────────┘

よくある失敗パターン

パターン症状原因対策
単一API連打異常に高いスループットキャッシュ100%ヒット実際のフローを再現、データに多様性
Think Time なし少ないVUsで大量リクエスト実際のユーザー行動と乖離操作種別に応じた待機時間
いきなり高負荷初期に大量エラーJVM/プールのウォームアップ不足低負荷から段階的に上げる
軽いAPIだけ良い結果だが本番で問題重い処理を含めていない本番の比率に基づいて全APIを含める
ローカル結果を本番に適用本番で問題発生環境差(リソース、ネットワーク)ローカルは傾向把握、本番同等環境で最終確認

次のステップ