
概要
今回は 性能測定用の負荷クライアントである K6 を紹介します。
K6は golang製の負荷テストツールとなります。特徴はシンプルで強力なところです。
K6はインストール、シナリオ記述、そして実行の各局面でサクッと進められるよう、とてもシンプルな仕様となっています。
性能試験でバタバタしている時に、仮に良いツールがあったとしても学習コストをかけていられないですよね?そんなユーザの心の声を汲んでかK6の習得は簡単です。
インストール
インストールの方法は3種類です。どれも簡単な手順で導入が可能です。
- パッケージマネージャ
- シングルバイナリ
- Docker イメージ
K6はCLIのツールなので、Homebrewや apt-get等のパッケージマネージャから落としてくることができます。
$ brew install k6
Mac (Homebrew) の場合これだけです。Linuxの場合は Debian系, RedHat系に応じて インストールすることができます。
また、k6は golang製ですので、シングルバイナリのファイルも GitHub上で提供されています。この場合ファイルを落としてきて実行するだけです。簡単ですね。
シナリオ定義
次にシナリオファイルです。シナリオファイルは JSで用意します。こんな感じ。
[basic-test.js]
import http from "k6/http";
import { check, sleep } from "k6";
export default function() {
http.get("http://test.loadimpact.com");
};
実行
ここまでできたなら、一旦実行してみます。
$ k6 run basic-test.js
/\ |‾‾| /‾‾/ /‾/
/\ / \ | |_/ / / /
/ \/ \ | | / ‾‾\
/ \ | |‾\ \ | (_) |
/ __________ \ |__| \__\ \___/ .io
execution: local
output: -
script: basic-test.js
duration: -, iterations: 1
vus: 1, max: 1
done [==========================================================] 1 / 1
data_received..............: 1.4 kB 3.5 kB/s
data_sent..................: 85 B 217 B/s
http_req_blocked...........: avg=25.6ms min=25.6ms med=25.6ms max=25.6ms p(90)=25.6ms p(95)=25.6ms
http_req_connecting........: avg=260µs min=260µs med=260µs max=260µs p(90)=260µs p(95)=260µs
http_req_duration..........: avg=364.78ms min=364.78ms med=364.78ms max=364.78ms p(90)=364.78ms p(95)=364.78ms
http_req_receiving.........: avg=100µs min=100µs med=100µs max=100µs p(90)=100µs p(95)=100µs
http_req_sending...........: avg=81µs min=81µs med=81µs max=81µs p(90)=81µs p(95)=81µs
http_req_tls_handshaking...: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...........: avg=364.6ms min=364.6ms med=364.6ms max=364.6ms p(90)=364.6ms p(95)=364.6ms
http_reqs..................: 1 2.563731/s
iteration_duration.........: avg=390.64ms min=390.64ms med=390.64ms max=390.64ms p(90)=390.64ms p(95)=390.64ms
iterations.................: 1 2.563731/s
vus........................: 1 min=1 max=1
vus_max....................: 1 min=1 max=1
1リクエストだけ実行するサンプルができました。次に同じスクリプトを利用して2ユーザによる5秒間, 10rpsでのアクセスを試みます。この指定はスクリプト内での固定定義も可能ですし実行時の引数で動的に指定することも可能です。
$ k6 run -u 2 -d 5s --rps 10 basic-test.js
/\ |‾‾| /‾‾/ /‾/
/\ / \ | |_/ / / /
/ \/ \ | | / ‾‾\
/ \ | |‾\ \ | (_) |
/ __________ \ |__| \__\ \___/ .io
execution: local
output: -
script: basic-test.js
duration: 5s, iterations: -
vus: 2, max: 2
done [==========================================================] 5s / 5s
data_received..............: 65 kB 13 kB/s
data_sent..................: 4.0 kB 798 B/s
http_req_blocked...........: avg=734.42µs min=3µs med=5µs max=33.89ms p(90)=6.39µs p(95)=26.49µs
http_req_connecting........: avg=12.93µs min=0s med=0s max=304µs p(90)=0s p(95)=0s
http_req_duration..........: avg=194.04ms min=184.02ms med=186.42ms max=372.34ms p(90)=188.9ms p(95)=192.17ms
http_req_receiving.........: avg=78.31µs min=54µs med=75µs max=123µs p(90)=101.39µs p(95)=108.09µs
http_req_sending...........: avg=23.02µs min=15µs med=19µs max=78µs p(90)=29.79µs p(95)=34.79µs
http_req_tls_handshaking...: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...........: avg=193.94ms min=183.93ms med=186.32ms max=372.2ms p(90)=188.81ms p(95)=192.04ms
http_reqs..................: 47 9.399554/s
iteration_duration.........: avg=210.17ms min=185.31ms med=200ms max=475.1ms p(90)=203.35ms p(95)=211.4ms
iterations.................: 47 9.399554/s
vus........................: 2 min=2 max=2
vus_max....................: 2 min=2 max=2
注意点
注意点として、一定の負荷を超えファイルディスクリプタが枯渇すると実行時にエラーが発生することがあります。
その際は OS上でファイルディスクリプタの最大値をチューニングすると収束します。
結果確認
結果が出力されたら内容を確認してみます。項目数が多いので面くらいますが、ポイントを抑えておけば大丈夫です。
Latencyが気になるなら http_req_duration を、Throughputなら data_received, data_sent を、そもそも全データを処理したかという点なら http_reqsを中心に確認します。
基本メトリクス
| # |
分類 |
要素名 |
内容 |
観点 |
| 1 |
設定 |
execution |
実行環境 (基本は local) |
(設定ログ) |
| 2 |
設定 |
output |
出力先 |
(設定ログ) |
| 3 |
設定 |
script |
シナリオスクリプト |
(設定ログ) |
| 4 |
設定 |
duration |
実行時間 |
(設定ログ) |
| 5 |
設定 |
iterations |
JSの実行回数 |
(設定ログ) |
| 6 |
設定 |
vus |
アクティブ仮想ユーザ数 |
(設定ログ) |
| 7 |
設定 |
max |
最大可能仮想ユーザ数 |
(設定ログ) |
| 8 |
- |
done |
プログレスバー |
- |
| 9 |
結果 |
data_received |
受信データの総量と秒間データ量 |
N/Wが輻輳すると秒間転送率が低下 |
| 10 |
結果 |
data_sent |
送信データの総量と秒間データ量 |
N/Wが輻輳すると秒間転送率が低下 |
| 11 |
結果 |
checks |
check関数がある場合の結果 |
- |
HTTP系メトリクス
| # |
分類 |
要素名 |
内容 |
観点 |
| 12 |
結果 |
http_reqs |
k6が生成した総リクエスト数 |
指定との乖離大は N/W・Server 遅延の可能性 |
| 13 |
結果 |
http_req_blocked |
空きTCPコネクションの待ち時間 |
TCPの接続プールは充分か |
| 14 |
結果 |
http_req_looking_up |
DNSの名前解決に費やした時間 |
キャッシュは適切か |
| 15 |
結果 |
http_req_tls_handshaking |
TLSセッションのハンドシェイクに費やした時間 |
接続におけるTLSの負荷は高いか |
| 16 |
結果 |
http_req_sending |
リモートホストへのデータ送信にかかった時間 |
送信は遅くないか |
| 17 |
結果 |
http_req_waiting |
リモートホストからの応答待ちにかかった時間 (= TTFB) |
サーバ処理は遅くないか |
| 18 |
結果 |
http_req_receiving |
リモートホストからのデータ受信にかかった時間 |
受信は遅くないか |
| 19 |
結果 |
http_req_duration |
リクエスト単体の合計時間 (sending + waiting + receiving) (接続・DNS lookupは含まない) |
全体で遅くないか |
(Option) レスポンスチェック
性能試験の結果に成功条件を盛り込みたい場合 check 関数を利用して実現することができます。
例えば つぎのスクリプトは 2種類のチェックを行っています。
- ステータスコードが 200 (成功) であること
- レスポンスタイムが 3秒 (3000ms) 以内であること
import { check } from "k6";
import http from "k6/http";
export default function() {
let res = http.get("http://test.loadimpact.com/");
check(res, {
"is status 200": (r) => r.status === 200,
"duration < 3000ms": (r) => r.timings.duration < 3000
});
}
この様にして実行すると、実行結果上にチェックの結果も追記されます。
$ k6 run -u 3 -d 3s --rps 3 check.js
/\ |‾‾| /‾‾/ /‾/
/\ / \ | |_/ / / /
/ \/ \ | | / ‾‾\
/ \ | |‾\ \ | (_) |
/ __________ \ |__| \__\ \___/ .io
execution: local
output: -
script: check.js
duration: 3s, iterations: -
vus: 3, max: 3
done [==========================================================] 3s / 3s
✓ is status 200
✓ duration < 3000ms
checks.....................: 100.00% ✓ 18 ✗ 0
data_received..............: 12 kB 4.1 kB/s
data_sent..................: 765 B 254 B/s
(以下略)
この際 ✓ 18 がOKで ✗ 0 がNGを意味します。日本と海外では ✓ の意味が逆だったりするので騙されないでください。
また、上記は全件OKですが、NGがある場合はチェック項目ごとに OK数と NG数が表示されます。
✓ status != 4xx
✓ status != 5xx
✗ duration < 3000ms
↳ 99% — ✓ 297 / ✗ 3
参考
まとめ