AKSとk6でブラウザベースの負荷試験を行う実行基盤を構築してみた
2026-02-05
azblob://2026/01/28/eyecatch/2026-01-28-aks-load-testing-k6-browser-000.png

はじめに

Web アプリケーションのパフォーマンス検証において、構築したシステムが非機能要件で定義されたパフォーマンスを満たすかどうかを確認するために、負荷試験を実施することは非常に重要です。
 
本記事では、オープンソースの負荷試験ツール「k6」と、そのブラウザ自動化機能「k6 browser」を使用して、Azure Kubernetes Service(以下、AKS)上でブラウザベースの負荷試験を実行できる基盤を構築してみます。
また、Kubernetes (以下、k8s) 上でk6を運用するために、「k6-operator」を活用します。k6-operator を使用することで、複数の Pod で負荷を分散させた大規模な試験や、k8s のリソースを活用したスケーラブルな負荷試験環境を実現できます。
※ 本記事ではブラウザベースの負荷試験を行いますが、大規模な負荷試験での使用はあまり推奨されていません。理由としては、フロントエンドのテストを自動化するツールは大量のリソースが必用で多大なコストが発生するためです。
 
Some concerns when doing this type of performance testing are its dependency on fully integrated environments and the cost of scaling. You can test frontend performance only once the application code and infrastructure have been integrated with a user interface. Tools to automate frontend testing are also inherently more resource-intensive, so they can be costly to run at scale and are not suitable for high load tests.
 

この記事で得られること

 
  • k6 と k6 browser の基本的な理解
  • AKS 上で k6-operator を使った、分散環境でブラウザベースの負荷試験を実行する基盤の構築方法

前提条件

本記事の手順を実行するには、以下の環境とツールが必要です。

Azure 環境

  • AKS および Azure Container Registry (以下、ACR) を作成できる権限を持つ Azure サブスクリプション
  • Azure CLI に適切な認証情報が設定されていること

ローカル環境のツール

以下のツールがインストールされていることを前提とします。
  • Azure CLI: バージョン 2.81.0
  • k8s クラスター: バージョン 1.33.6
  • kubectl: バージョン 1.33.0
  • Helm: バージョン 3.17.1
  • Docker CLI: バージョン 27.5.1-rd で動作確認済み
  • コンテナランタイム (Docker Desktop や Rancher Desktop など)
  • k6: バージョン 1.4.0
  • k6-operator: バージョン 4.2.0

用語説明

k6 とは

k6 は Grafana Labs が開発するオープンソースの負荷試験ツールです。JavaScript (ES6) でテストシナリオを記述でき、シンプルで読みやすいコードで負荷試験を実装できます。


k6 の主な特徴

  • シンプルな記述: JavaScript でテストシナリオを記述
  • 高パフォーマンス: Go 言語で実装されており、軽量で高速
  • 豊富なメトリクス: レスポンスタイム、スループット、エラー率など、詳細なメトリクスを収集
  • 拡張性: カスタムメトリクスの定義や外部サービスとの連携が可能

k6 browser とは

k6 browser は、k6 に統合されたブラウザ自動化機能です。Playwright をベースとしており、実際のブラウザ (Chromium) を使用して Web ページの操作を自動化できます。
 

k6 browser の主な特徴

  • 実ブラウザでの動作: Chromium を使用し、JavaScript の実行やレンダリングを含めた負荷試験が可能
  • k6 との統合: 従来の HTTP ベースの負荷試験と組み合わせて使用できる
  • ユーザー操作の再現: クリック、入力、スクロールなど、実際のユーザー操作をシミュレート

k6-operator とは

k6-operator は、k8s 上で k6 の負荷試験を実行・管理するための Operator です。

k6-operator の主な特徴

  • カスタムリソースによる管理: TestRun などのカスタムリソースで負荷試験を定義できる
  • 分散実行: 複数の Pod で負荷を分散させ、大規模な負荷試験を実行可能
  • 宣言的な定義: YAML マニフェストで試験内容を定義し、kubectl で管理
  • 自動スケーリング: 指定した並列数に応じて自動的に Pod を起動
参考リンク

ブラウザベースの負荷試験とは

ブラウザベースの負荷試験は、実際のブラウザを使用してエンドユーザーの操作を想定して Web アプリケーションに負荷をかける試験手法です。
※ 前述の通り、この負荷試験の実行には大量のリソースが消費されるため、大規模な試験には適しません。もし大規模な負荷試験を実行する場合は、対義語として挙げられるプロトコルベースの負荷試験を実施する必要があります。
Backend testing is less resource-intensive than frontend performance testing and is thus more suitable for generating high load.
 

インフラ構成

本記事で構築する負荷試験基盤の全体構成は以下の通りです。

構成図
  

構成要素

