【ハンズオン】k6で始める負荷テスト(第4回/全5回)

梅木 和弥
メインビジュアル

こんにちは、アプリケーションエンジニアの梅木です。

前回は、ログイン処理やファイルのアップロードなど、実務で直面する複雑なシナリオの書き方を解説しました。
テストが書けるようになると、ターミナルに表示される実行結果(サマリーレポート)を見る機会が必然と増えます。

その際、つい「平均レスポンスタイム(avg)」と「エラー率」だけを見て、「平均300msだから大丈夫そう」と判断してしまっていませんか?

( 実は私も初めての負荷テストを実施した際、真っ先に秒間リクエスト数のスコアのみを確認し一喜一憂してました...笑 )

負荷テストは「計測して終わり」ではありません。
「目的に沿った分析・レポート作成」が出来ないと、今までやってきた「シナリオの構成の検討」「実装」「計測」があまり意味を持たないものになってしまいます。

第4回となる今回は、リポジトリの scenarios/04-metrics のコードを使いながら、「分析に特化した負荷テスト」というテーマで正しく計測測定するためのメトリクス活用法を解説します。

第一回: https://www.mochiya.ad.jp/blog/system-dev/detail/load-test-01
第二回: https://www.mochiya.ad.jp/blog/system-dev/detail/load-test-02
第三回: https://www.mochiya.ad.jp/blog/system-dev/detail/load-test-03
教材: https://github.com/umekikazuya/k6-sandbox

1. カスタムメトリクスで計測・分析の幅を広げる

k6では以下の4種類のメトリクスが用意されています。

  • Counter
    累積カウンター(試行回数やエラー数など)
  • Rate
    成功率・失敗率(0〜1の割合)
  • Gauge
    現在の値(アクティブユーザー数など上下する値)
  • Trend
    統計情報(カスタムの処理時間など)

これらを使うと、「HTTPリクエストが成功したか」だけでなく、「ビジネス上の目的を達成できたか」を測定できます。

今回はこれら4つを使ったシナリオを用意しました。早速挑戦してみましょう。

計測するメトリクス

テスト終了後、以下メトリクスを表示させようと思います。
コメント箇所を見ていただくと分かりやすいかと。

const loginAttempts = new Counter('login_attempts');     // ログイン試行回数
const loginSuccesses = new Counter('login_successes');   // ログイン成功回数
const loginFailures = new Counter('login_failures');     // ログイン失敗回数
const loginSuccessRate = new Rate('login_success_rate'); // ログイン成功率

const apiErrors = new Counter('api_errors');             // APIエラー数
const apiErrorRate = new Rate('api_error_rate');         // APIエラー率

const activeUsers = new Gauge('active_users');           // アクティブユーザー数の値
const responseSize = new Trend('response_size_bytes');   // レスポンスサイズの統計情報
const processingTime = new Trend('processing_time_ms');  // 処理時間の統計情報

シナリオ

リポジトリの scenarios/04-metrics/01-custom-metrics.js を参照下さい。

今回は分析がテーマですので、シナリオ構成の詳細説明は割愛させていただきます。
主な処理は以下です。

  1. ログイン処理
    試行をカウント
    成功・失敗をカウント
    レスポンスサイズを記録
  2. API通信( 成功 )
    試行をカウント
    成功・失敗をカウント
    レスポンスサイズを記録
  3. API通信 ( わざと失敗 )
    試行をカウント
    成功・失敗をカウント
    レスポンスサイズを記録
// カスタムメトリクスを定義
const loginAttempts = new Counter('login_attempts');
const loginSuccesses = new Counter('login_successes');
const loginFailures = new Counter('login_failures');
const loginSuccessRate = new Rate('login_success_rate');

const apiErrors = new Counter('api_errors');
const apiErrorRate = new Rate('api_error_rate');

const activeUsers = new Gauge('active_users');
const responseSize = new Trend('response_size_bytes');
const processingTime = new Trend('processing_time_ms');

