LESSON 30分

ストーリー

高橋アーキテクト
シナリオ設計はできた。次は実際にテストを書いて実行する。ツールはk6を使う

高橋アーキテクトがターミナルを開いた。

高橋アーキテクト
k6はJavaScriptでテストを書ける負荷テストツールだ。Grafanaが開発していて、開発者フレンドリーなのが特徴だ

k6 の基本

最小構成のテスト

// basic-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';

// テスト設定
export const options = {
  vus: 10,          // 仮想ユーザー数
  duration: '30s',  // テスト時間
};

// テスト本体(各VUが繰り返し実行)
export default function () {
  const res = http.get('http://localhost:3000/api/products');

  // レスポンスの検証
  check(res, {
    'status is 200': (r) => r.status === 200,
    'response time < 500ms': (r) => r.timings.duration < 500,
  });

  sleep(1); // 思考時間(1秒待機)
}

実行方法

# 基本実行
k6 run basic-test.js

# 出力例
# ✓ status is 200.......... 100.00%
# ✓ response time < 500ms.. 98.50%
#
# http_req_duration.....: avg=45ms min=12ms med=38ms max=380ms p(90)=82ms p(95)=120ms
# http_reqs.............: 2850   95.0/s

ステージ(段階的負荷)

// staged-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '2m', target: 50 },   // 2分かけて50VUまで増加
    { duration: '5m', target: 50 },   // 5分間50VUを維持
    { duration: '2m', target: 100 },  // 2分かけて100VUまで増加
    { duration: '5m', target: 100 },  // 5分間100VUを維持
    { duration: '2m', target: 0 },    // 2分かけて0VUまで減少
  ],
  thresholds: {
    // 成功基準
    http_req_duration: ['p(95)<300', 'p(99)<500'], // p95 < 300ms, p99 < 500ms
    http_req_failed: ['rate<0.01'],                 // エラー率 < 1%
  },
};

export default function () {
  const res = http.get('http://localhost:3000/api/products');

  check(res, {
    'status is 200': (r) => r.status === 200,
  });

  sleep(Math.random() * 3 + 1); // 1-4秒のランダムな思考時間
}

実践的なシナリオ

// scenario-test.js
import http from 'k6/http';
import { check, group, sleep } from 'k6';
import { SharedArray } from 'k6/data';

// テストデータの読み込み
const productIds = new SharedArray('productIds', function () {
  return JSON.parse(open('./test-data/product-ids.json'));
});

export const options = {
  scenarios: {
    // シナリオ1: 閲覧ユーザー(60%)
    browsers: {
      executor: 'ramping-vus',
      startVUs: 0,
      stages: [
        { duration: '2m', target: 60 },
        { duration: '5m', target: 60 },
        { duration: '1m', target: 0 },
      ],
      exec: 'browsingScenario',
    },
    // シナリオ2: 購入ユーザー(15%)
    buyers: {
      executor: 'ramping-vus',
      startVUs: 0,
      stages: [
        { duration: '2m', target: 15 },
        { duration: '5m', target: 15 },
        { duration: '1m', target: 0 },
      ],
      exec: 'purchaseScenario',
    },
  },
  thresholds: {
    http_req_duration: ['p(95)<300'],
    http_req_failed: ['rate<0.01'],
  },
};

// 閲覧シナリオ
export function browsingScenario() {
  group('Browse Products', function () {
    // 商品一覧を閲覧
    const listRes = http.get('http://localhost:3000/api/products');
    check(listRes, { 'list status 200': (r) => r.status === 200 });
    sleep(2);

    // ランダムな商品を閲覧
    const randomId = productIds[Math.floor(Math.random() * productIds.length)];
    const detailRes = http.get(`http://localhost:3000/api/products/${randomId}`);
    check(detailRes, { 'detail status 200': (r) => r.status === 200 });
    sleep(3);
  });
}

// 購入シナリオ
export function purchaseScenario() {
  group('Purchase Flow', function () {
    const randomId = productIds[Math.floor(Math.random() * productIds.length)];

    // 商品詳細を閲覧
    http.get(`http://localhost:3000/api/products/${randomId}`);
    sleep(2);

    // カートに追加
    const cartRes = http.post(
      'http://localhost:3000/api/cart',
      JSON.stringify({ productId: randomId, quantity: 1 }),
      { headers: { 'Content-Type': 'application/json' } }
    );
    check(cartRes, { 'cart add status 200': (r) => r.status === 200 });
    sleep(1);

    // 注文確定
    const orderRes = http.post(
      'http://localhost:3000/api/orders',
      JSON.stringify({ cartId: cartRes.json('cartId') }),
      { headers: { 'Content-Type': 'application/json' } }
    );
    check(orderRes, { 'order status 201': (r) => r.status === 201 });
    sleep(1);
  });
}

k6 の主要メトリクス

メトリクス説明目標値の例
http_req_durationリクエスト全体の所要時間p95 < 300ms
http_req_waitingサーバー処理時間(TTFB)p95 < 200ms
http_req_failed失敗したリクエストの割合< 0.1%
http_reqs1秒あたりのリクエスト数> 3000 RPS
vusアクティブな仮想ユーザー数-
iterationsテスト反復の完了数-

まとめ

ポイント内容
k6JavaScriptで書ける開発者フレンドリーな負荷テストツール
stages段階的に負荷を増減できる
scenarios複数のユーザー行動パターンを並行実行
thresholds自動的に合格/不合格を判定
checkレスポンスの正当性を検証

チェックリスト

  • k6の基本的なテストを書ける
  • stagesで段階的な負荷パターンを設定できる
  • scenariosで複数のユーザー行動を定義できる
  • thresholdsで成功基準を設定できる

次のステップへ

次は「結果分析とレポーティング」を学びます。テスト結果のp50、p95、p99の読み方と、ボトルネックの特定方法を理解しましょう。


推定読了時間: 30分