Azure リソース

  • AKS (Azure Kubernetes Service): k6-operator と負荷試験用 Pod を実行する k8s クラスタ
  • ACR (Azure Container Registry): 負荷試験シナリオを含む Docker イメージを格納

Kubernetes リソース

  • k6-operator: TestRun カスタムリソースを監視し、その定義に基づいて負荷試験用の Job を作成
  • TestRun (カスタムリソース)**: 負荷試験の定義 (シナリオ、並列数、実行条件など)
  • 負荷試験用 Job**: TestRun の定義に基づいて作成される Job
  • 負荷試験用 Pod**: 実際に k6 を実行し、対象システムに負荷をかける Pod


名前空間について

k6-operator 自体は k6-operator-system 名前空間に、負荷試験用のリソースは k6-load-testing 名前空間に配置します。
これにより、管理するリソースを用途ごとに分離し、整理された環境を構築できます。
 
 

負荷試験実施の流れ

下記の数値リストの添え字は構成図の負荷試験実施の流れに基づきます。
0に関しては別途追加しています。
0. 事前準備: ローカル環境で負荷試験シナリオを含む Docker イメージを作成し、必要な Azure リソース (AKS と ACR) を構築
1. 負荷試験シナリオの登録: Docker イメージを ACR にプッシュ
2. k6-operator のインストール: AKS クラスタに k6-operator を Helm でインストール
3. TestRun の作成: 負荷試験の定義を記述した TestRun カスタムリソースを kubectl で作成
4. カスタムリソースの状態監視とトリガー: k6-operator が TestRun を検知し、カスタムリソースの状態に基づいて Job を作成
5. Pod の作成: Job が指定された並列数分の Pod を起動
6. 負荷試験の実行: 各 Pod 上で k6 brwoser が実行され、負荷試験対象のシステムに対してブラウザベースの負荷をかける
これらの構成と実行フローにより、単一の実行環境では実現できない大規模な分散環境でのブラウザベースの負荷試験が可能になります。

リソースの構築と負荷試験の実施

本章では「負荷試験実施の流れ」に従って、リソースの構築と負荷試験の実施をしていきます。
進め方は下記の通り想定しています。
0. 事前準備
  • Azure リソース (AKS、ACR) の作成
  • 負荷試験シナリオと Dockerfile の作成
1. 負荷試験シナリオの登録
  • Docker ファイルのビルド
  • Docker イメージを ACR にプッシュ
2. k6-operator のインストール
  • Helm を使用して AKS クラスタに k6-operator をインストール
3. TestRun の作成と負荷試験の実施
  • TestRun カスタムリソースを作成して負荷試験を実行

0. 事前準備

0-1. Azure リソースの作成

本セクションでは、負荷試験基盤に必要な Azure リソースを作成します。
リソースの作成は Azure CLI を実行して作成します。
# 変数の定義
RESOURCE_GROUP_NAME="rg-k6-loadtest-ve-je"
LOCATION="japaneast"
ACR_NAME="crk6loadtestveje"
AKS_NAME="aks-k6-loadtest-ve-je"

# リソースグループの作成
az group create \
  --name $RESOURCE_GROUP_NAME \
  --location $LOCATION

# ACR の作成
az acr create \
  --resource-group $RESOURCE_GROUP_NAME \
  --name $ACR_NAME \
  --sku Basic

# ACR へのログイン
az acr login --name $ACR_NAME

# AKS クラスタの作成
## クラスターの作成には数分かかります。完了したら、クラスターへの接続情報を取得します。
az aks create \
  --resource-group $RESOURCE_GROUP_NAME \
  --name $AKS_NAME \
  --node-count 2 \
  --node-vm-size Standard_D4ds_v5 \
  --attach-acr $ACR_NAME \
  --generate-ssh-keys
 
 # AKS クラスタの認証情報を取得
az aks get-credentials \
  --resource-group $RESOURCE_GROUP_NAME \
  --name $AKS_NAME

# 接続確認
kubectl config current-context
接続確認で "aks-k6-loadtest-ve-je" と返されたら OK です。

0-2. 負荷試験シナリオの作成

本セクションでは、k6 browser を使用した負荷試験シナリオを作成します。

ディレクトリ構成

以下のディレクトリ構成をホームディレクトリの直下に作成します。
k6-browser-test/
├── Dockerfile
└── test/
    └── browser-test.js

 browser-test.js のファイルの内容は下記の通りです。

# browser-test.js

import { browser } from 'k6/browser';