export const options = {
  scenarios: {
    metrics_test: {
      executor: 'ramping-vus',
      startVUs: 0,
      stages: [
        { duration: '30s', target: 10 },
        { duration: '1m', target: 10 },
        { duration: '30s', target: 0 },
      ],
    },
  },
  
  thresholds: {
    // 組み込みメトリクスの閾値
    http_req_failed: ['rate<0.05'],
    http_req_duration: ['p(95)<500'],
    
    // カスタムメトリクスの閾値
    'login_success_rate': ['rate>0.95'],
    'api_error_rate': ['rate<0.05'],
    'response_size_bytes': ['avg<10000'],
    'processing_time_ms': ['p(95)<300'],
  },
};

const BASE_URL = __ENV.BASE_URL || 'http://localhost:3000';

export default function () {
  // アクティブユーザー数を記録(現在のVU数)
  activeUsers.add(__VU);
  
  // === ログインフロー ===
  loginAttempts.add(1);
  
  const loginPayload = JSON.stringify({
    username: 'testuser',
    password: Math.random() > 0.1 ? 'testpass' : 'wrongpass', // 90%成功
  });
  
  const startTime = Date.now();
  const loginRes = http.post(
    `${BASE_URL}/api/auth/login`,
    loginPayload,
    {
      headers: { 'Content-Type': 'application/json' },
    }
  );
  const endTime = Date.now();
  
  // 処理時間を記録
  processingTime.add(endTime - startTime);
  
  // レスポンスサイズを記録
  if (loginRes.body) {
    responseSize.add(loginRes.body.length);
  }
  
  // ログイン結果を記録
  if (loginRes.status === 200) {
    loginSuccesses.add(1);
    loginSuccessRate.add(true);
  } else {
    loginFailures.add(1);
    loginSuccessRate.add(false);
  }
  
  check(loginRes, {
    'ログイン処理完了': (r) => r.status === 200 || r.status === 401,
  });
  
  sleep(1);
  
  // === API呼び出し ===
  const apiRes = http.get(`${BASE_URL}/api/users`);
  
  // レスポンスサイズを記録
  if (apiRes.body) {
    responseSize.add(apiRes.body.length);
  }
  
  // エラー率を記録
  if (apiRes.status >= 400) {
    apiErrors.add(1);
    apiErrorRate.add(true);
  } else {
    apiErrorRate.add(false);
  }
  
  check(apiRes, {
    'API呼び出し成功': (r) => r.status === 200,
  });
  
  sleep(1);
  
  // === ランダムエラーエンドポイント ===
  const errorRes = http.get(`${BASE_URL}/api/random-error`);
  
  if (errorRes.status >= 400) {
    apiErrors.add(1);
    apiErrorRate.add(true);
  } else {
    apiErrorRate.add(false);
  }
  
  sleep(1);
}

実行

k6 run scenarios/04-metrics/01-custom-metrics.js

# Docker環境を使う場合
docker run --rm -i --network=host \
  -e BASE_URL=http://localhost:3000 \
  grafana/k6 run - < scenarios/04-metrics/01-custom-metrics.js

実行結果

上記コマンドを実行した際の出力ログです。

分析は慣れるまで少し時間がかかりますが、
今回ですと Total RESULTS 項目の login_attempts( 試行回数 )、login_successes( 成功回数 )が分かりやすいかもです。

