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

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

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

「k6負荷テスト」シリーズの最終回です。

これまでの4回で、「APIの叩き方」「負荷パターンの設計」「実践的なシナリオ構築」「メトリクスの分析方法」を解説してきました。

第1回でもお伝えしましたが、負荷テストは特別な行事ではなく、開発において継続的に実施する日常的な業務、だと私は捉えています。
そこで今回は「負荷テストのCI/CD統合」に挑戦したいと思います。

本記事でも教材を使います。
リポジトリの scenarios/05-cicd をご参照下さい。

※ 本記事では Github Actions についての解説は割愛させていただきます。

1. k6の合否判定の仕組み

負荷テストをCI/CDに組み込む前に、k6がテストの合否(Pass/Fail)をどう判定しているかを押さえておきましょう。

以下が合否における仕様です。

  • k6 run は終了コードを返却する
    終了コードとは
  • 閾値(thresholds)をすべて満たせば 0(正常終了)
  • 閾値を1つでも下回ると 非ゼロ(異常終了)

GitHub Actionsなどの最近のCIツールは、この終了コードを見て自動的にビルドを「失敗(Red)」にしてくれます。
つまり、閾値さえ正しく設定すれば、合否判定のために特別なスクリプトを書く必要はありません。

2. 環境の特性に合わせて閾値を設定する

合否判定が閾値に基づく以上、CI/CDへの組み込みにあたって閾値の設計が重要となります。

すべての環境に同じ基準を適用するのはあまり現実的ではないです。
「開発環境はフィードバックを早く回すことを優先して緩い基準」に、「ステージング環境は本番に近い基準に」、というように環境特性に合わせた設定が出来れば良いと思います。

では、リポジトリの scenarios/05-cicd/01-threshold-validation.js を見てみましょう。

// 環境ごとに異なる閾値を設定
const thresholds = {
  dev: {
    http_req_failed: ['rate<0.1'],        // 開発環境: 10%まで許容
    http_req_duration: ['p(95)<1000'],    // 開発環境: 1秒
    checks: ['rate>0.9'],                 // 開発環境: 90%以上
  },
  staging: {
    http_req_failed: ['rate<0.05'],       // ステージング: 5%まで許容
    http_req_duration: ['p(95)<500'],     // ステージング: 500ms
    checks: ['rate>0.95'],                // ステージング: 95%以上
  },
};

const ENVIRONMENT = __ENV.ENVIRONMENT || 'dev';

export const options = {
  // CI/CDに適した設定(短時間で完了させる)
  stages: [
    { duration: '30s', target: 10 },
    { duration: '1m', target: 10 },
    { duration: '30s', target: 0 },
  ],
  thresholds: thresholds[ENVIRONMENT] || thresholds.dev,
};

実行

環境変数 ENVIRONMENT を渡して実行します。
※ ローカル環境で実行可能なものです。
※ k6をインストールしていない人は2つ目のコマンドでDocker環境でご実施下さい。

# ステージング環境の厳しい閾値でテストを実行
ENVIRONMENT=staging BASE_URL=http://localhost:3000 k6 run scenarios/05-cicd/01-threshold-validation.js

# Docker環境で実行
docker run --rm -i --network=host \
  -e ENVIRONMENT=staging BASE_URL=http://localhost:3000 \
  grafana/k6 run  - < scenarios/05-cicd/01-threshold-validation.js

ワンポイントアドバイス

CI/CDにおける負荷テストは、「実行時間を短くする(長くても5分以内)」のがポイントです。

何十分もかかるテストをPull Requestの度に回すと、開発体験が著しく低下することが想像できますよね。
長時間のストレステストはNightlyビルド(いわゆる夜間バッチ)などに回し、通常のCIでは「サニティチェック(短時間の性能劣化検知)」に留めるのが現場でよく取られるアプローチです。

2. GitHub Actionsへの組み込み

k6は公式のGitHub Actionsを提供しているため、YAMLファイルを書くだけでパイプラインに組み込めます。

以下のワークフローファイルをリポジトリの .github/workflows/ に配置してください。
※ 本記事では Github Actions についての解説は割愛させていただきます。

name: k6 Load Tests

on:
  pull_request:
    branches: [main]