export const options = {
  scenarios: {
    phase1: {
      executor: 'constant-vus',
      vus: 1,
      duration: '25s',
      startTime: '0s',
      gracefulStop: '5s',
      options: {
        browser: {
          type: 'chromium',
        },
      },
    },
    phase2: {
      executor: 'constant-vus',
      vus: 2,
      duration: '25s',
      startTime: '30s',
      gracefulStop: '5s',
      options: {
        browser: {
          type: 'chromium',
        },
      },
    },
    phase3: {
      executor: 'constant-vus',
      vus: 3,
      duration: '25s',
      startTime: '60s',
      gracefulStop: '5s',
      options: {
        browser: {
          type: 'chromium',
        },
      },
    },
  },
  // 閾値は仮置きです。
  thresholds: {
    checks: ['rate==1.0'],
  },
};

export default async function () {
  const page = await browser.newPage();
 
  try {
    await page.goto('https://test.k6.io/');
    await page.screenshot({ path: 'screenshots/screenshot.png' });
  } finally {
    await page.close();
  }
}

 
テストの内容は下記の通りです。
  • 30秒ごとに仮想ユーザー数が増え、段階的に負荷をかける
  • 仮想ユーザー一人あたりの操作は、テスト用に用意されているk6の Web サイトにアクセスをし、スクリーンショットを取得することの繰り返し操作
その他のオプションに関しては下記が参考となります。

Dockerfile の内容は下記の通りです。

 
# Dockerfile

FROM grafana/k6:latest-with-browser
 
COPY --chown=k6:k6 test/ /test/ 

RUN chown -R k6:k6 /test/ && \
    chmod -R 755 /test/ 
    
# 非ルートユーザーに切替
USER k6 

WORKDIR /test/ 

# 実行結果の確認を行うため、コンテナを起動し続ける。
ENTRYPOINT ["tail", "-f", "/dev/null"]

 
使用するイメージは grafana/k6:latest-with-browser を使用します。
リリースされているバージョンの中で最新のもの、かつ、k6 browser が実行可能なランタイムをイメージに含めたいためです。
タグに関するリファレンスは「Available Tags | grafana/k6 | Docker Hub」を参考にしています。

1. 負荷試験シナリオの登録

本セクションでは、作成した負荷試験シナリオを含む Dockerfile をビルドしてイメージを作成し、ACR にプッシュします。
 
# 変数の初期化
IMAGE_NAME='k6-browser-test'
IMAGE_TAG='v1.0.0'

# k6-browser-test ディレクトリに移動
cd $HOME/k6-browser-test

# イメージのビルド
docker build -t ${IMAGE_NAME}:${IMAGE_TAG} .

# イメージのタグ付け
## ACR のログインサーバー名を取得
ACR_LOGIN_SERVER=$(az acr show --name $ACR_NAME --query loginServer --output tsv)

## イメージにタグを付ける
docker tag ${IMAGE_NAME}:${IMAGE_TAG} ${ACR_LOGIN_SERVER}/${IMAGE_NAME}:${IMAGE_TAG}

# イメージをプッシュ
docker push ${ACR_LOGIN_SERVER}/${IMAGE_NAME}:${IMAGE_TAG}
これで負荷試験を実行するためのイメージの準備まで完了です。

2. k6-operator のインストール 

本セクションでは、Helm を使用して AKS クラスタに k6-operator をインストールします。 
下記コマンドを実行して、インストールを進めます。
 
# k6-operator の Helm リポジトリの追加
helm repo add grafana https://grafana.github.io/helm-charts

# リポジトリの更新
helm repo update

# k6-operator のインストール
helm install k6-operator grafana/k6-operator --version 4.2.0

# k6-operator のリソースの状態を確認
# Pod の状態が Running になっていない場合は、少し待ってから再実行
# wait コマンドで deploy の状態に基づいて待機してもいい
kubectl get all -n k6-operator-system
出力された k6-operator のすべてのリソースに問題がなければ、k6-operator のインストールは完了です。

3. TestRun の作成と負荷試験の実施

本セクションでは、TestRun カスタムリソースを作成して負荷試験を実施します。

3-1. TestRun マニフェストの作成 

TestRun を定義する YAML ファイルを作成します。
# testrun.yaml
apiVersion: k6.io/v1alpha1
kind: TestRun
metadata:
  name: k6-browser-test
  namespace: k6-load-testing
spec:
  parallelism: 3 # シナリオテストで指定している VUs の最大値に設定しています。
  script:
    localFile: /test/browser-test.js
  runner:
    image: crk6loadtestveje.azurecr.io/k6-browser-test:v1.0.0 # grafana/k6:latest-with-browser イメージから作成したカスタムイメージ
    topologySpreadConstraints: # Pod の配置を複数のノードに分散し、各ノードごとに配置される負荷試験用の Pod の数の差分が 1 以内にする。
      - maxSkew: 1
        whenUnsatisfiable: DoNotSchedule
        topologyKey: kubernetes.io/hostname
        labelSelector:
          matchLabels:
            k6_cr: k6-browser-test # metadata.name の値と一致させる

 

3-2. TestRun の作成