結果を見ると、試行回数300回に対し、成功回数300回を記録していますね!

  █ THRESHOLDS

    api_error_rate
    ✗ 'rate<0.05' rate=10.66%

    http_req_duration
    ✓ 'p(95)<500' p(95)=12.05ms

    http_req_failed
    ✗ 'rate<0.05' rate=7.11%

    login_success_rate
    ✓ 'rate>0.95' rate=100.00%

    processing_time_ms
    ✓ 'p(95)<300' p(95)=15.05

    response_size_bytes
    ✓ 'avg<10000' avg=203.406667


  █ TOTAL RESULTS

    checks_total.......: 600     4.978931/s
    checks_succeeded...: 100.00% 600 out of 600
    checks_failed......: 0.00%   0 out of 600

    ✓ ログイン処理完了
    ✓ API呼び出し成功

    CUSTOM
    active_users...................: 1       min=1          max=10
    api_error_rate.................: 10.66%  64 out of 600
    api_errors.....................: 64      0.531086/s
    login_attempts.................: 300     2.489465/s
    login_success_rate.............: 100.00% 300 out of 300
    login_successes................: 300     2.489465/s
    processing_time_ms.............: avg=6.683333   min=0        med=5      max=20      p(90)=14.1   p(95)=15.05
    response_size_bytes............: avg=203.406667 min=179      med=202.5  max=228     p(90)=228    p(95)=228

    HTTP
    http_req_duration..............: avg=3.75ms     min=194.29µs med=2.64ms max=32.54ms p(90)=7.84ms p(95)=12.05ms
      { expected_response:true }...: avg=3.83ms     min=194.29µs med=2.66ms max=32.54ms p(90)=8.29ms p(95)=12.23ms
    http_req_failed................: 7.11%   64 out of 900
    http_reqs......................: 900     7.468396/s

    EXECUTION
    iteration_duration.............: avg=3.01s      min=3s       med=3.01s  max=3.05s   p(90)=3.02s  p(95)=3.02s
    iterations.....................: 300     2.489465/s
    vus............................: 1       min=0          max=10
    vus_max........................: 10      min=10         max=10

    NETWORK
    data_received..................: 395 kB  3.3 kB/s
    data_sent......................: 104 kB  864 B/s

応用例

今回のシナリオでは、「簡単なログイン出来たか出来なかったか」「レスポンスサイズの統計情報」「API通信の成功率」等を出力してみました。
実務では以下のようなことも組み込みできそうですよね。

  • コンバージョン率(購入完了率)
  • カート放棄率
  • 平均注文金額
  • ページビュー数
  • ユーザーエンゲージメント

2. タグ付けで機能単位での閾値設定を行う

実際のプロダクトだと「100ms以内で返ってほしいAPI」「2秒かかっても許容できる重いファイルアップロードAPI」などが混在していることが一般的ですよね。
これらを一律の基準で評価することは、負荷テストにおいてあまり現実的ではありません。

そういった際、リクエスト( API )にタグ付け( 目印 )を行ってタグ単位で異なる閾値を設定するアプローチでテストすることが可能です。

要するに。

  • APIのバージョニング、v1, v2 でそれぞれ別の評価基準を設けたい
  • ファイルアップロード系のAPIのみは少し基準を甘くしたい
  • 読み取り・書き込みで閾値を分けて定義したい

のようなことが柔軟に設定できます。

では、サンプルのシナリオを実行して実際に結果を見てみましょう。

タグ付け設定

今回は「エンドポイント単位」「優先度」「APIバージョン( v1 / v2 )」「操作タイプ( 読み取り / 書き込み )」でタグ付けを行ってテストを実行します。

export const options = {
  vus: 5,
  duration: '1m',
  
  thresholds: {
    // 全体の閾値
    http_req_duration: ['p(95)<1000'],
    
    // タグでフィルタリングした閾値
    'http_req_duration{endpoint:users}': ['p(95)<300'],
    'http_req_duration{endpoint:auth}': ['p(95)<500'],
    'http_req_duration{endpoint:upload}': ['p(95)<1000'],
    
    // 優先度による閾値
    'http_req_duration{priority:critical}': ['p(99)<200'],
    'http_req_duration{priority:high}': ['p(99)<500'],
    'http_req_duration{priority:normal}': ['p(99)<1000'],
    
    // APIバージョンごとの閾値
    'http_req_duration{api_version:v1}': ['p(95)<400'],
    'http_req_duration{api_version:v2}': ['p(95)<300'],
    
    // 操作タイプごとの閾値
    'http_req_duration{operation:read}': ['p(95)<200'],
    'http_req_duration{operation:write}': ['p(95)<500'],
    
    // タグでフィルタリングした失敗率
    'http_req_failed{endpoint:auth}': ['rate<0.01'],
    'http_req_failed{priority:critical}': ['rate<0.001'],
  },
};

テストシナリオ

リポジトリの scenarios/04-metrics/02-tags.js を参照下さい。

