💬

負荷テストツールのk6を試してみた

2022/07/18に公開

はじめに

一定期間、緩やかなアクセスを繰り返した場合に、サーバの負荷がどのように変化するのか知りたくなり、調べたところk6がよさそうだったので、試してみました。

環境

k6 v0.39.0

k6による負荷テスト

インストール

今回は、Homebrewでインストールします。

$ brew install k6

$ k6 version
k6 v0.39.0 ((devel), go1.18.3, darwin/amd64)

これ以外にも、Linux,Windows,Dockerなど、複数の方法が提供されています。

https://k6.io/docs/getting-started/installation/

負荷テストの実行

公式サイトの Getting Started の手順にそって、負荷テストを実行してみます。

  • テストの実行
script.js
import http from 'k6/http';
import { sleep } from 'k6';

export default function () {
  http.get('https://test.k6.io');
  sleep(1);
}

オプションを指定しない場合は、1並列で、script.jsが1回だけ実行されます。

$ k6 run script.js

          /\      |‾‾| /‾‾/   /‾‾/
     /\  /  \     |  |/  /   /  /
    /  \/    \    |     (   /   ‾‾\
   /          \   |  |\  \ |  ()  |
  / __________ \  |__| \__\ \_____/ .io

  execution: local
     script: script.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)


running (00m01.6s), 0/1 VUs, 1 complete and 0 interrupted iterations
default ✓ [======================================] 1 VUs  00m01.6s/10m0s  1/1 iters, 1 per VU

     data_received..................: 17 kB 10 kB/s
     data_sent......................: 438 B 267 B/s
     http_req_blocked...............: avg=444.31ms min=444.31ms med=444.31ms max=444.31ms p(90)=444.31ms p(95)=444.31ms
     http_req_connecting............: avg=198.15ms min=198.15ms med=198.15ms max=198.15ms p(90)=198.15ms p(95)=198.15ms
     http_req_duration..............: avg=176.7ms  min=176.7ms  med=176.7ms  max=176.7ms  p(90)=176.7ms  p(95)=176.7ms
       { expected_response:true }...: avg=176.7ms  min=176.7ms  med=176.7ms  max=176.7ms  p(90)=176.7ms  p(95)=176.7ms
     http_req_failed................: 0.00% ✓ 01
     http_req_receiving.............: avg=231µs    min=231µs    med=231µs    max=231µs    p(90)=231µs    p(95)=231µs
     http_req_sending...............: avg=6.12ms   min=6.12ms   med=6.12ms   max=6.12ms   p(90)=6.12ms   p(95)=6.12ms
     http_req_tls_handshaking.......: avg=233.21ms min=233.21ms med=233.21ms max=233.21ms p(90)=233.21ms p(95)=233.21ms
     http_req_waiting...............: avg=170.35ms min=170.35ms med=170.35ms max=170.35ms p(90)=170.35ms p(95)=170.35ms
     http_reqs......................: 1     0.610261/s
     iteration_duration.............: avg=1.62s    min=1.62s    med=1.62s    max=1.62s    p(90)=1.62s    p(95)=1.62s
     iterations.....................: 1     0.610261/s
     vus............................: 1     min=1      max=1
     vus_max........................: 1     min=1      max=1

  • オプションの指定

並列数(--vus 3)と期間(--duration 30s)を指定して、テストを実行してみます。

