負荷テストツール『k6』をローカル環境でサクッと試してみた
はじめに
簡単なPHPアプリケーションとDockerコンテナを用意して、k6を試してみました!
k6は、軽量かつ高速なパフォーマンステストツールで、JavaScriptでテストスクリプトを記述できるのが特徴です。
今回は、特定のURLに対して負荷をかけるため、ローカル環境をDockerで構築しています。
「自分の環境でサクッと試したい」という方はぜひ参考にしてください!
Macにk6をインストールするには、以下のコマンドを実行します。
brew install k6
専用のJavaScriptファイルを用意すれば、すぐに動作させることができます。
構成
最低限の動作確認ができるシンプルなディレクトリ構成にしています。
├── docker-php-app
│ ├── Dockerfile
│ ├── docker-compose.yml
│ └── src
│ ├── calculate.php
│ ├── index.php
│ └── submit.php
└── script.js
環境作成
# PHP + Apache の公式イメージを使用
FROM php:8.1-apache
# Apache のドキュメントルートを設定
COPY src/ /var/www/html
# ポートを公開
EXPOSE 80
version: '3.8'
services:
php-apache:
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:80"
volumes:
- ./src:/var/www/html
実行処理
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>計算処理 - k6 Test App</title>
</head>
<body>
<h1>計算処理</h1>
<p>負荷テスト用の計算処理を実行しています...</p>
<?php
$result = 0;
// より重い計算処理
for ($i = 1; $i <= 1000; $i++) {
for ($j = 1; $j <= 1000; $j++) {
// 複雑な数学関数を利用
$value = sqrt($i) * log($j + 1) + pow($i, 2) * sin($j);
$result += $value;
}
}
echo "<p>計算結果: " . number_format($result, 2) . "</p>";
?>
</body>
</html>
※ 画面表示
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>フォーム送信 - k6 Test App</title>
</head>
<body>
<h1>フォーム送信</h1>
<form action="submit.php" method="post">
<label>名前: <input type="text" name="name" required></label>
<button type="submit">送信</button>
</form>
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = htmlspecialchars($_POST['name']);
echo "<p>送信された名前: " . $name . "</p>";
}
?>
</body>
</html>
※ 画面表示
負荷テストスクリプト
import http from 'k6/http';
import { check, sleep } from 'k6';
// 負荷テスト対象の URL
const BASE_URL = 'http://localhost:8080';
const CALCULATE_URL = `${BASE_URL}/calculate.php`;
const SUBMIT_URL = `${BASE_URL}/submit.php`;
// シナリオ設定
export const options = {
stages: [
{ duration: '10s', target: 100 }, // 10 秒で VU を 100 まで急増
{ duration: '2m', target: 100 }, // 2 分間 維持
{ duration: '10s', target: 0 } // 10 秒で 0 に急減
],
thresholds: {
http_req_duration: ['p(95)<500'], // 95% のリクエストが 500ms 未満
http_req_failed: ['rate<0.01'], // 失敗率を 1% 未満に抑える
}
};
// 負荷テストのメイン処理
export default function () {
// 1. calculate.php への GET リクエスト
const calculateRes = http.get(CALCULATE_URL);
check(calculateRes, {
'calculate.php: ステータスが 200': (res) => res.status === 200,
});
// 2. submit.php への POST リクエスト
const payload = { name: `user_${Math.floor(Math.random() * 1000)}` };
const submitRes = http.post(SUBMIT_URL, payload);
check(submitRes, {
'submit.php: ステータスが 200': (res) => res.status === 200,
});
// 1秒休憩
sleep(1);
}
ステージの流れと処理内容
⭐️急増フェーズ (10秒間で VU を 100 に増加)
負荷テストを開始した最初の10秒間で、仮想ユーザー(VU)が 0 から 100 に急速に増加します。
この段階でサーバーが急激な負荷にどれだけ耐えられるかを確認します。
⭐️維持フェーズ (2分間で VU を 100 のままで維持)
2分間、100人の仮想ユーザーがサーバーにリクエストを送り続けます。
高負荷状態を維持して、サーバーがこの負荷に対してどれくらいのスケーラビリティと安定性を持つかを測定します。
⭐️急減フェーズ (10秒間で VU を 0 に急減)
最後の10秒間で仮想ユーザーが急速に減少します。
サーバーが負荷を急激に減少させた後にどれだけ速やかに安定するかを確認します。
しきい値の設定
SLAとして、シナリオ設定に以下の条件を thresholds に設定しています。
- 95% のリクエストが 500ms 未満で応答すること
- エラーレートが 1% 未満であること
結果
k6 の実行結果は以下の通りです。
% k6 run script.js
/\ Grafana /‾‾/
/\ / \ |\ __ / /
/ \/ \ | |/ / / ‾‾\
/ \ | ( | (‾) |
/ __________ \ |_|\_\ \_____/
execution: local
script: script.js
output: -
scenarios: (100.00%) 1 scenario, 100 max VUs, 2m50s max duration (incl. graceful stop):
* default: Up to 100 looping VUs for 2m20s over 3 stages (gracefulRampDown: 30s, gracefulStop: 30s)
✓ calculate.php: ステータスが 200
✓ submit.php: ステータスが 200
checks.........................: 100.00% 21458 out of 21458
data_received..................: 16 MB 117 kB/s
data_sent......................: 2.9 MB 20 kB/s
http_req_blocked...............: avg=9.85µs min=0s med=4µs max=2.09ms p(90)=8µs p(95)=10µs
http_req_connecting............: avg=3.98µs min=0s med=0s max=687µs p(90)=0s p(95)=0s
✓ http_req_duration..............: avg=108.24ms min=298µs med=59.08ms max=3.26s p(90)=259.3ms p(95)=349.85ms
{ expected_response:true }...: avg=108.24ms min=298µs med=59.08ms max=3.26s p(90)=259.3ms p(95)=349.85ms
✓ http_req_failed................: 0.00% 0 out of 21458
http_req_receiving.............: avg=48.32µs min=6µs med=40µs max=2.05ms p(90)=86µs p(95)=107µs
http_req_sending...............: avg=17.04µs min=2µs med=15µs max=1.08ms p(90)=28µs p(95)=30µs
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=108.17ms min=270µs med=59ms max=3.26s p(90)=259.26ms p(95)=349.81ms
http_reqs......................: 21458 152.328208/s
iteration_duration.............: avg=1.21s min=1.05s med=1.16s max=4.27s p(90)=1.36s p(95)=1.5s
iterations.....................: 10729 76.164104/s
vus............................: 7 min=7 max=100
vus_max........................: 100 min=100 max=100
注目すべきポイントは以下の通りです。
-
http_req_duration
の p(95) が 349.85ms なので、95% のリクエストが 500ms 未満で応答していることが確認できました。 -
http_req_failed
が 0% であるため、エラーは発生していません。
これらの結果から、今回のPHPアプリケーションは 100 VU の負荷に対して十分に応答できていると言えます。
おわりに
初めてk6を触ってみましたが、ローカル環境でも簡単に試すことができました!
JavaScriptで実装できるため、条件分岐やループ、外部APIとの連携など、より複雑なシナリオを構築することも可能です。
ユーザーの行動パターンをシミュレーションしたり、認証が必要なフローをテストするなど、実際の利用状況に近い負荷テストを実施できるのが楽しみです!
Discussion