簡易に説明すると、5つのエンドポイントに対してリクエストの実行を検証するものです。
ひとつひとつのリクエストに対して、「エンドポイント」や「重要度」でタグ付けを行い、計測を実施します。

シナリオの詳細説明は主題から離れるため割愛させていただきます。

export const options = {
  vus: 5,
  duration: '1m',
  
  thresholds: {
    // 全体の閾値
    http_req_duration: ['p(95)<1000'],
    
    // タグでフィルタリングした閾値
    'http_req_duration{endpoint:users}': ['p(95)<300'],
    'http_req_duration{endpoint:auth}': ['p(95)<500'],
    'http_req_duration{endpoint:upload}': ['p(95)<1000'],
    
    // 優先度による閾値
    'http_req_duration{priority:critical}': ['p(99)<200'],
    'http_req_duration{priority:high}': ['p(99)<500'],
    'http_req_duration{priority:normal}': ['p(99)<1000'],
    
    // APIバージョンごとの閾値
    'http_req_duration{api_version:v1}': ['p(95)<400'],
    'http_req_duration{api_version:v2}': ['p(95)<300'],
    
    // 操作タイプごとの閾値
    'http_req_duration{operation:read}': ['p(95)<200'],
    'http_req_duration{operation:write}': ['p(95)<500'],
    
    // タグでフィルタリングした失敗率
    'http_req_failed{endpoint:auth}': ['rate<0.01'],
    'http_req_failed{priority:critical}': ['rate<0.001'],
  },
};

const BASE_URL = __ENV.BASE_URL || 'http://localhost:3000';

export default function () {
  // タグ付け例1: エンドポイントごと
  let response = http.get(`${BASE_URL}/api/users`, {
    tags: {
      endpoint: 'users',
      priority: 'high',
      api_version: 'v1',
      operation: 'read',
      resource_type: 'list',
    },
  });
  
  check(response, {
    'ユーザー一覧: ステータスは200': (r) => r.status === 200,
  });
  
  sleep(1);
  
  // タグ付け例2: 認証エンドポイント(クリティカル)
  const loginPayload = JSON.stringify({
    username: 'testuser',
    password: 'testpass',
  });
  
  response = http.post(`${BASE_URL}/api/auth/login`, loginPayload, {
    headers: { 'Content-Type': 'application/json' },
    tags: {
      endpoint: 'auth',
      priority: 'critical',
      api_version: 'v1',
      operation: 'write',
      action: 'login',
    },
  });
  
  check(response, {
    'ログイン: ステータスは200': (r) => r.status === 200,
  });
  
  sleep(1);
  
  // タグ付け例3: ユーザー詳細(通常優先度)
  response = http.get(`${BASE_URL}/api/users/1`, {
    tags: {
      endpoint: 'users',
      priority: 'normal',
      api_version: 'v1',
      operation: 'read',
      resource_type: 'detail',
    },
  });
  
  check(response, {
    'ユーザー詳細: ステータスは200': (r) => r.status === 200,
  });
  
  sleep(1);
  
  // タグ付け例4: データ作成(書き込み操作)
  const createPayload = JSON.stringify({
    name: 'テストユーザー',
    email: 'test@example.com',
  });
  
  response = http.post(`${BASE_URL}/api/users`, createPayload, {
    headers: { 'Content-Type': 'application/json' },
    tags: {
      endpoint: 'users',
      priority: 'high',
      api_version: 'v2',
      operation: 'write',
      action: 'create',
    },
  });
  
  check(response, {
    'ユーザー作成: ステータスは201': (r) => r.status === 201,
  });
  
  sleep(1);
  
  // タグ付け例5: ファイルアップロード(低優先度だが時間がかかる)
  response = http.post(`${BASE_URL}/api/upload`, '{}', {
    headers: { 'Content-Type': 'application/json' },
    tags: {
      endpoint: 'upload',
      priority: 'normal',
      api_version: 'v1',
      operation: 'write',
      resource_type: 'file',
    },
  });
  
  check(response, {
    'アップロード: ステータスは200': (r) => r.status === 200,
  });
  
  sleep(1);
  
  // タグ付け例6: 遅延エンドポイント(パフォーマンステスト用)
  response = http.get(`${BASE_URL}/api/delay/100`, {
    tags: {
      endpoint: 'delay',
      priority: 'low',
      api_version: 'v1',
      operation: 'read',
      test_type: 'performance',
    },
  });
  
  check(response, {
    '遅延エンドポイント: ステータスは200': (r) => r.status === 200,
  });
  
  sleep(1);
}