jobs:
  load-test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      # k6公式アクションを使用してテストを実行
      - name: Run k6 test
        uses: grafana/k6-action@v0.3.0
        with:
          filename: scenarios/05-cicd/01-threshold-validation.js
        env:
          ENVIRONMENT: staging
          BASE_URL: ${{ secrets.STAGING_API_URL }}

これで、mainブランチへのPull Requestが作成されるたびに、ステージング環境に向けた負荷テストが自動実行されます。
自分の書いたコードが原因でAPIのレスポンスが極端に悪化した場合、このステップが「Fail」になりマージを防ぐことができます。

4. テスト結果をHTMLレポートで出力する

CI/CDのコンソール出力(標準出力)は、非エンジニアにとっては読みづらいものだと思います。

k6の handleSummary() 関数を使うと、テスト終了後のサマリーデータを任意の形式で出力できます。

例えば、「職種問わずチーム全体でグラフィックとして共有するためにHTMLとして出力」出来たり、「解析結果を独自のアプリケーションと統合するためにJSON形式で出力」することが出来ます。

scenarios/05-cicd/02-json-output.js を参照してください。

import { htmlReport } from '<https://raw.githubusercontent.com/benc-uk/k6-reporter/main/dist/bundle.js>';
import { textSummary } from '<https://jslib.k6.io/k6-summary/0.0.1/index.js>';

// メインのテスト処理
export default function () {
  http.get(`${BASE_URL}/api/users`);
}

// カスタムサマリーハンドラー
export function handleSummary(data) {
  return {
    // コンソールには標準のテキストサマリーを表示
    'stdout': textSummary(data, { indent: ' ', enableColors: true }),
    
    // JSON形式で詳細結果を保存
    'summary.json': JSON.stringify(data),
    
    // 見やすいHTMLレポートを生成
    'summary.html': htmlReport(data),
  };
}

実行

※ ローカル環境で実行可能なものです。

k6 run scenarios/05-cicd/02-json-output.js

実行後、ディレクトリに summary.html が生成されます。
これをブラウザで開くと、グラフや表で綺麗に整形されたテストレポートを確認することが可能です。

以下、生成されたレポートのイメージです。

生成されたレポートのイメージ画像。

GitHub Actionsでレポートを保存する

ワークフローに以下のステップを追加すると、HTMLレポートをArtifactsとして保存できます。

- name: Upload test results
        if: always() # テストが失敗してもレポートは保存する
        uses: actions/upload-artifact@v3
        with:
          name: k6-results
          path: summary.html`

PRの画面からいつでもレポートをダウンロードできるようになります。

5. CI/CDで負荷テストを運用するコツ

最後に、CI/CDで負荷テストを継続的に回すための実践的なポイントを紹介します。

実行時間は短く保つ

CI/CDの負荷テストは、長くても5分以内に収めるのが鉄則です。 何十分もかかるテストをPull Requestのたびに回すと、開発体験が著しく低下します。

通常のCIでは「サニティチェック(短時間の性能劣化検知)」に留め、長時間のストレステストはNightlyビルド(夜間バッチ)に回すのが現場でよく取られるアプローチです。

閾値は育てるもの

最初から完璧な閾値を設定する必要はありません。
まずは緩めの基準で始めて、実際の計測データを見ながら徐々に締めていくのが現実的です。

まとめ

全5回にわたって「k6で始める負荷テスト」をテーマにお届けしてきました。

k6を使えば、私たちが普段書いているJavaScript/TypeScriptのコードと同じ感覚で、パフォーマンスを「テスト可能な仕様」として管理できます。

用意している 教材 には、連載では触れていないシナリオやトラブルシューティング方法も用意しておりますのでぜひご活用下さい。
まずは「ヘルスチェックAPIを1VUで叩く」といった、ごく小さなところから試せる「取り組みやすさ」もk6の魅力だと思います。

今回の連載で、「負荷テストはインフラチームにお願いするもの」「準備が億劫」といった印象が少しでもなくなったのであれば幸いです。

以上です。ここまで読んできただきありがとうございます。

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

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

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

最新の関連記事

Tag

Category

Contents

Download

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

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

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

資料ダウンロード

Download 資料ダウンロード

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


Contact お問い合わせ

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