負荷テストを手軽にできるツール「k6」を試してみた
はじめに
現在、開発を進めているプロダクトの負荷テストをすることになり、簡単にできる方法がないか探していたところ、「k6」というツールを使って手軽に負荷テストをすることができたので紹介します。
k6とは
k6は、オープンソースの負荷テストツールで、JavaScriptにテストシナリオを記述して負荷テストを実施することができます。
負荷テストはローカルマシンから実行します。
またクラウド上から負荷テストを実行できるk6クラウドというSaaS型のサービスもあります。
オープンソース版とクラウド版の比較はこちら
k6のインストール
k6は、installationに各OSごとのインストール方法が記載されており、簡単にインストールできます。
macOSの場合はHomebrewを使ってインストールします。
brew install k6
k6の使い方
シナリオを記述したJavaScriptファイルを作成します。例えば、以下のようなシナリオを記述したtest.jsファイルを用意します。
import http from 'k6/http';
import { sleep } from 'k6';
export default function () {
http.get('<https://test.k6.io>');
sleep(1);
}
このシナリオでは、https://test.k6.io に対してHTTP GETリクエストを送信し、1秒待機するものです。
k6コマンドを使って負荷テストを実行します。
k6 run test.js
実行するとこんな感じでテスト結果が出力されます。
/\\ |‾‾| /‾‾/ /‾‾/
/\\ / \\ | |/ / / /
/ \\/ \\ | ( / ‾‾\\
/ \\ | |\\ \\ | (‾) |
/ __________ \\ |__| \\__\\ \\_____/ .io
execution: local
script: test.js
output: -
scenarios: (100.00%) 1 scenario, 1 max VUs, 10m30s max duration (incl. graceful stop):
* default: 1 iterations for each of 1 VUs (maxDuration: 10m0s, gracefulStop: 30s)
data_received..................: 17 kB 11 kB/s
data_sent......................: 438 B 271 B/s
http_req_blocked...............: avg=434.89ms min=434.89ms med=434.89ms max=434.89ms p(90)=434.89ms p(95)=434.89ms
http_req_connecting............: avg=173.52ms min=173.52ms med=173.52ms max=173.52ms p(90)=173.52ms p(95)=173.52ms
http_req_duration..............: avg=177.93ms min=177.93ms med=177.93ms max=177.93ms p(90)=177.93ms p(95)=177.93ms
{ expected_response:true }...: avg=177.93ms min=177.93ms med=177.93ms max=177.93ms p(90)=177.93ms p(95)=177.93ms
http_req_failed................: 0.00% ✓ 0 ✗ 1
http_req_receiving.............: avg=143µs min=143µs med=143µs max=143µs p(90)=143µs p(95)=143µs
http_req_sending...............: avg=72µs min=72µs med=72µs max=72µs p(90)=72µs p(95)=72µs
http_req_tls_handshaking.......: avg=197.42ms min=197.42ms med=197.42ms max=197.42ms p(90)=197.42ms p(95)=197.42ms
http_req_waiting...............: avg=177.72ms min=177.72ms med=177.72ms max=177.72ms p(90)=177.72ms p(95)=177.72ms
http_reqs......................: 1 0.618231/s
iteration_duration.............: avg=1.61s min=1.61s med=1.61s max=1.61s p(90)=1.61s p(95)=1.61s
iterations.....................: 1 0.618231/s
vus............................: 1 min=1 max=1
vus_max........................: 1 min=1 max=1
running (00m01.6s), 0/1 VUs, 1 complete and 0 interrupted iterations
default ✓ [======================================] 1 VUs 00m01.6s/10m0s 1/1 iters, 1 per VU
さらにコマンドのオプションで、10人の仮想ユーザーを使って30秒間負荷テストを実行できます。
k6 run --vus 10 --duration 30s test.js
プログレスバーが右に進み、処理が進行しているのが分かります。
/\ |‾‾| /‾‾/ /‾‾/
/\ / \ | |/ / / /
/ \/ \ | ( / ‾‾\
/ \ | |\ \ | (‾) |
/ __________ \ |__| \__\ \_____/ .io
execution: local
script: test.js
output: -
scenarios: (100.00%) 1 scenario, 10 max VUs, 1m0s max duration (incl. graceful stop):
* default: 10 looping VUs for 30s (gracefulStop: 30s)
running (0m04.0s), 10/10 VUs, 28 complete and 0 interrupted iterations
default [====>---------------------------------] 10 VUs 04.0s/30s
結果は下記の通り。
/\ |‾‾| /‾‾/ /‾‾/
/\ / \ | |/ / / /
/ \/ \ | ( / ‾‾\
/ \ | |\ \ | (‾) |
/ __________ \ |__| \__\ \_____/ .io
execution: local
script: test.js
output: -
scenarios: (100.00%) 1 scenario, 10 max VUs, 1m0s max duration (incl. graceful stop):
* default: 10 looping VUs for 30s (gracefulStop: 30s)
data_received..................: 3.0 MB 96 kB/s
data_sent......................: 28 kB 911 B/s
http_req_blocked...............: avg=17.47ms min=2µs med=5µs max=457.81ms p(90)=8µs p(95)=24.69µs
http_req_connecting............: avg=6.87ms min=0s med=0s max=182.71ms p(90)=0s p(95)=0s
http_req_duration..............: avg=182.93ms min=174.29ms med=176.02ms max=349.4ms p(90)=177.33ms p(95)=178.13ms
{ expected_response:true }...: avg=182.93ms min=174.29ms med=176.02ms max=349.4ms p(90)=177.33ms p(95)=178.13ms
http_req_failed................: 0.00% ✓ 0 ✗ 254
http_req_receiving.............: avg=6.91ms min=32µs med=70.5µs max=173.03ms p(90)=355µs p(95)=628.79µs
http_req_sending...............: avg=26.9µs min=9µs med=20µs max=684µs p(90)=35µs p(95)=40µs
http_req_tls_handshaking.......: avg=8.05ms min=0s med=0s max=216.55ms p(90)=0s p(95)=0s
http_req_waiting...............: avg=175.99ms min=173.46ms med=175.82ms max=196.98ms p(90)=176.9ms p(95)=177.23ms
http_reqs......................: 254 8.180674/s
iteration_duration.............: avg=1.2s min=1.17s med=1.17s max=1.63s p(90)=1.17s p(95)=1.34s
iterations.....................: 254 8.180674/s
vus............................: 4 min=4 max=10
vus_max........................: 10 min=10 max=10
running (0m31.0s), 00/10 VUs, 254 complete and 0 interrupted iterations
default ✓ [======================================] 10 VUs 30s
結果の出力内容
k6 run test.js
を実行すると、各メトリクスの集計結果が出力されます。
またオプションで閾値を設定した場合(後述)、閾値チェックの合否結果も出力されます。
/\ |‾‾| /‾‾/ /‾‾/
/\ / \ | |/ / / /
/ \/ \ | ( / ‾‾\
/ \ | |\ \ | (‾) |
/ __________ \ |__| \__\ \_____/ .io
execution: local
script: test.js
output: -
scenarios: (100.00%) 1 scenario, 10 max VUs, 1m0s max duration (incl. graceful stop):
* default: 10 looping VUs for 30s (gracefulStop: 30s)
data_received..................: 3.0 MB 96 kB/s
data_sent......................: 28 kB 911 B/s
http_req_blocked...............: avg=17.47ms min=2µs med=5µs max=457.81ms p(90)=8µs p(95)=24.69µs
http_req_connecting............: avg=6.87ms min=0s med=0s max=182.71ms p(90)=0s p(95)=0s
http_req_duration..............: avg=182.93ms min=174.29ms med=176.02ms max=349.4ms p(90)=177.33ms p(95)=178.13ms
{ expected_response:true }...: avg=182.93ms min=174.29ms med=176.02ms max=349.4ms p(90)=177.33ms p(95)=178.13ms
http_req_failed................: 0.00% ✓ 0 ✗ 254
http_req_receiving.............: avg=6.91ms min=32µs med=70.5µs max=173.03ms p(90)=355µs p(95)=628.79µs
http_req_sending...............: avg=26.9µs min=9µs med=20µs max=684µs p(90)=35µs p(95)=40µs
http_req_tls_handshaking.......: avg=8.05ms min=0s med=0s max=216.55ms p(90)=0s p(95)=0s
http_req_waiting...............: avg=175.99ms min=173.46ms med=175.82ms max=196.98ms p(90)=176.9ms p(95)=177.23ms
http_reqs......................: 254 8.180674/s
iteration_duration.............: avg=1.2s min=1.17s med=1.17s max=1.63s p(90)=1.17s p(95)=1.34s
iterations.....................: 254 8.180674/s
vus............................: 4 min=4 max=10
vus_max........................: 10 min=10 max=10
running (0m31.0s), 00/10 VUs, 254 complete and 0 interrupted iterations
default ✓ [======================================] 10 VUs 30s
メトリクス
メトリクスには組み込みメトリクスとユーザーで定義できるカスタムメトリクスがあります。
ここでは組み込みメトリクスの一部を紹介します。
詳細は公式のmetricsのページをご覧ください。
メトリック名 | 説明 |
---|---|
data_received | 受信したデータの量 |
data_sent | 送信されたデータ量 |
http_req_blocked | リクエストを開始するまでにブロック(TCPコネクションスロットの空き待ち)された時間 |
http_req_connecting | リモートホストとのTCPコネクション確立に要した時間 |
http_req_duration | リクエストの合計時間(http_req_sending + http_req_waiting + http_req_receiving) |
http_req_failed | 失敗したリクエストの割合 |
http_req_receiving | リモートホストからの応答データ受信に要した時間 |
http_req_sending | リモートホストへのデータ送信に費やした時間 |
http_req_tls_handshaking | リモートホストとのTLSセッションのハンドシェイクに費やした時間 |
http_req_waiting | リモートホストからの応答待ち時間(time to first byte TTFBとも呼ばれる) |
http_reqs | httpリクエストの総数 |
iteration_duration | 1回のイテレーションの実行に要した時間 |
iterations | vus(仮想ユーザー)が実行したデフォルト関数の回数 |
vus | virtual usersの略 同時にアクセスする仮想ユーザー数 |
vus_max | 仮想ユーザーの最大可能数 |
オプション
k6 run test.js
を実行するたびに、--vus 10 と --duration 30s と入力する代わりに、JavaScriptファイルにオプションとして指定することができます。
import http from 'k6/http';
import { sleep } from 'k6';
export const options = {
vus: 10,
duration: '30s',
};
export default function () {
http.get('http://test.k6.io');
sleep(1);
}
こちらのページで指定できるオプションを参照することができます。
ここでは指定できるオプションの一部を紹介します。
vus
同時実行する仮想ユーザーの数を指定します。iterationsまたはdurationオプションと一緒に使用します。
duration
テストの総時間を指定します。
iterations
テストスクリプトを実行する回数を指定します。
export const options = {
vus: 10,
iterations: 20,
};
/\ |‾‾| /‾‾/ /‾‾/
/\ / \ | |/ / / /
/ \/ \ | ( / ‾‾\
/ \ | |\ \ | (‾) |
/ __________ \ |__| \__\ \_____/ .io
execution: local
script: test.js
output: -
scenarios: (100.00%) 1 scenario, 10 max VUs, 10m30s max duration (incl. graceful stop):
* default: 20 iterations shared among 10 VUs (maxDuration: 10m0s, gracefulStop: 30s)
~~~~
~略~
~~~~
iteration_duration.............: avg=1.64s min=1.17s med=1.57s max=2.3s p(90)=2.3s p(95)=2.3s
iterations.....................: 20 5.733751/s
vus............................: 10 min=10 max=10
vus_max........................: 10 min=10 max=10
running (00m03.5s), 00/10 VUs, 20 complete and 0 interrupted iterations
default ✓ [======================================] 10 VUs 00m03.5s/10m0s 20/20 shared iters
出力結果から20回テストスクリプトが実行されたことが分かります。
thresholds
メトリクスに対して閾値を指定します。
下記はメトリクス http_req_failed
と http_req_duration
に閾値を指定しました。
import http from 'k6/http';
export const options = {
thresholds: {
http_req_failed: ['rate<0.01'], // リクエストの失敗率は1%未満
http_req_duration: ['p(95)<200'], // 95%のリクエストで処理時間が200ms未満
},
};
export default function () {
http.get('https://test-api.k6.io/public/crocodiles/1/');
}
✓ http_req_duration..............: avg=151.06ms min=151.06ms med=151.06ms max=151.06ms p(90)=151.06ms p(95)=151.06ms
{ expected_response:true }...: avg=151.06ms min=151.06ms med=151.06ms max=151.06ms p(90)=151.06ms p(95)=151.06ms
✓ http_req_failed................: 0.00% ✓ 0 ✗ 1
http_req_durationに指定した閾値を超えた場合、メトリックの頭に「✗」がつき、最終行にエラーメッセージが表示されます。
まとめ
今回は、負荷テストを手軽にできるツール「k6」を紹介してみました。
JavaScriptでテストスクリプトを記述してすぐに負荷テストができるのが魅力的です。
k6ではさらに、ブラウザの操作を記録してテストスクリプトを出力できたり、OpenAPI(Swagger)の仕様に基づいて記述されたAPI定義ファイルからテストスクリプトを出力することができます。
まだまだ紹介しきれてないこともあるので、またいずれ記事にしたいと思います。
Discussion