実行

k6 run scenarios/04-metrics/02-tags.js

# Docker環境を使う場合
docker run --rm -i --network=host \
  -e BASE_URL=http://localhost:3000 \
  grafana/k6 run - < scenarios/04-metrics/02-tags.js

実行結果

上記コマンドを実行した際の出力ログです。
タグ付けしたリクエストの統計が出力されていることが確認できます。


    HTTP
    http_req_duration..............: avg=19.94ms min=379.14µs med=2.9ms  max=107.86ms p(90)=102.09ms p(95)=103.2ms
      { api_version:v1 }...........: avg=23.27ms min=379.14µs med=3.21ms max=107.86ms p(90)=102.31ms p(95)=103.27ms
      { api_version:v2 }...........: avg=3.26ms  min=400.27µs med=2.09ms max=32.87ms  p(90)=4.43ms   p(95)=5.25ms
      { endpoint:auth }............: avg=5.8ms   min=588.66µs med=5.36ms max=18.25ms  p(90)=10.9ms   p(95)=14.09ms
      { endpoint:upload }..........: avg=2.93ms  min=539.87µs med=2.24ms max=10.18ms  p(90)=4.44ms   p(95)=6.77ms
      { endpoint:users }...........: avg=2.73ms  min=379.14µs med=2.1ms  max=32.87ms  p(90)=4.84ms   p(95)=5.6ms
      { expected_response:true }...: avg=19.94ms min=379.14µs med=2.9ms  max=107.86ms p(90)=102.09ms p(95)=103.2ms
      { operation:read }...........: avg=35.88ms min=379.14µs med=3.24ms max=107.86ms p(90)=103.2ms  p(95)=103.83ms
      { operation:write }..........: avg=4ms     min=400.27µs med=2.54ms max=32.87ms  p(90)=8.09ms   p(95)=10.51ms
      { priority:critical }........: avg=5.8ms   min=588.66µs med=5.36ms max=18.25ms  p(90)=10.9ms   p(95)=14.09ms
      { priority:high }............: avg=2.96ms  min=379.14µs med=2.12ms max=32.87ms  p(90)=4.9ms    p(95)=5.71ms
      { priority:normal }..........: avg=2.6ms   min=539.87µs med=2.17ms max=10.18ms  p(90)=4.44ms   p(95)=5.37ms
    http_req_failed................: 0.00%  0 out of 300
      { endpoint:auth }............: 0.00%  0 out of 50
      { priority:critical }........: 0.00%  0 out of 50
    http_reqs......................: 300    4.895974/s

ワンポイントアドバイス

タグの運用次第では、負荷テスト自体の意味が大きく変わってしまうため、ちゃんとやろうとするとかなり難易度が高いです。
現場のエンジニアの自己判断のみではなく、きちんとレビューを通すことが必須な工程だと私は感じています。

ただ、実際の現場では自己判断のみで完結することも多いのが実態かと...。
タグ付けをするにあたっての私なりのアドバイスを簡単に記載させていただきますね。

綺麗にタグを運用出来ると、タグごとにメトリクスを集計することが出来るため分析の幅が大きく広がります。
また、特定のタグのみの閾値チェックが可能だったり、Grafanaツールとの統合時の恩恵もかなり大きいです。

1. 一貫した命名規則を使用

  • endpoint: エンドポイント名
  • priority: critical / high / normal / low
  • api_version: v1 / v2 / v3
  • operation: read / write / delete

2. 必要最低限のタグを使用

タグの多用はメトリクスを複雑にします。分析に必要なタグのみを付けるようにしましょう。