$ k6 run --vus 3 --duration 30s script.js

          /\      |‾‾| /‾‾/   /‾‾/
     /\  /  \     |  |/  /   /  /
    /  \/    \    |     (   /   ‾‾\
   /          \   |  |\  \ |  ()  |
  / __________ \  |__| \__\ \_____/ .io

  execution: local
     script: script.js
     output: -

  scenarios: (100.00%) 1 scenario, 3 max VUs, 1m0s max duration (incl. graceful stop):
           * default: 3 looping VUs for 30s (gracefulStop: 30s)


running (0m31.1s), 0/3 VUs, 74 complete and 0 interrupted iterations
default ✓ [======================================] 3 VUs  30s

     data_received..................: 855 kB 28 kB/s
     data_sent......................: 8.3 kB 266 B/s
     http_req_blocked...............: avg=20.94ms  min=4µs      med=7µs      max=518.48ms p(90)=11µs     p(95)=41.59µs
     http_req_connecting............: avg=10.05ms  min=0s       med=0s       max=248.01ms p(90)=0s       p(95)=0s
     http_req_duration..............: avg=222.86ms min=160.6ms  med=226.32ms max=433.55ms p(90)=229.38ms p(95)=230.02ms
       { expected_response:true }...: avg=222.86ms min=160.6ms  med=226.32ms max=433.55ms p(90)=229.38ms p(95)=230.02ms
     http_req_failed................: 0.00%  ✓ 074
     http_req_receiving.............: avg=8.72ms   min=61µs     med=119µs    max=224.24ms p(90)=248.8µs  p(95)=438.19µs
     http_req_sending...............: avg=44.68µs  min=16µs     med=37.5µs   max=268µs    p(90)=71.2µs   p(95)=97.79µs
     http_req_tls_handshaking.......: avg=9.03ms   min=0s       med=0s       max=224.73ms p(90)=0s       p(95)=0s
     http_req_waiting...............: avg=214.09ms min=160.44ms med=225.81ms max=230.17ms p(90)=228.7ms  p(95)=229.36ms
     http_reqs......................: 74     2.378671/s
     iteration_duration.............: avg=1.24s    min=1.16s    med=1.22s    max=1.71s    p(90)=1.23s    p(95)=1.43s
     iterations.....................: 74     2.378671/s
     vus............................: 1      min=1      max=3
     vus_max........................: 3      min=3      max=3

オプションは、スクリプト内でも指定することもできます。

script.js
import http from 'k6/http';
import { sleep } from 'k6';
export const options = {
  vus: 3,
  duration: '30s',
};
export default function () {
  http.get('http://test.k6.io');
  sleep(1);
}

CLIのオプションを指定せずに実行しても、並列数(--vus 3)、期間(--duration 30s)と指定した場合と同様にテストが実行されます。

$ k6 run script.js

          /\      |‾‾| /‾‾/   /‾‾/
     /\  /  \     |  |/  /   /  /
    /  \/    \    |     (   /   ‾‾\
   /          \   |  |\  \ |  (‾)  |
  / __________ \  |__| \__\ \_____/ .io

  execution: local
     script: script.js
     output: -

  scenarios: (100.00%) 1 scenario, 3 max VUs, 1m0s max duration (incl. graceful stop):
           * default: 3 looping VUs for 30s (gracefulStop: 30s)


running (0m31.1s), 0/3 VUs, 75 complete and 0 interrupted iterations
default ✓ [======================================] 3 VUs  30s

     data_received..................: 866 kB 28 kB/s
     data_sent......................: 8.4 kB 270 B/s
     http_req_blocked...............: avg=21.01ms  min=2µs      med=6µs     max=525.87ms p(90)=13µs     p(95)=42.79µs
     http_req_connecting............: avg=9.79ms   min=0s       med=0s      max=244.83ms p(90)=0s       p(95)=0s
     http_req_duration..............: avg=219.93ms min=163.32ms med=227.6ms max=241.79ms p(90)=228.79ms p(95)=229.8ms
       { expected_response:true }...: avg=219.93ms min=163.32ms med=227.6ms max=241.79ms p(90)=228.79ms p(95)=229.8ms
     http_req_failed................: 0.00%  ✓ 0       ✗ 75
     http_req_receiving.............: avg=124.81µs min=37µs     med=104µs   max=442µs    p(90)=191.2µs  p(95)=247.59µs
     http_req_sending...............: avg=34.85µs  min=8µs      med=31µs    max=99µs     p(90)=61µs     p(95)=73.89µs
     http_req_tls_handshaking.......: avg=10.84ms  min=0s       med=0s      max=273.32ms p(90)=0s       p(95)=0s
     http_req_waiting...............: avg=219.77ms min=163.18ms med=227.4ms max=241.62ms p(90)=228.66ms p(95)=229.7ms
     http_reqs......................: 75     2.41479/s
     iteration_duration.............: avg=1.24s    min=1.16s    med=1.22s   max=1.76s    p(90)=1.23s    p(95)=1.23s
     iterations.....................: 75     2.41479/s
     vus............................: 3      min=3     max=3
     vus_max........................: 3      min=3     max=3
  • レスポンスの検証

リクエストに対するレスポンスの検証も行えます。
レスポンスのステータスコードが200であることを検証する場合は、スクリプトを以下のように記述します。

script.js
import { check } from 'k6';
import http from 'k6/http';

export default function () {
  const res = http.get('http://test.k6.io/');
  check(res, {
    'is status 200': (r) => r.status === 200,
  });
}
$ k6 run script.js

          /\      |‾‾| /‾‾/   /‾‾/
     /\  /  \     |  |/  /   /  /
    /  \/    \    |     (   /   ‾‾\
   /          \   |  |\  \ |  ()  |
  / __________ \  |__| \__\ \_____/ .io

  execution: local
     script: script.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)


running (00m01.7s), 0/1 VUs, 1 complete and 0 interrupted iterations
default ✓ [======================================] 1 VUs  00m01.7s/10m0s  1/1 iters, 1 per VU

     ✓ is status 200

     checks.........................: 100.00% ✓ 10
     data_received..................: 17 kB   9.9 kB/s
     data_sent......................: 438 B   259 B/s
     http_req_blocked...............: avg=495.37ms min=495.37ms med=495.37ms max=495.37ms p(90)=495.37ms p(95)=495.37ms
     http_req_connecting............: avg=277.44ms min=277.44ms med=277.44ms max=277.44ms p(90)=277.44ms p(95)=277.44ms
     http_req_duration..............: avg=193.77ms min=193.77ms med=193.77ms max=193.77ms p(90)=193.77ms p(95)=193.77ms
       { expected_response:true }...: avg=193.77ms min=193.77ms med=193.77ms max=193.77ms p(90)=193.77ms p(95)=193.77ms
     http_req_failed................: 0.00%   ✓ 01
     http_req_receiving.............: avg=234µs    min=234µs    med=234µs    max=234µs    p(90)=234µs    p(95)=234µs
     http_req_sending...............: avg=75µs     min=75µs     med=75µs     max=75µs     p(90)=75µs     p(95)=75µs
     http_req_tls_handshaking.......: avg=216.13ms min=216.13ms med=216.13ms max=216.13ms p(90)=216.13ms p(95)=216.13ms
     http_req_waiting...............: avg=193.46ms min=193.46ms med=193.46ms max=193.46ms p(90)=193.46ms p(95)=193.46ms
     http_reqs......................: 1       0.590737/s
     iteration_duration.............: avg=1.69s    min=1.69s    med=1.69s    max=1.69s    p(90)=1.69s    p(95)=1.69s
     iterations.....................: 1       0.590737/s
     vus............................: 1       min=1      max=1
     vus_max........................: 1       min=1      max=1
  • 並列数の増減

オプションの stages を利用すると、テスト中の並列数も変更できます。
最初の30秒で並列数を5まで増加させ、次の30秒で並列数を3に減少させ、最後の30秒で並列数を0にします。

script.js
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '1m', target: 5 },
    { duration: '30s', target: 3 },
    { duration: '30s', target: 0 },
  ],
};

