JMeter の基本概念
| コンポーネント | 役割 |
|---|---|
| Thread Group | 仮想ユーザー数・ランプアップ時間・繰り返し数を定義するテストの基本単位 |
| HTTP Request Sampler | HTTP リクエストを定義(URL・メソッド・パラメータ・ボディ) |
| Response Assertion | レスポンスコード・テキスト・レスポンスタイムを検証 |
| Summary Report | スループット・エラー率・平均/最大/90th パーセンタイルを集計 |
| CSV Data Set Config | テストデータを CSV から読み込んでリクエストに埋め込む |
| Constant Timer | リクエスト間に待機時間を設定(実際のユーザー行動に近づける) |
JMX ファイルでのテスト計画定義
JMeter の GUI は視覚的ですが、CI では JMX(XML)ファイルをバージョン管理してコマンドラインで実行します。主要な設定値を解説します。
<!-- user-api-load-test.jmx(主要部分抜粋) -->
<jmeterTestPlan version="1.2">
<hashTree>
<TestPlan testname="User API 負荷テスト">
<hashTree>
<!-- ─ スレッドグループ設定 ─ -->
<ThreadGroup testname="通常負荷シナリオ">
<stringProp name="ThreadGroup.num_threads">50</stringProp> <!-- 仮想ユーザー数 -->
<stringProp name="ThreadGroup.ramp_time">30</stringProp> <!-- 30 秒かけて立ち上げ -->
<stringProp name="ThreadGroup.duration">120</stringProp> <!-- 120 秒間実行 -->
<boolProp name="ThreadGroup.scheduler">true</boolProp>
<hashTree>
<!-- ─ HTTP リクエスト ─ -->
<HTTPSamplerProxy testname="GET /api/users">
<stringProp name="HTTPSampler.domain">${HOST}</stringProp>
<intProp name="HTTPSampler.port">8080</intProp>
<stringProp name="HTTPSampler.path">/api/v1/users</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
</HTTPSamplerProxy>
<!-- ─ レスポンスアサーション ─ -->
<ResponseAssertion testname="ステータスコード 200">
<intProp name="Assertion.test_type">8</intProp> <!-- 8 = equals -->
<collectionProp name="Asserion.test_strings">
<stringProp>200</stringProp>
</collectionProp>
<stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
</ResponseAssertion>
</hashTree>
</ThreadGroup>
</hashTree>
</TestPlan>
</hashTree>
</jmeterTestPlan>
JMeter CLI 実行とレポート生成
# JMeter のインストール(Homebrew 例)
brew install jmeter
# ── コマンドライン実行 ──
jmeter -n -t tests/load/user-api-load-test.jmx -l results/result.jtl -e -o reports/load-test-report/ -JHOST=localhost -JPORT=8080
# オプション説明
# -n : GUI なし(コマンドラインモード)
# -t : JMX ファイルのパス
# -l : 結果ファイル(JTL)の出力先
# -e -o : HTML ダッシュボードレポートの生成と出力先
# -JHOST : JMX 内の変数 ${HOST} に値を設定
# ── Docker での実行(CI 推奨) ──
docker run --rm -v $(pwd)/tests/load:/load -v $(pwd)/reports:/reports justb4/jmeter:5.6.3 -n -t /load/user-api-load-test.jmx -l /reports/result.jtl -e -o /reports/html/
結果分析の指標
| 指標 | 意味 | 一般的な合否基準 |
|---|---|---|
| スループット(Throughput) | 単位時間あたりのリクエスト数(req/sec) | 要件値以上 |
| 平均レスポンスタイム | 全リクエストの平均応答時間 | 1,000ms 以下(要件による) |
| 90th パーセンタイル | リクエストの 90% がこの時間以内に完了 | 2,000ms 以下(一般的目安) |
| エラー率(Error Rate) | 失敗したリクエストの割合 | 0.1% 以下 |
| 最大レスポンスタイム | 最も遅かったリクエストの応答時間 | タイムアウト設定値未満 |
⚠️ 平均値よりパーセンタイルを重視する
平均レスポンスタイムは一部の超高速リクエストに引き下げられることがあります。ユーザー体験の評価には 90th・95th パーセンタイルを主要指標として使用してください。
OWASP ZAP の仕組み
OWASP ZAP(Zed Attack Proxy)はプロキシとして動作し、アプリケーションへの HTTP リクエスト・レスポンスを傍受・分析してセキュリティの脆弱性を検出します。
| スキャン種別 | 内容 | 用途 |
|---|---|---|
| Baseline Scan(受動的) | Spider でページを巡回し、アクティブ攻撃なしで脆弱性を検出 | CI/CD での毎回スキャン |
| Full Scan(能動的) | SQL インジェクション・XSS 等を実際に試みる攻撃的スキャン | リリース前・ステージング環境 |
| API Scan | OpenAPI / Swagger / GraphQL 仕様書からエンドポイントを列挙してスキャン | REST API のセキュリティテスト |
Baseline スキャン(受動的スキャン)
# Docker を使った ZAP Baseline スキャン(推奨) docker run --rm -v $(pwd)/reports:/zap/wrk/:rw ghcr.io/zaproxy/zaproxy:stable zap-baseline.py -t http://host.docker.internal:8080 \ # スキャン対象 URL -r zap-baseline-report.html \ # HTML レポート -J zap-baseline-report.json \ # JSON レポート(CI パース用) -l WARN \ # 警告以上を報告 -I # アラートがあっても終了コード 0(CI で失敗させない場合) # ── よく検出される脆弱性カテゴリ ── # - Missing Anti-CSRF Tokens(CSRF トークンなし) # - X-Frame-Options Header Not Set(クリックジャッキング対策なし) # - Content-Security-Policy Header Not Set(CSP ヘッダーなし) # - Server Leaks Information via "X-Powered-By"(サーバー情報漏洩) # - Cookie without Secure Flag Set(Secure 属性なし Cookie)
API スキャン(OpenAPI スペックから)
# OpenAPI 仕様書を使った API スキャン docker run --rm -v $(pwd)/reports:/zap/wrk/:rw ghcr.io/zaproxy/zaproxy:stable zap-api-scan.py -t http://host.docker.internal:8080/v3/api-docs \ # OpenAPI JSON の URL -f openapi \ # フォーマット指定 -r zap-api-report.html -J zap-api-report.json -z "-config replacer.full_list(0).description=Auth -config replacer.full_list(0).enabled=true -config replacer.full_list(0).matchtype=REQ_HEADER -config replacer.full_list(0).matchstr=Authorization -config replacer.full_list(0).replacement=Bearer\ YOUR_TOKEN" # ↑ 認証が必要な API に Bearer トークンを挿入する設定
GitHub Actions 連携
# .github/workflows/security-and-load.yml
name: System Test — Load + Security
on:
push:
branches: [main]
schedule:
- cron: "0 2 * * *" # 毎日 AM 2:00 に実行(深夜バッチ)
jobs:
load-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run JMeter load test
run: |
docker run --rm -v ${{ github.workspace }}/tests/load:/load -v ${{ github.workspace }}/reports:/reports --network host justb4/jmeter:5.6.3 -n -t /load/user-api-load-test.jmx -l /reports/result.jtl -e -o /reports/html/ -JHOST=localhost
- name: Upload JMeter report
uses: actions/upload-artifact@v4
if: always()
with:
name: jmeter-report
path: reports/html/
security-scan:
runs-on: ubuntu-latest
needs: load-test # 負荷テスト後に実行
steps:
- uses: actions/checkout@v4
- name: ZAP Baseline Scan
uses: zaproxy/action-baseline@v0.12.0
with:
target: "http://localhost:8080"
rules_file_name: ".zap/rules.tsv" # 無視ルールファイル
cmd_options: "-I" # アラートで CI 失敗させない
- name: Upload ZAP report
uses: actions/upload-artifact@v4
if: always()
with:
name: zap-report
path: report_html.html
まとめ
- JMeter のテスト計画は JMX ファイルでバージョン管理し、CLI + Docker で CI に組み込む
- 性能目標はスループット・90th パーセンタイル・エラー率を基準に設定し、結果は HTML ダッシュボードで共有する
- OWASP ZAP の Baseline スキャンは CI の毎実行に組み込み、Full/API スキャンはリリース前・ステージング環境で実施する
- 認証が必要な API は ZAP の
replacer設定でトークンを自動付与してスキャンする - ZAP の誤検知は
.zap/rules.tsvで個別にルールを無効化してノイズを管理する