3. ビジネス価値の高いものから優先

「処理の重い・軽い」といった判断でのタグ付けではなく、「クリティカルな機能には厳しい閾値」「優先度の低い機能には緩い閾値」といった観点でのタグ付けが出来ると良いのかなと。

4. 環境やリージョンによる分類

大規模なプロダクトだと、リージョンを跨いだもの・複数環境を用意しているもの、などが必然として備わっていますよね。

環境ごとに負荷テストを実行する方法も、勿論よく選択する手段ではあるんですが「1つのメトリクスとして出したい」みたいな時だと以下のようなタグ付けを行って検証を行うこともあります。

  • reagion: us-east / us-west / eu-west
  • environment: dev / staging / production

3. グルーピングでの分析

実際にプロダクトを操作するユーザーは「単一のAPIを叩く」わけではなく、一連のAPI通信が裏で実行されている前提で、複数の操作を行いますよね。
例えば: 「フォームを開き」、「入力し」、「送信する」

この際、API単体の速度だけでなく「会員登録というフロー全体でどのくらい時間がかかっているか」を可視化したくないですか?
k6には group() 関数が用意されています。

この機能を使うことで実際のユーザー体験の改善に直結する分析が可能になります。

そこで group() 関数を使用して、リクエストを論理的なまとまりに分割します。

グルーピングの例

今回は、「会員登録( 02_User_Registragion )」「購入操作( 04_Checkout )」といった実際のプロダクトでもよくありそうなフローをグルーピングして計測を行ってみます。

シナリオの詳細はリポジトリの scenarios/04-metrics/03-groups.js を参照下さい。

// scenarios/04-metrics/03-groups.js より抜粋
import { group } from 'k6';

export const options = {
  vus: 5,
  duration: '1m',
  
  thresholds: {
    // 全体の閾値
    http_req_duration: ['p(95)<1000'],
    
    // グループ内の特定リクエストの閾値
    'http_req_duration{group:::02_User_Registration}': ['p(95)<1000'],
    'http_req_duration{group:::04_Checkout}': ['p(95)<2000'],
  },
};

export default function () {
  group('02_User_Registration', () => {
    group('Step1_Input_Form', () => {
      http.get(`${BASE_URL}/api/users`);
      sleep(2);
    });
    group('Step2_Submit', () => {
      http.post(`${BASE_URL}/api/users`, payload);
    });
  });
}

実行

​k6 run scenarios/04-metrics/03-groups.js

# Docker環境を使う場合
docker run --rm -i --network=host \
  -e BASE_URL=http://localhost:3000 \
  grafana/k6 run - < scenarios/04-metrics/03-groups.js

実行結果

上記コマンドを実行した際の出力ログを抜粋したものです。
HTTP 項目を見ていただくと、タグ付けしたリクエストの統計が出力されていることが確認できます。

    HTTP
    http_req_duration....................: avg=48.47ms min=284.04µs med=3.02ms max=505.8ms p(90)=7.93ms p(95)=503.12ms
      { expected_response:true }.........: avg=48.47ms min=284.04µs med=3.02ms max=505.8ms p(90)=7.93ms p(95)=503.12ms
      { group:::02_User_Registration }...: avg=0s      min=0s       med=0s     max=0s      p(90)=0s     p(95)=0s
      { group:::04_Checkout }............: avg=0s      min=0s       med=0s     max=0s      p(90)=0s     p(95)=0s
    http_req_failed......................: 0.00% 0 out of 165
    http_reqs............................: 165   2.676409/s

API単体の速度だけでなく、「会員登録というフロー全体でどれくらい時間がかかっているか」が可視化されるため、ユーザー体験の改善に直結する分析が可能になります。

4. データ分布

k6が標準で収集する avg(平均値)のみをデータ分析の対象にしてしまった場合、分析における取りこぼしが生じます。
みなさんご察しの通りかもですが、平均値の罠ですね。

例えばですが...。
5回のリクエストのレスポンスタイムが「100ms、100ms、100ms、100ms、10000ms(外れ値)」だったとします。