下記コマンドを実行して TestRun を作成します。
 
 
# TestRun を作成するための名前空間を作成する。
kubectl create namespace k6-load-testing

# TestRun を作成
kubectl apply -f testrun.yaml

# TestRun の確認
kubectl get testrun -n k6-load-testing
TestRun の状態が started となっていれば実行中で、実行が完了すれば finished となります。
started の状態になっている場合は、次のステップに進みます。

3-3. Job と Pod の確認

TestRun が作成されると、k6-operator が自動的に Job を作成し、Job が 負荷試験を行う Pod を起動します。
 
# Job の確認
kubectl get jobs -n k6-load-testing

# Pod の確認
kubectl get pods -n k6-load-testing
負荷試験の実行が完了するとすべての Job と Pod は状態が Completed となります。
最後に改めて下記コマンドを実行し、TestRun の状態が finished になっていることを確認します。
 
# TestRun の確認
kubectl get testrun -n k6-load-testing

 

実行結果の確認方法

それではこれで負荷試験の実行が完了になるので、実行ログと必要に応じてログをローカルに保存しておきます。

Pod のログ確認

負荷試験の実行ログは、下記コマンドを実行することで、各 Pod のログから確認できます。
※ 出力されるメトリックの説明は割愛します。Browser metrics から詳細を確認することが可能です。
# Pod 名を確認
kubectl get pods -n k6-load-testing
 
# 特定の Pod のログを確認
# ${POD_NAME} は実際の Pod の名前に置き換えてください。
kubectl logs pod/${POD_NAME} -n k6-load-testing

 
実行ログは Pod の削除に伴って削除されてしまうため、必要であればログをどこかに保存しておきましょう。
ここまでくれば最後に下記コマンドを実行して TestRun リソースを削除します。
TestRun 削除に伴ってそれに紐づく Job や Pod はすべて削除されます。
# TestRun の削除
kubectl delete -f ./testrun.yaml
 

まとめ

本記事では、AKS 上で k6-operator を使用したブラウザベースの負荷試験基盤の構築手順を紹介しました。
ユースケースとしてブラウザベースの負荷試験を実施することは多くはないかもしれませんが、非機能要件を満たすかどうかを確認する際に必要な負荷試験を実施して信頼性の高いシステムを構築していきたいと思います。

おまけ

負荷試験の基盤構築を進めていく中での発見や困ったことなどを簡単にまとめておきます。

負荷試験の種類について

k6 の公式ドキュメント内に初学者でもわかりやすいように負荷試験の種類についてまとめられているドキュメントがありました。
理解しやすくコンパクトにまとめられているので読んでみるとおもしろいです。

k6 のデフォルトイメージでは k6 browser が使用できない

Dockerfile の作成時にも記載した通り、デフォルトの k6 のイメージでは k6 browser のランタイム等が含まれません。k6 browser を使用する必要がある場合は、latest-with-browsermaster-with-browser を使用しましょう。タグに関するリファレンスは「Available Tags | grafana/k6 | Docker Hub」を参考にしています。

負荷試験実行時に CPU やメモリなどのリソースが不足する

負荷試験実行時にマシンリソースが足りない場合は、負荷試験実行用の Pod のログに `time="yyyy-mm-ddThh:mm:ssZ" level=error msg="process with PID XXXX unexpectedly ended: context canceled" category=browser elapsed="0 ms" source=browser` が表示されました。
 
OOM キラーでしょうか?
このエラー発生時に、AKS のワーカーノードメトリックを確認するメモリが不足していることが確認できました。
ワーカーノードのスケールアップを行う必要がありそうです。

シナリオテストのファイルの管理方法

k6-operator によるシナリオファイルの管理方法は下記の3つがあります。
 
  • ConfigMap
  • VolumeClaim
  • LocalFile
 
ConfigMap にはシナリオテストのファイルサイズの上限が 1MiB であり、共通ロジックなどを記載したテストファイルを参照できないこと。また、VolumeClaim は PVC シナリオを記述する必要があり、セキュリティの担保が必用なこと。
これらの制限と複雑性から LocalFile を用いてシナリオファイルを管理することにしました。

インフラランニングコスト

実際にブラウザベースの負荷試験を実行できましたがその一方で、膨大なインフラランニングコストが発生する可能性が高いです。
負荷試験のためだけに AKS を構築した場合は、負荷試験の実行が完了したらすぐにリソースを削除することをお勧めします。

送信元 IP 制限やポート枯渇

 
今回は非常に少ない負荷量で試験を実施しました。
 
業務レベルのユースケースでは、負荷試験の対象となるシステムで送信元 IP アドレスの制限を設ける必要や、莫大なアクセス量によって送信元で Port の枯渇が発生するかもしれません。
 
このようなユースケースであれば、NatGateway + Public IP Address (or Public IP Address Prefix)を設けることで解決することができるかもしれません。