export default function () {
  const res = http.get('https://httpbin.test.k6.io/');
  check(res, { 'status was 200': (r) => r.status == 200 });
  sleep(1);
}
$ k6 run script.js

          /\      |‾‾| /‾‾/   /‾‾/
     /\  /  \     |  |/  /   /  /
    /  \/    \    |     (   /   ‾‾\
   /          \   |  |\  \ |  ()  |
  / __________ \  |__| \__\ \_____/ .io

  execution: local
     script: script.js
     output: -

  scenarios: (100.00%) 1 scenario, 5 max VUs, 2m30s max duration (incl. graceful stop):
           * default: Up to 5 looping VUs for 2m0s over 3 stages (gracefulRampDown: 30s, gracefulStop: 30s)


running (2m00.9s), 0/5 VUs, 285 complete and 0 interrupted iterations
default ✓ [======================================] 0/5 VUs  2m0s

     ✓ status was 200

     checks.........................: 100.00% ✓ 2850
     data_received..................: 2.8 MB  23 kB/s
     data_sent......................: 32 kB   264 B/s
     http_req_blocked...............: avg=7.38ms   min=3µs      med=7µs      max=446.05ms p(90)=11µs     p(95)=13µs
     http_req_connecting............: avg=3.53ms   min=0s       med=0s       max=233.87ms p(90)=0s       p(95)=0s
     http_req_duration..............: avg=215.56ms min=161.99ms med=227ms    max=488.96ms p(90)=229.88ms p(95)=232.9ms
       { expected_response:true }...: avg=215.56ms min=161.99ms med=227ms    max=488.96ms p(90)=229.88ms p(95)=232.9ms
     http_req_failed................: 0.00%   ✓ 0285
     http_req_receiving.............: avg=130.98µs min=46µs     med=100µs    max=2.92ms   p(90)=172.39µs p(95)=219.4µs
     http_req_sending...............: avg=37.09µs  min=14µs     med=36µs     max=252µs    p(90)=52µs     p(95)=60µs
     http_req_tls_handshaking.......: avg=3.78ms   min=0s       med=0s       max=228.81ms p(90)=0s       p(95)=0s
     http_req_waiting...............: avg=215.39ms min=161.83ms med=226.86ms max=488.87ms p(90)=229.67ms p(95)=232.8ms
     http_reqs......................: 285     2.356448/s
     iteration_duration.............: avg=1.22s    min=1.16s    med=1.22s    max=1.64s    p(90)=1.23s    p(95)=1.24s
     iterations.....................: 285     2.356448/s
     vus............................: 1       min=1      max=5
     vus_max........................: 5       min=5      max=5

CLIでオプションを指定する場合は、以下のようにします。

$ k6 run -s '1m:5' -s '30s:3' -s '30s:0' script.js
  • Basic認証が設定されている場合
$ curl -i https://httpbin.test.k6.io/basic-auth/user/passwd
HTTP/1.1 401 UNAUTHORIZED
Date: Mon, 18 Jul 2022 06:43:20 GMT
Content-Length: 0
Connection: keep-alive
WWW-Authenticate: Basic realm="Fake Realm"
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

$ curl -i --user 'user:passwd' https://httpbin.test.k6.io/basic-auth/user/passwd
HTTP/1.1 200 OK
Date: Mon, 18 Jul 2022 06:43:59 GMT
Content-Type: application/json
Content-Length: 47
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

{
  "authenticated": true,
  "user": "user"
}
basic-auth.js
import encoding from 'k6/encoding';
import http from 'k6/http';
import { check } from 'k6';

const username = 'user';
const password = 'passwd';

export default function () {
  const credentials = `${username}:${password}`;

  const url = `https://${credentials}@httpbin.test.k6.io/basic-auth/${username}/${password}`;

  let res = http.get(url);

  check(res, {
    'status is 200': (r) => r.status === 200,
    'is authenticated': (r) => r.json().authenticated === true,
    'is correct user': (r) => r.json().user === username,
  });
}
$ k6 run basic-auth.js

          /\      |‾‾| /‾‾/   /‾‾/
     /\  /  \     |  |/  /   /  /
    /  \/    \    |     (   /   ‾‾\
   /          \   |  |\  \ |  ()  |
  / __________ \  |__| \__\ \_____/ .io

  execution: local
     script: basic-auth.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)


running (00m00.6s), 0/1 VUs, 1 complete and 0 interrupted iterations
default ✓ [======================================] 1 VUs  00m00.6s/10m0s  1/1 iters, 1 per VU

     ✓ status is 200
     ✓ is authenticated
     ✓ is correct user

     checks.........................: 100.00% ✓ 30
     data_received..................: 5.7 kB  9.7 kB/s
     data_sent......................: 515 B   870 B/s
     http_req_blocked...............: avg=399ms    min=399ms    med=399ms    max=399ms    p(90)=399ms    p(95)=399ms
     http_req_connecting............: avg=207.9ms  min=207.9ms  med=207.9ms  max=207.9ms  p(90)=207.9ms  p(95)=207.9ms
     http_req_duration..............: avg=171.46ms min=171.46ms med=171.46ms max=171.46ms p(90)=171.46ms p(95)=171.46ms
       { expected_response:true }...: avg=171.46ms min=171.46ms med=171.46ms max=171.46ms p(90)=171.46ms p(95)=171.46ms
     http_req_failed................: 0.00%   ✓ 01
     http_req_receiving.............: avg=183µs    min=183µs    med=183µs    max=183µs    p(90)=183µs    p(95)=183µs
     http_req_sending...............: avg=5.39ms   min=5.39ms   med=5.39ms   max=5.39ms   p(90)=5.39ms   p(95)=5.39ms
     http_req_tls_handshaking.......: avg=188.94ms min=188.94ms med=188.94ms max=188.94ms p(90)=188.94ms p(95)=188.94ms
     http_req_waiting...............: avg=165.89ms min=165.89ms med=165.89ms max=165.89ms p(90)=165.89ms p(95)=165.89ms
     http_reqs......................: 1       1.689889/s
     iteration_duration.............: avg=576.97ms min=576.97ms med=576.97ms max=576.97ms p(90)=576.97ms p(95)=576.97ms
     iterations.....................: 1       1.689889/s

まとめ

負荷テストツールのk6について、インストールからテストの実行までを試してみました。
また、テスト中の並列数の増減や、認証が設定されたサイトもテストが行えることが確認できました。
CLIツールが提供されているためテストの実行が容易で、また、テスト自体はJavaScriptで記述できるので、とても使いやすいという印象を持ちました。

参考

GitHubで編集を提案

Discussion