🏋️

負荷テストを手軽にできるツール「k6」を試してみた

2023/05/17に公開

はじめに

現在、開発を進めているプロダクトの負荷テストをすることになり、簡単にできる方法がないか探していたところ、「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% ✓ 01  
     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%  ✓ 0254 
     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%  ✓ 0254 
     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_failedhttp_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に指定した閾値を超えた場合、メトリックの頭に「✗」がつき、最終行にエラーメッセージが表示されます。

image

まとめ

今回は、負荷テストを手軽にできるツール「k6」を紹介してみました。
JavaScriptでテストスクリプトを記述してすぐに負荷テストができるのが魅力的です。

k6ではさらに、ブラウザの操作を記録してテストスクリプトを出力できたり、OpenAPI(Swagger)の仕様に基づいて記述されたAPI定義ファイルからテストスクリプトを出力することができます。

まだまだ紹介しきれてないこともあるので、またいずれ記事にしたいと思います。

レスキューナウテックブログ

Discussion