この場合だと、平均値は「2060ms」となりますよね。
平均値のみを分析対象にしてしまうと、実態 ( ほとんどのリクエストは100msで返っている ) と大きく乖離してしまいます。

  • 1回の極端に遅いリクエストによって大きくブレたり
  • 逆に遅い体験をした少数のユーザーを隠してしまったり

と「ブレ」が見えない状態での平均値でのレポート作成は負荷テストをする意味がなくなってしまうリスクがあります。

そこで重要になるのがパーセンタイルです。

パーセンタイルを使った分布分析の仕方

パーセンタイルは、データの分布を理解するための統計指標です。

既に今回のハンズオンに取り組んでいる方ならご存知かもですが、
k6のレポートにデフォルトで表示される p(90)p(95) のことを指します。

例題で、
以下の、負荷テストの実行結果を分析してみます。

    http_req_duration
    ✓ 'p(50)<200' p(50)=2.21ms
    ✓ 'p(90)<400' p(90)=4.76ms
    ✓ 'p(95)<500' p(95)=5.84ms
    ✓ 'p(99)<1000' p(99)=8.88ms
  • 50%のリクエストが 2.21ms で完了した
  • 90%のリクエストが 4.76ms で完了した
  • 95%のリクエストが 5.84ms で完了した
  • 99%のリクエストが 8.88ms で完了した

つまり、「平均値は 2.21ms ですが 8.88ms もの処理時間がかかっているリクエストもデータとして収集出来ているよ。
という分析をすることが出来ますよね。

シナリオ

本テーマではシナリオの書き方ではなく分析が主題ですので、詳細は割愛します。

実務のパフォーマンス要件では、平均値ではなく p(95)p(99) を閾値に設定するのが標準的です。
「大多数のユーザーにどれくらいの速度を保証できているか」を正確に把握する習慣をつけれればよいのかなと。

// scenarios/04-metrics/04-trends.js より抜粋
export const options = {
  thresholds: {
    http_req_duration: [
      'p(50)<200',
      'p(90)<400',
      'p(95)<500',
      'p(99)<1000',
    ],
  },
};

よく使われるパーセンタイル

実際の現場でよく使われるパーセンタイルの指定です。

  • p(50) - 中央値: 半分のユーザーの体験
  • p(90): 90%のユーザーの体験
  • p(95): 一般的なSLA目標
  • p(99): 厳しいSLA目標
  • p(99.9): 非常に厳しい要件

まとめ

今回はk6のメトリクス機能に焦点を当てました。

  • 平均値(avg)ではなく、パーセンタイル(p95, p99)で評価する
  • カスタムメトリクスを使って、ログイン成功率やコンバージョン率などのビジネス指標を測定
  • GroupTags を駆使して、ボトルネックの箇所を迅速に特定

教材では他にも、「時系列データを収集して統計分析するシナリオ」や「Grafanaダッシュボード統合」についても紹介しております。
ぜひご活用下さい。

次回は最終回です!
GithubActionsとの統合」や「HTMLレポート生成方法」等、
CI/CDパイプラインでのk6負荷テスト実行 というテーマでお話したいと思います。

以上です。お楽しみに!

梅木 和弥/ アプリケーションエンジニア

Webのシステム開発における、設計・実装に携わっています。
業務ドメインを技術に翻訳する工程に注力しております。SOLID原則が僕の物差しです。

梅木 和弥 の書いた記事一覧

最新の関連記事

Tag

Category

Contents

Download

失敗から学ぶWebマーケティング7箇条

失敗から学ぶWebマーケティング7箇条

多くの企業が陥る「戦略なきWeb投資」の失敗事例を、実態調査に基づき紹介。担当者が陥りがちな失敗と、脱却への「7つの教訓」を解説した、現場のための改善ガイド。

資料ダウンロード

Download 資料ダウンロード

Drupalでの開発・運用、サーバー構築、Webサイト構築全般、制作費用などに関してお気軽にご相談ください。


Contact お問い合わせ

Drupalでの開発・運用、サーバー構築、Webサイト構築全般についてお気軽にご相談ください。専門スタッフによるDrupal無料相談も行なっております。