performance-testing について
開発・QAチーム向けのパフォーマンステストスキルで、バックエンドの負荷テストとフロントエンドのWeb性能を扱います。k6のしきい値とLighthouse CIのアサーションでCIを制御し、ラボ測定と実ユーザーデータを区別します。
主な機能
- 01k6による負荷とスループットのテスト
- 02定常・ランプ・スパイク・ソークの設定
- 03性能しきい値によるCIゲート
- 04Lighthouse CIによるWeb性能予算の確認
- 05LCP、INP、CLSの測定
- 06手順に沿ったボトルネック調査
活用シーン
- 01想定トラフィック下でのバックエンド検証
- 02性能予算超過時のビルド阻止
- 03ページのCore Web Vitals確認
- 04性能予算超過の原因調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ petrkindlmann/ qa-skills --skill performance-testingインストール後、Claude Code で/performance-testingを入力して読み込まれたか確認します。
同梱ファイルreferences/recipes.md
詳細
クイックガイド
| 目的 | 移動先 |
|---|---|
| トラフィック負荷時のバックエンドの処理能力/スループット/レイテンシをテストする | k6 Load Testing → references/recipes.md |
| 負荷パターン(一定/ランプアップ/スパイク/長時間維持)を選ぶ | Load Profiles テーブル |
| 実際のユーザーにとってのフロントエンドの速度(LCP、INP、CLS)を測定する | Web Performance + Core Web Vitals |
| CI でページのパフォーマンスをゲートする | Lighthouse CI(INP ではなく TBT をゲートします。以下を参照) |
| 予算を超過し、原因を特定する必要がある | Bottleneck Identification |
| 既存のスイートを k6 v1 から v2 に移行する | k6 v2 Migration のコールアウト |
検討事項
まず .agents/qa-project-context.md を確認してください。ファイルが存在する場合はその内容を使い、すでに回答されている質問は省略してください。
測定対象
- ウェブパフォーマンスと負荷テストのどちらですか? ウェブパフォーマンスは、ユーザーが体感する速度(Core Web Vitals、ページ読み込み)を測定します。負荷テストは、サーバーの処理能力(RPS、負荷時のレイテンシ)を測定します。ほとんどの製品には両方が必要です。
- パフォーマンスが重要なユーザージャーニーはどれですか? すべてのエンドポイントに負荷テストが必要なわけではありません。トラフィックの多いフロー、収益に直結するフロー、レイテンシに敏感なフローに重点を置いてください。
- 既存のパフォーマンス予算はありますか? ある場合は、目標値を確認してください。ない場合は、このスキルを使って設定できます。まずベースラインを測定してください。
現状
- 現在のベースラインはどの程度ですか? 目標を設定するには、まず数値が必要です。先に測定し、その後で予算を定義してください。
- これまでにパフォーマンスが原因で何が問題になりましたか? ユーザーを失うほど遅いページ、負荷時にタイムアウトするエンドポイント、応答しなくなるクエリなどは、重点的に調べるべき箇所を示しています。
- 既存の監視はありますか? APM(Datadog、New Relic)、RUM、合成プローブから得られる実環境のデータを使って、現実的な負荷をモデル化できます。
インフラストラクチャ
- 負荷テストはどこで実行しますか? ステージング環境または専用の負荷テスト環境を対象にしてください。運用チームとの明示的な調整なしに、本番環境を対象にしてはいけません。
- 想定されるトラフィックパターンはどのようなものですか? 安定したトラフィック、日々のピーク、季節的な急増(Black Friday)、イベントに伴う突発的なバーストのいずれかによって、選ぶべき負荷パターンが決まります。
- 経路上にレート制限、WAF ルール、自動スケーリングはありますか? これらは結果を歪めるため、テスト設計で考慮する必要があります。
基本原則
1. 最適化の前に測定する
パフォーマンスに関する直感は信頼できません。開発者は日常的に、間違った対象を最適化しています。まずプロファイリングを行い、実際のボトルネックを特定してから最適化してください。適切な箇所でプロファイリングによって確認された 50ms の改善は、誤った箇所で期待された 500ms の改善に勝ります。
2. CI で適用しない予算に実効性はない
Wiki に記載されていても CI でチェックされない予算は、数週間以内に破られます。k6 のしきい値と Lighthouse のアサーションとしてパイプラインに予算を組み込み、回帰時に四半期レビューではなくビルドを失敗させてください。
3. 最大負荷より現実的な負荷を優先する
トラフィックの 10 倍まで負荷をかけるストレステストは、システムが限界に達する地点を教えてくれます。想定トラフィックの 1.5 倍で行う負荷テストは、明日の実際のユーザーが快適に利用できるかを教えてくれます。どちらにも価値はありますが、現実的な負荷テストはより頻繁に実行でき、回帰を早期に検出できます。
4. Core Web Vitals はユーザーが実際に体感する指標
サーバー側の指標(レイテンシ、スループット)は重要ですが、ユーザーが体感するパフォーマンスはブラウザーを通じて決まります。LCP、INP、CLS は、ユーザーが認識する速度を測定します。API が高速でも描画が遅ければ、ユーザーにとっては遅いままです。
5. パフォーマンスは機能の一部
パフォーマンスは偶然に実現するものではありません。専用のテストインフラストラクチャ、予算、監視が必要であり、機能の正確性と同じように継続的な注意を要します。パフォーマンスの回帰は、機能上のバグと同じ緊急度で扱ってください。
k6 負荷テスト
k6 は、スクリプトに JavaScript/TypeScript を使用し、CLI から実行でき、CI と統合するオープンソースの負荷テストツールです。現在の安定版:k6 v2.0.0(最終版は2026-05にリリース)。v2 には v1 からの破壊的変更があります。以下の移行に関する注意事項を参照してください。
負荷テストは、負荷プロファイル(stages/scenarios の構成)、チェック(リクエストごとのアサーション)、しきい値(終了コードを設定する合否予算)の3つで構成されます。同じスクリプトをローカル、ステージング、CI で実行できるよう、ベース URL は必ず __ENV から設定してください。
基本的な負荷テスト全体、カスタムメトリクス、シナリオについては references/recipes.md を参照してください。しきい値ブロックの最小例:
export const options = {
stages: [
{ duration: '1m', target: 20 },
{ duration: '3m', target: 20 },
{ duration: '1m', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<500', 'p(99)<1000'],
http_req_failed: ['rate<0.01'],
},
};
負荷プロファイル
| プロファイル | 確認すること | 構成 | 所要時間 |
|---|---|---|---|
| 一定 | システムは通常のトラフィックを処理できますか? | vus: 50, duration: '10m' | 10分 |
| ランプアップ(ストレス) | どの時点で性能が低下しますか? | 50 → 100 → 200 → 400 → 800 → 0 | 12分 |
| スパイク | 急増から回復できますか? | 50 → 500に急増 → 維持 → 50に低下 → 回復 | 6分 |
| ソーク | 時間の経過とともにリソースがリークしますか? | 100までランプアップ、4時間維持、ランプダウン | 4時間以上 |
スパイクテストでは、「回復するかどうか」がコメントではなくアサーションになるまでは完了ではありません。スパイク後の期間にタグ(例:phase:recovery)を付け、その期間にしきい値を適用して、p95 が高いままなら実行が失敗するようにしてください。references/recipes.md の回復検出レシピを参照してください。
カスタムメトリクスとシナリオ
k6 には4種類のメトリクスがあります:Counter(累積カウント)、Rate(ゼロ以外/true の値の割合、0..1)、Trend(統計分布 — p50/p95/p99)、Gauge(最新の値)。リクエストに { tags: { name: 'endpoint' } } を付けると、エンドポイント、シナリオ、またはフローごとにメトリクスを絞り込めます。
シナリオでは、それぞれ独自のエグゼキューターとシナリオ別のしきい値('http_req_duration{scenario:checkout}': ['p(95)<500'])を持つ、異なるユーザーフローを並行して実行します。ブラウザー、チェックアウト、API 中心の負荷を同時に発生させるなど、実際の混在パターンをモデル化するために使用してください。カスタムメトリクスとシナリオの完全な例は references/recipes.md にあります。
k6 の CI 統合
k6 のインストールには、独自に用意した apt/gpg キーサーバーの設定ブロック(壊れやすく、陳腐化しやすく、バージョンを固定できません)ではなく、公式の grafana/setup-k6-action@v1 を使用してください。いずれかのしきい値を超えると k6 はゼロ以外の終了コードを返すため、追加設定なしで予算超過によりジョブが失敗します。GitHub Actions ワークフロー全体(チェックアウト → setup-k6 → 実行 → アーティファクトのアップロード)は references/recipes.md にあります。
k6 v1 → v2 の移行(v2.0.0 最終版、2026-05):
k6/experimental/websockets→k6/websockets(experimental/プレフィックスを削除。現在は安定版です)k6/experimental/redis→k6/x/redis— 削除されたわけではありません。このインポートはxk6-redis拡張機能を自動解決します(自動拡張機能解決はデフォルトで有効です。JS の使用方法に変更はありません)。 Redis クライアントを独自に実装しないでください。externally-controlledエグゼキューターを削除options.ext.loadimpactを削除 →options.cloudを使用(Grafana Cloud k6、旧称 k6 Cloud/Load Impact)- CLI:
--no-summary→--summary-mode=disabled;--upload-only→k6 cloud upload script.js;k6 login/pause/resume/scale/statusを削除(k6 cloud loginなどを使用);位置引数のk6 cloud script.jsを削除- 終了コード 97 を新設:しきい値とは無関係な、クラウド側による中断です。CI の処理に組み込んでください。 参考:https://grafana.com/docs/k6/latest/get-started/migrating-to-v2/
Web パフォーマンス
Lighthouse CI
Lighthouse CI(@lhci/cli)は Google Lighthouse の監査を自動化し、lighthouserc.js のアサーションを通じてパイプライン内で予算を適用します。現在のバージョンは Lighthouse 12.6 エンジン上の @lhci/cli 0.15.x です。このプロジェクトはメンテナンスモードにあり(最終リリースは約1年前)、まだ Lighthouse 13 をサポートしていません(Node 22.19 以降が必要です)。Lighthouse の CI での標準的な選択肢として引き続き利用できますが、新規プロジェクトで採用する前に上流の動向を確認してください。
lighthouserc.js 全体(LCP/CLS/TBT/パフォーマンススコアのアサーション)と lhci autorun の CI ステップは references/recipes.md にあります。
INP はフィールドデータ専用 — ラボでは TBT をゲートします
これはウェブパフォーマンスで最も誤解されやすい点なので、正確に理解してください。
- INP(Interaction to Next Paint)はフィールドデータ専用の指標です。 Lighthouse のラボ監査では、ユーザーの操作なしでページを読み込むため、INP を測定したりスコア化したりすることはできません。標準的な
lhci autorunの実行でinteraction-to-next-paintをアサートしても、Lighthouse が生成しない指標をゲートすることになります。 - ラボでは、代理指標として Total Blocking Time(TBT)をゲートします。
'total-blocking-time': ['error', { maxNumericValue: 200 }]をアサートしてください。TBT は INP と相関しますが、同じものではありません(TBT が 0ms でも、フィールドデータでの INP が基準を満たさないページもあります)。 - 実際の INP はフィールドデータから追跡してください — Chrome UX Report(CrUX)または RUM ツールを使います。これがユーザーが実際に体験する数値であり、Google のランキングに使われる数値です。
- スクリプト化した操作のレイテンシを測定する必要がある場合に限り、スクリプト化したクリックを使う Lighthouse の user-flow/timespan モード、または
eventエントリーに対するPerformanceObserverを使ったk6/browserを使用します。これはスクリプト化したラボでの代理指標として扱い、フィールドデータでの INP とは区別してください。
FID は廃止されています: FID は非推奨となり、
web-vitalsv5+ では削除されました。INP は 2024 年 3 月に Core Web Vital になりました。新しいコードでは FID をアサートしないでください。
Playwright での CWV の測定
既存の Playwright テストスイート内でページごとのラボチェックを行うには、page.evaluate と PerformanceObserver を使って LCP と CLS を取得し(どちらもページ読み込み時に適切に監視できます)、しきい値をアサートします。テスト全体は references/recipes.md を参照してください。負荷時の CWV 取得には、同じファイルにある k6/browser のレシピを使用します。
Core Web Vitals
Google がユーザーにとってのパフォーマンス評価に使う 3 つの指標です。
| 指標 | 測定内容 | 良好 | 改善が必要 | 不良 |
|---|---|---|---|---|
| LCP (Largest Contentful Paint) | 読み込み — 最大の要素が描画されるタイミング | ≤ 2.5s | 2.5s–4.0s | > 4.0s |
| INP (Interaction to Next Paint) | 応答性 — 操作 → 次の描画(フィールドデータ専用) | ≤ 200ms | 200ms–500ms | > 500ms |
| CLS (Cumulative Layout Shift) | 視覚的安定性 — 予期しないレイアウトの移動 | ≤ 0.1 | 0.1–0.25 | > 0.25 |
CLS は面積で重み付けされます。横幅の広いビューポートでは、小さなアプリでも遅れて表示されたバナーによって 240px の移動が発生し、スコアが 0.07 になることがあります。ラボテストでは、初回描画後の主要な操作要素のバウンディングボックスも固定して確認してください(references/recipes.md を参照)。
よくある修正方法:
- LCP — サーバーが遅い(キャッシュ/CDN を使用し、LCP コンテンツを SSR/SSG で描画する)、レンダリングを妨げる CSS/JS(遅延させる、クリティカル CSS をインライン化する)、画像の読み込みが遅い(WebP/AVIF を使用し、LCP 画像を
preloadする)。 - INP — JS タスクが長い(
scheduler.yield()、requestIdleCallbackを使う)、イベントハンドラーが重い(デバウンス/スロットリング、Web Workers を使う)、レイアウトスラッシング(requestAnimationFrameを使って DOM の読み取り/書き込みをまとめる)。 - CLS — 画像に
width/heightを設定する、挿入コンテンツ用のスペースを確保する(aspect-ratio/min-height)、size-adjustと併せてfont-display: swapを使う、広告/埋め込みコンテンツ用に固定サイズのコンテナーを使う。
フィールドデータとラボデータ
| ラボデータ | フィールドデータ | |
|---|---|---|
| ソース | Lighthouse、WebPageTest、Playwright、k6/browser | Chrome UX Report(CrUX)、RUM ツール |
| 環境 | シミュレーションされた制御環境 | 実際のユーザー、実際のデバイス、実際のネットワーク |
| 用途 | デバッグ、CI ゲート、デプロイ前の確認 | 実際のユーザー体験の把握 |
| 制限事項 | 実環境でのばらつきがなく、フィールドデータでの INP を測定できない | 特定の条件を再現できない |
CI ゲートとデバッグにはラボデータを、実際のユーザー体験の把握にはフィールドデータを使います。Lighthouse のスコアが 100 でも CrUX データが悪いページには実際の問題があります — そして INP が現れるのはフィールドデータの列だけです。
ボトルネックの特定
予算を超過した場合は、次の順序で調査してください。推測で判断したり、プロファイリングの前に最適化したりしてはいけません。
- 遅いエンドポイントを特定します。 k6 のエンドポイントごとの
Trendメトリクスで、API ごとの p50/p95/p99 を確認できます。最も遅いエンドポイントを最初の対象にします。 - データベースを確認します。 インデックスの不足(
EXPLAIN)、N+1 パターン(JOIN/バッチ処理)、書き込みの多いテーブルでのロック競合(トランザクションを最適化)、上限のない結果セット(ページネーションを使用)を確認します。 - CDN/キャッシュを確認します。 静的アセットの
cache-control(public, max-age=31536000, immutable)と、x-cache: HITの割合を確認します。 - サードパーティーのスクリプトを確認します。 分析/チャット/トラッキングの
blockedUrlPatternsを指定して Lighthouse を実行し、スコアを比較してコストを定量化します。 - サーバーメトリクスと照合します。 クライアント側のレイテンシだけでは原因がわかりません。テスト実行中の CPU、メモリ、ディスク I/O、コネクションプールの飽和状態、クエリの実行時間を確認します。
アンチパターン
1. 調整せずに本番環境で負荷テストを実施する
負荷テストによって、自動スケーリング(高額なコストが発生)、レート制限(テストが失敗)、アラート(不要な通知)、または障害が発生することがあります。必ず運用担当者と調整し、ステージング環境または専用の負荷テスト環境を対象にしてください。
2. 非現実的な負荷シナリオ
DAU が 500 の製品で同時ユーザー数を 10,000 人にしてテストしたり、実際のトラフィックにピークがあるのに均一なトラフィックでテストしたりすることです。分析データを基に負荷をモデル化してください。データがない場合は、推定ピークの 2 倍から開始して増やします。
3. サーバー側のメトリクスを無視する
k6 のクライアント側レスポンスタイムだけを見ることです。サーバーの CPU 使用率が 95% に達していたり、コネクションプールが枯渇していたり、メモリリークが発生していたりする可能性があります。負荷テストの結果をサーバーメトリクスと照合してください。
4. リリース前にしかパフォーマンステストを実施しない
四半期ごとのリリース前負荷テストでは、原因を追跡できないリグレッションが何十件も蓄積する可能性があります。リグレッションをすぐに検出できる予算を設定し、main へのマージごとに CI で実行してください。
5. パフォーマンス予算がない
「LCP は 3.2 秒です」は情報ですが、「LCP は 2.5 秒未満でなければなりません」はゲートです。予算を定義し、k6 のしきい値と Lighthouse のアサーションとして適用して、違反をバグとして扱ってください。
6. 標準の Lighthouse CI で INP をアサートする
標準の lhci autorun のラボモードでは、ユーザー操作がないため INP の監査結果を生成できません。そこで interaction-to-next-paint をゲートにすると、Lighthouse が一度も測定しない数値をゲートにすることになります。ラボ環境の代替指標として total-blocking-time をゲートし、CrUX/RUM から実際の INP を追跡してください。
7. プロファイリングせずに最適化する
レスポンスタイムの 2% を占める関数に何日も費やすことです。プロファイリングを行い、実際のボトルネックを見つけてから最適化してください。200ms のクエリは、毎回 5ms の JS 関数より優先度が高くなります。
8. 空のデータベースでテストする
本番環境には 1,000 万件あるのに、シードした 100 件の行で負荷テストを行うことです。クエリのパフォーマンスは、規模によって大きく異なります。まず、負荷テスト環境に本番環境と同規模の(匿名化した)データをシードしてください。
検証
完了を宣言する前に、成果物が実際に動作することを確認してください。
- k6 スクリプト:
k6 run --summary-mode=disabled load-tests/api-load.jsが 0 で終了し、テスト終了時のサマリーですべてのthresholds行が緑色(✓)であることを確認します。しきい値の行が赤色なら、予算を超過しており、終了コードは 0 以外になります。 - スパイクからの回復: スパイクスクリプトを実行し、サマリーに
{phase:recovery}しきい値が表示され、合格することを確認します。これにより、回復がコメントされているだけでなく、アサーションされていると証明できます。 - Lighthouse CI:
lhci autorunが 0 で終了し、すべての['error', ...]アサーションが合格することを確認します。LCP/CLS/TBT のアサーションに違反すると、0 以外で終了します。
完了条件
- k6 スクリプトで、対象の負荷プロファイル(ベースライン(一定)、ストレス(限界点まで段階的に増加)、ソーク(持続))をカバーし、それぞれのベース URL を
__ENVで指定できます。 - スパイクテストでは、
// recoveryコメントだけでなく、スパイク後の期間にスコープを限定したしきい値(例:http_req_duration{phase:recovery})で回復を検証します。 - パフォーマンス予算を k6 のしきい値(例:
p(95)<500、http_req_failed rate<0.01)として設定し、超過した場合は CI ジョブを失敗させます。 - Lighthouse CI の
lighthouserc.jsで、largest-contentful-paint、cumulative-layout-shift、total-blocking-time(INP のラボ環境での代替指標)を['error', ...]アサーションとして設定し、マージを制御します。実際の INP はラボ環境でアサートせず、CrUX/RUM から追跡します。 - 主要ページ(ホーム、チェックアウト、ダッシュボード)ごとに、Core Web Vitals のベースラインを Good/Needs Improvement/Poor の分類とともに文書化します。
- テスト結果には、シナリオごとの p95 と p99 のレイテンシー、エラー率、スループットを含め、CI アーティファクトとして保存します。
参考ファイル(references/ 内)
- recipes.md — 実行可能な成果物:基本的な k6 負荷テスト、回復検出を含むスパイクテスト、カスタムメトリクス、シナリオ、k6 CI ワークフロー(
grafana/setup-k6-action)、lighthouserc.js、k6/browserの負荷時 CWV の例、および Playwright の LCP/CLS テスト。
関連スキル
- ci-cd-integration — k6 と Lighthouse CI のパイプラインへの組み込み、夜間実行のスケジュール設定、予算に基づくデプロイの制御。
- qa-metrics — QA メトリクスの包括的なダッシュボードの一部としての LCP/INP/CLS と p95 レイテンシー。
- release-readiness — リリース可否チェックリストでのパフォーマンスベンチマーク。
- synthetic-monitoring — リリース 後 に実施する、本番環境の CWV/稼働時間の定期プローブ。このスキルはリリース前のラボ環境でのゲート制御を対象とします。
- observability-driven-testing — 本番環境のテレメトリーを、新しいパフォーマンステストの設計における 入力 とする場合。
- qa-project-context — テスト対象のパフォーマンス予算、トラフィックパターン、重要なフローを記録します。