k6という負荷テストツールに少し触れてみた

# はじめに
k6という負荷テストツールを最近よく見かけます。今後、利用する機会がありそうなので触れてみたいと思います。

# k6とは？
[Grafana Labs](https://grafana.com/) が公開しているオープンソースの負荷テストツールです。
元々は [Load Impact](https://loadimpact.com/) という名称で、2020年に変更されたみたいです。
また、[Cloud版](https://k6.io/cloud/)の有料サービスもあります。

https://k6.io/

# インストール for MacOS
今回は MacOS でインストールします。
MacOS の場合は [Homebrew](https://brew.sh/index_ja) で簡単にインストールできます。

```zsh:インストール
% brew install k6
Running `brew update --auto-update`...
==> Auto-updated Homebrew!
Updated 2 taps (kayac/tap and homebrew/core).
==> New Formulae
adamstark-audiofile   dumpling              gold                  leapp-cli             livekit               nb                    qbe                   tuc
astro                 evernote-backup       hwatch                levant                llvm@13               phrase-cli            req                   vectorscan
aztfy                 fastnetmon            jaq                   lexicon               markdown-toc          poac                  swtpm
czg                   flock                 kt-connect            libpython-tabulate    mcap                  podman-compose        trzsz-go

You have 38 outdated formulae installed.
You can upgrade them with brew upgrade
or list them with brew outdated.

==> Downloading https://ghcr.io/v2/homebrew/core/k6/manifests/0.38.3
######################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/k6/blobs/sha256:feb9c885fe5ea6b50578f9c608b23777e48412b9961f8630eb685f1dde850107
==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sha256:feb9c885fe5ea6b50578f9c608b23777e48412b9961f8630eb685f1dde850107?se=2022-06-29T03%3A00%3A00Z&
######################################################################## 100.0%
==> Pouring k6--0.38.3.arm64_monterey.bottle.tar.gz
🍺  /opt/homebrew/Cellar/k6/0.38.3: 5 files, 26.9MB
==> Running `brew cleanup k6`...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
```
その他のインストール方法について以下のリンクでご確認ください。

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

利用可能なコマンドについては `k6` コマンドで確認できます。

```zsh:k6
% k6

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

Usage:
  k6 [command]

Available Commands:
  archive     Create an archive
  cloud       Run a test on the cloud
  completion  Generate the autocompletion script for the specified shell
  convert     Convert a HAR file to a k6 script
  help        Help about any command
  inspect     Inspect a script or archive
  login       Authenticate with a service
  pause       Pause a running test
  resume      Resume a paused test
  run         Start a load test
  scale       Scale a running test
  stats       Show test metrics
  status      Show test status
  version     Show application version

Flags:
  -a, --address string      address for the REST API server (default "localhost:6565")
  -c, --config string       JSON config file (default "/Users/kohei/Library/Application Support/loadimpact/k6/config.json")
  -h, --help                help for k6
      --log-format string   log output format
      --log-output string   change the output for k6 logs, possible values are stderr,stdout,none,loki[=host:port],file[=./path.fileformat] (default "stderr")
      --no-color            disable colored output
  -q, --quiet               disable progress updates
  -v, --verbose             enable verbose logging

Use "k6 [command] --help" for more information about a command.
```


# 準備
それでは簡単なスクリプトを使って負荷テストを行なってみます。
`script.js` というファイルを作成し、以下のコードを記載します。
```js:script.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` へアクセスした後、 [sleep](https://k6.io/docs/javascript-api/k6/sleep/) を1秒入れるプログラムです。
k6 では https://test.k6.io がテストページとして用意されているので今回はそれを利用したいと思います。

# 実行
実行するとレポートが表示されます。
```zsh:run
% 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 266 B/s
     http_req_blocked...............: avg=468.61ms min=468.61ms med=468.61ms max=468.61ms p(90)=468.61ms p(95)=468.61ms
     http_req_connecting............: avg=175.19ms min=175.19ms med=175.19ms max=175.19ms p(90)=175.19ms p(95)=175.19ms
     http_req_duration..............: avg=177.18ms min=177.18ms med=177.18ms max=177.18ms p(90)=177.18ms p(95)=177.18ms
       { expected_response:true }...: avg=177.18ms min=177.18ms med=177.18ms max=177.18ms p(90)=177.18ms p(95)=177.18ms
     http_req_failed................: 0.00% ✓ 0        ✗ 1  
     http_req_receiving.............: avg=412µs    min=412µs    med=412µs    max=412µs    p(90)=412µs    p(95)=412µs   
     http_req_sending...............: avg=51µs     min=51µs     med=51µs     max=51µs     p(90)=51µs     p(95)=51µs    
     http_req_tls_handshaking.......: avg=194.15ms min=194.15ms med=194.15ms max=194.15ms p(90)=194.15ms p(95)=194.15ms
     http_req_waiting...............: avg=176.72ms min=176.72ms med=176.72ms max=176.72ms p(90)=176.72ms p(95)=176.72ms
     http_reqs......................: 1     0.606633/s
     iteration_duration.............: avg=1.64s    min=1.64s    med=1.64s    max=1.64s    p(90)=1.64s    p(95)=1.64s   
     iterations.....................: 1     0.606633/s
     vus............................: 1     min=1      max=1
     vus_max........................: 1     min=1      max=1
```
レポートに表示されているメトリックの内容はこんな感じです。
|メトリック名                 |内容                                |
|---                      |---                                |
|data_received            |受信データ量（左：Total, 右：/秒）         |
|data_sent                |送信データ量（左：Total, 右：/秒）         |
|http_req_blocked         |TCP接続の待ち時間（平均,）              |
|http_req_connecting      |TCP接続にかかった時間(平均,)           |
|http_req_duration        |リクエストにかかった総時間<br> （http_req_sending + http_req_waiting + http_req_receiving） |
|expected_response:true   |成功したリクエストのみの総時間            |
|http_req_failed          |リクエストが失敗した割合                 |
|http_req_receiving       |リモートホストからのリクエスト応答時間        |
|http_req_sending         |リモートホストへリクエストを送るのにかかった時間|
|http_req_tls_handshaking |TLSセッションのハンドシェイクにかかった時間   |
|http_req_waiting         |リクエストしてから応答があるまでの待機時間（“time to first byte”, or “TTFB”）|
|http_reqs                |k6が生成したリクエスト数（総数, /秒）       |
|iteration_duration|1回のJS実行にかかった時間(準備と終了処理も含む)    |
|iterations               |VUがJSを実行した合計回数               |
|vus                      |アクティブ仮想ユーザー数                 |
|vus_max                  |最大アクティブ仮想ユーザー数              |

とりあえず今回はここまで。

# おわりに
今回は何となくk6がどんな感じかを掴むために実行してみました。
導入も操作も手軽に行えるので、思った以上に取っ付きやすいかなというのが最初の感想です。
まだ、javascriptでのシナリオ作成やコマンドオプションについてなど、いろいろと触れていきたいと思います。