K6のシナリオを使ってみる
はじめに
K6では負荷試験のシナリオを設定することが可能です。このブログではその機能を簡単に試してみてざっくりとした使い方を把握したいと思います。 このブログではK6をDockerを用いて起動します。また、テスト対象のアプリは以前のK6について書いたブログで利用したものを利用します。helloの文字列を返すだけのAPIです。
やってみる
環境
$ lsb_release -a LSB Version: core-11.1.0ubuntu2-noarch:security-11.1.0ubuntu2-noarch Distributor ID: Ubuntu Description: Ubuntu 20.04.1 LTS Release: 20.04 Codename: focal $ uname -srvmpio Linux 5.4.0-58-generic #64-Ubuntu SMP Wed Dec 9 08:16:25 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux $ docker version Client: Docker Engine - Community Version: 19.03.12 API version: 1.40 Go version: go1.13.10 Git commit: 48a66213fe Built: Mon Jun 22 15:45:36 2020 OS/Arch: linux/amd64 Experimental: false Server: Docker Engine - Community Engine: Version: 19.03.12 API version: 1.40 (minimum version 1.12) Go version: go1.13.10 Git commit: 48a66213fe Built: Mon Jun 22 15:44:07 2020 OS/Arch: linux/amd64 Experimental: true containerd: Version: 1.2.13 GitCommit: 7ad184331fa3e55e52b890ea95e65ba581ae3429 runc: Version: 1.0.0-rc10 GitCommit: dc9208a3303feef5b3839f4323d9beb36df0a9dd docker-init: Version: 0.18.0 GitCommit: fec3683
シナリオで何ができるか
ざっくり言うとK6のシナリオではVirtual Users(VUs)、イテレーション、スケジュールの詳細な設定を行なうことができます。
シナリオを使うメリットは主に以下のようなものがあります。
- 同じスクリプトに対して複数のシナリオを設定することができる。それぞれは独立したJavaScriptの関数として実行されることによってテストをより整理しやすくフレキシブルに作成することができる。
- それぞれのシナリオがVUや、イテレーション、スケジューリングパターンを独立して利用することができる(この機能はExecutorsによって実装される)。この機能を活用することによってより高度な現実に近いリクエストを送ることができる
- それぞれのシナリオは直列でも並列でも実行可能で、また、それらを混ぜたような実行も可能である
- シナリオごとに違った環境変数やメトリクスのタグをりようすることができる
Executorsって何ぞ?
ExecutorsはK6のエンジンのワークホースです。それぞれのワークホースがVUsをスケジューリングし、それぞれが別で反復実行されます。
Executorsには以下のようなものがあります。
Executor名 | 値 | 説明 |
---|---|---|
Shared iterations | shared-iterations | 固定値分だけ反復実行するExecutor。この固定値は複数のVUsの間でシェアされる |
Per VU iterations | per-vu-iterations | 固定値の値の回数それぞれのVUsがスクリプトを反復実行するExecutor。この固定値は複数VUs間でシェアされない。 |
Constant VUs | constant-vus | 設定された時間内で、できるだけ反復を実行するVUsの固定値 |
Ramping VUs | ramping-vus | 設定された時間内で、できるだけ反復を実行するVUsの数の変数。開始時点でのVUs数や終了時点のVUs数等を設定できる。グローバルのstages と同等のことができる |
Constant arrival rate | constant-arrival-rate | 固定値の数のイテレーションを設定された時間内で実行するExecutor。VUsの数を変動させることでイテレーションレートを保証しようとする |
Ramping Arrival Rate | ramping-arrival-rate | 区切られた時間の中で、変動するイテレーション回数実行するExecutor。Ramping VUsに似ているが、イテレーションのためのもので、VUsの数は動的に変動する |
Externally Controlled | externally-controlled | externally-controlled|K6のREST APIやCLIを通して、実行時に設定を変更することができるExecutor |
すべてのシナリオで共通の一般的な設定項目
K6のシナリオではExecutor固有の設定項目とは別に、共有の設定項目がいくつかあります。
その設定項目を以下にまとめます。
値 | 型 | 説明 | デフォルト値 |
---|---|---|---|
executor* | string | ユニークなExecutorの名前 | - |
startTime | string | シナリオの実行が開始されるべき時間 | 0s |
gracefulStop | string | イテレーションが強制的に終了される前の待ち時間 | 30s |
exec | string | 実行されるJSの関数名 | defualt |
env | object | シナリオで利用される環境変数 | {} |
tags | object | シナリオのタグ | {} |
実際にシナリオを書いていく
基本的な書き方
シナリオの基本的な書き方は以下のようになります。
export let options = { scenarios: { example_scenario: { // 利用するExecutorの名前 executor: 'shared-iterations', // すべてのシナリオで共通の一般的な設定項目 startTime: '10s', gracefulStop: '5s', env: { EXAMPLEVAR: 'testing' }, tags: { example_tag: 'testing' }, // エクセキューターごとの設定項目 vus: 10, iterations: 200, maxDuration: '10s', }, //二個目のExecutor another_scenario: { ... } } }
Executorを設定する際はscenarios
にobjectを複数記述することが可能なようです。それぞれのシナリオのKey(example_scenario
、another_scenario
) などは任意の値を利用することが可能ですが、scenariosセクションの中でユニークな値である必要があります。
ユーザが上昇するシナリオを書く
シナリオについてある程度まとめたところで実際に一つ書いてみたいと思います。
お題は以下のように
- 開始10秒はリクエストを送らない
- 10秒経過あとは時間経過とともにユーザが上昇する
- 開始10秒で一秒間ウェイトを置いてリクエストを送るVUsが10から20まで段階的に上昇
- 開始20秒から30秒でVUsが20から60まで段階的に上昇
- 開始30秒間から10秒かけて段階的にVUsが減少
また、スクリプトは以下のものを利用します。
import http from 'k6/http'; import { sleep } from 'k6'; export default function () { http.get('http://{$APP_IP}:8080/hello'); sleep(1); }
それでは早速書いていきたいと思います。
今回利用するExecutorは Ramping VUs
です。
お題のストーリーを記述すると以下のようになります。
import http from 'k6/http'; import { sleep } from 'k6'; export let options = { scenarios: { ramping_up_scenario: { executor: 'ramping-vus', startVUs:10, stages: [ {duration: '10s', target: 20}, {duration: '10s', target: 60}, {duration: '10s', target: 0}, ] } } }; export default function () { http.get('http://192.168.10.8:8080/hello'); sleep(1); }
上記の設定でスクリプトを実行すると以下のようになります。
$ docker run --rm --network k6net -i loadimpact/k6 run - < k6script.js /\ |‾‾| /‾‾/ /‾‾/ /\ / \ | |/ / / / / \/ \ | ( / ‾‾\ / \ | |\ \ | (‾) | / __________ \ |__| \__\ \_____/ .io execution: local script: - output: - scenarios: (100.00%) 1 scenario, 60 max VUs, 1m0s max duration (incl. graceful stop): * ramping_up_scenario: Up to 60 looping VUs for 30s over 3 stages (gracefulRampDown: 30s, gracefulStop: 30s) running (0m00.8s), 10/60 VUs, 0 complete and 0 interrupted iterations ramping_up_scenario [ 3% ] 10/60 VUs 00.8s/30.0s running (0m01.8s), 11/60 VUs, 10 complete and 0 interrupted iterations ramping_up_scenario [ 6% ] 11/60 VUs 01.8s/30.0s running (0m02.8s), 12/60 VUs, 21 complete and 0 interrupted iterations ramping_up_scenario [ 9% ] 12/60 VUs 02.8s/30.0s (中略) running (0m08.8s), 18/60 VUs, 108 complete and 0 interrupted iterations ramping_up_scenario [ 29% ] 18/60 VUs 08.8s/30.0s running (0m09.8s), 19/60 VUs, 126 complete and 0 interrupted iterations ramping_up_scenario [ 33% ] 19/60 VUs 09.8s/30.0s running (0m10.8s), 23/60 VUs, 145 complete and 0 interrupted iterations ramping_up_scenario [ 36% ] 23/60 VUs 10.8s/30.0s (中略) running (0m17.8s), 51/60 VUs, 389 complete and 0 interrupted iterations ramping_up_scenario [ 59% ] 51/60 VUs 17.8s/30.0s running (0m18.8s), 55/60 VUs, 440 complete and 0 interrupted iterations ramping_up_scenario [ 63% ] 55/60 VUs 18.8s/30.0s running (0m19.8s), 59/60 VUs, 494 complete and 0 interrupted iterations ramping_up_scenario [ 66% ] 59/60 VUs 19.8s/30.0s running (0m20.8s), 58/60 VUs, 553 complete and 0 interrupted iterations ramping_up_scenario [ 69% ] 58/60 VUs 20.8s/30.0s (中略) running (0m28.8s), 12/60 VUs, 853 complete and 0 interrupted iterations ramping_up_scenario [ 96% ] 12/60 VUs 28.8s/30.0s running (0m29.8s), 06/60 VUs, 865 complete and 0 interrupted iterations ramping_up_scenario [ 99% ] 06/60 VUs 29.8s/30.0s running (0m30.1s), 00/60 VUs, 871 complete and 0 interrupted iterations ramping_up_scenario ✓ [ 100% ] 00/60 VUs 30s data_received..............: 103 kB 3.4 kB/s data_sent..................: 77 kB 2.5 kB/s http_req_blocked...........: avg=52.36µs min=1.48µs med=8.59µs max=12.99ms p(90)=14.6µs p(95)=267.27µs http_req_connecting........: avg=37.05µs min=0s med=0s max=12.96ms p(90)=0s p(95)=222.62µs http_req_duration..........: avg=1.49ms min=257.57µs med=1.37ms max=11.37ms p(90)=2.59ms p(95)=3.11ms http_req_receiving.........: avg=87.02µs min=11.56µs med=83.98µs max=654.79µs p(90)=144.41µs p(95)=163.26µs http_req_sending...........: avg=41.95µs min=6.09µs med=36.36µs max=719.74µs p(90)=67.04µs p(95)=106.4µs http_req_tls_handshaking...: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s http_req_waiting...........: avg=1.36ms min=229.33µs med=1.22ms max=11.21ms p(90)=2.42ms p(95)=2.95ms http_reqs..................: 871 28.917444/s iteration_duration.........: avg=1s min=1s med=1s max=1.01s p(90)=1s p(95)=1s iterations.................: 871 28.917444/s vus........................: 6 min=6 max=59 vus_max....................: 60 min=60 max=60
最初に注目するのは以下の出力です。
scenarios: (100.00%) 1 scenario, 60 max VUs, 1m0s max duration (incl. graceful stop): * ramping_up_scenario: Up to 60 looping VUs for 30s over 3 stages (gracefulRampDown: 30s, gracefulStop: 30s)
シナリオが読み込まれ、そのマックスVUsが60であることがわかります。
また、3つのステージに分けてVUsが上昇していくことも出力されています。実際に最後の10秒は減少ですがK6の出力ではそこは読み取れないみたいですね。
その後各リクエストの結果を読み込んでみると、最初の10秒程度でVUsがユーザが20まで上昇し、次の10秒で20から60まで上昇して(増加数が20以降から変わってます)そして、最後の10秒でユーザが0まで減少しています。
お題はクリアできたみたいですね。