ご無沙汰しています。LDIで開発しているOKです。
パフォーマンスツールの定番というと JMeter
あたりが割と有名で使われている人が多いのかなと思いますが、
設定が大変でちょっとしたテストをしようと思ってもサクッとできない印象があるのですよね(個人の感想です)。
そこで今回紹介するのは、オープンソースの k6 という負荷テストツールです。
k6
の特徴としては、負荷試験のシナリオをJavaScriptで記述することです。
スクリプトコードで書けるので、API実行前に何か処理したり、レスポンスを受けてから何か実施したり、
API①のレスポンスを使って、API②のパラメータにしたりと
多少複雑な事も比較的簡単に実現可能なところが個人的には気に入った点です。
開発環境
本記事は以下の環境で作成しています。
- Mac Book Pro (Intel製チップ)
- macOS Monterey
- Docker version 20.10.17
k6 環境構築
まず k6
の実行環境を作っていきます。
Docker を利用して構築します。こうしておけば、ファイルを配布すれば誰でもサクッと環境構築できるので便利ですね。
とりあえず以下の様なディレクトリ構成とします。
k6Demo/ - docker-compose.yml - src/ - get.js - env/ - staging.yml - production.yml
docker-compose.yml
は次のように記述します。
version: '3' services: k6: image: loadimpact/k6:latest command: run volumes: - ./src:/src - ./output:/output environment: ENV: "Docker環境" HOST: "http://host.docker.internal:8080"
environment
と環境変数を設定しておくと、スクリプトコードでこの値を取得できるので、
ローカル環境、Staging環境、本番環境で値を切り替えるなんて事ができます。
例えば、staging.yml は次の様に記述しておいて、後述する 環境を指定した実行方法
で environment
を上書きして実行する事が可能です。
version: '3' services: k6: environment: ENV: "Staging環境" HOST: "https://hoge.example.com"
k6 のスクリプト作成
それではシナオリのスクリプトを書いていきます。 HTTP GET のAPIに対してリクエストする場合は以下の様に書けます。
import http from 'k6/http'; import { check, sleep, group } from 'k6'; // 1. 初期化 // 2. API実行前の処理 export function setup() { console.log(__ENV.ENV) } // 3. API実行 export default function() { group('GetGroup', getAPI); } export function getAPI() { const res = http.get(__ENV.HOST + `/k6demo/get`, { tags: { name: "GetAPI", } }); const checkStatus = check(res, { 'status was 200': (r) => r.status === 200, }); if (!checkStatus) { return; } const checkBody = check(res, { 'body was OK': (r) => r.json().result === 'GET', }); if (!checkBody) { return; } sleep(1); }
export function setup()
としている所は読んで字のごとくセットアップに用いるのですが、
k6 がテスト実施の最初に1度だけ呼んでくれる関数になるので、ここで何かしら初期化だったりをすると良いでしょう。
負荷テストとして繰り返し実行する処理としては、 export default function()
としている部分になります。
今回は、 GetGroup
というグループ名をつけて、 function getAPI()
を呼んでいるので、こちらがメインの処理になります。
APIのコールは16〜21行目で実装しています。難しいところはないと思いますが、今回 tags
を設定して、APIの呼び出しにタグを設定しています。
タグの利用に関しては、複数のAPIで負荷試験を実施する際の識別に役立つのですが、それはまた別の機会に説明できればと思うので、今回は割愛します。
22〜27行目は、レスポンスのHTTPステータスのチェック、28〜33行目はレスポンスボディのチェックをしています。 APIの仕様によって、ここで呼び出しが成功しているか確認することで、負荷テスト時の成功率をみる事ができるようになります。
k6 実行
それでは負荷テストを実行していきます。 とりあえず一番単純な負荷の掛け方で実行するにはコマンドラインツールで以下のようなコマンドを打ちます。
docker-compose -f docker-compose.yml run --rm k6 run --out csv=/output/result.csv /src/get.js --vus 10 --duration 1m
これは、 get.js
を 10の vus(virtual users (同時接続数)) で 1分間実施している形になります。
また、 csv
で実施結果の詳細を出力します。
負荷の掛け方や、出力の方法は他にも色んなやり方がありますが、今回は割愛します。
実行すると、負荷試験が始まり、実施が終わると結果のサマリをコンソールに出力してくれます。
/\ |‾| /‾/ /‾/ /\ / \ | |/ / / / / \/ \ | ( / ‾‾\ / \ | |\ \ | (‾) | / _____ \ |_| \_\\___/ .io execution: local script: /src/get.js output: csv (/output/result.csv) scenarios: (100.00%) 1 scenario, 10 max VUs, 32s max duration (incl. graceful stop): * default: 10 looping VUs for 2s (gracefulStop: 30s) INFO[0000] Docker環境 source=console running (02.0s), 00/10 VUs, 20 complete and 0 interrupted iterations default ✓ [======================================] 10 VUs 2s █ setup █ GetGroup ✓ status was 200 ✓ body was OK checks.........................: 100.00% ✓ 40 ✗ 0 data_received..................: 2.8 kB 1.4 kB/s data_sent......................: 2.0 kB 1.0 kB/s group_duration.................: avg=1s min=1s med=1s max=1.01s p(90)=1.01s p(95)=1.01s http_req_blocked...............: avg=2.88ms min=1.2µs med=1.84ms max=6.61ms p(90)=6.55ms p(95)=6.6ms http_req_connecting............: avg=788.2µs min=0s med=467µs max=2.18ms p(90)=1.92ms p(95)=2.04ms http_req_duration..............: avg=2.06ms min=878.57µs med=2ms max=3.65ms p(90)=3.09ms p(95)=3.27ms { expected_response:true }...: avg=2.06ms min=878.57µs med=2ms max=3.65ms p(90)=3.09ms p(95)=3.27ms http_req_failed................: 0.00% ✓ 0 ✗ 20 http_req_receiving.............: avg=40.27µs min=8.26µs med=18.01µs max=222.76µs p(90)=98.66µs p(95)=105.54µs http_req_sending...............: avg=197.78µs min=4.74µs med=88.79µs max=784.36µs p(90)=646.94µs p(95)=738.76µs http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s http_req_waiting...............: avg=1.83ms min=837.37µs med=1.77ms max=3.15ms p(90)=2.67ms p(95)=2.78ms http_reqs......................: 20 9.918911/s iteration_duration.............: avg=959.13ms min=79.49µs med=1s max=1.01s p(90)=1.01s p(95)=1.01s iterations.....................: 20 9.918911/s vus............................: 10 min=10 max=10 vus_max........................: 10 min=10 max=10
環境を指定した実行方法
前述しましたが、環境を変更して実施する場合は、 -f
オプションで、環境別の設定ファイルを指定してあげれば切り替えることができます。
docker-compose -f docker-compose.yml -f env/staging.yml run --rm k6 run --out csv=/output/result.csv /src/get.js --vus 10 --duration 1m
結果の見方
サマリ
色々ありますが、 checks
と http_req_duration
を見るのが良いかと思います。
checks
はリクエストの成功率、成功数、エラー数を出しています。
成功かどうかの判定は、前述のソースにある 22〜31行目で実装していた check
処理によって判定されます。
続いて、http_req_duration
は、APIにリクエストを投げてから、レスポンスが返ってくるまでの応答速度を出しているので、
これでAPIの性能が測れます。
その他の項目も知りたい場合は こちら で確認できます。
詳細
output ディレクトリに出力した result.csv を Excelで開いてみると次様に出力されているのがわかります。
一見何だコレと思うかもしれないですが落ち着いて見ていくと、1回1回のリクエストに対して、サマリで出していた結果を出力してくれていることがわかります。 5〜15行目は1回目のリクエストの結果、16〜26行目は2回目のリクエストの結果、、、といった形です。
1つ1つのリクエストの詳細な結果が確認できるので、例えば A列の metric_name
を http_req_duration
のみでフィルターし、
C列の metric_value
でグラフ化してあげると、応答時間の推移を見たりと言ったことができます。
あとがき
今回は、 k6
の使い方をザッと見ていった形になるので、この使い方だけだと
JMeterに置き換えるのは難しいのでは?と思われるかもしれません。
ただ、今回紹介したやり方以外にも色んな負荷の掛け方だったり、 実行の仕方やアウトプットの出力方法があるので、 その辺を知ってくるともうJMeterに戻ろうという気が起こらなくなるんじゃないかと思います。(個人の感想です)
そういった所は、また別の機会に紹介できればと思います。 それでは!!