K6を利用した負荷テストの実行

## 初めに

### K6とは

goで作られた負荷テストツール。
ただし、ユーザ側のテストシナリオはjavascriptを使用する。

https://k6.io/


### K6の利点

- goを利用しているので、少ないリソースでより多くの負荷をかけられる
- dockerイメージが用意されており、テスト実行だけならjavascript1つ用意するだけで可能
  - gitlabなどのCIとの統合も容易

## 環境構築

- 実行環境の準備
- influxdbの構築(データストア)
- grafanaの構築(influxデータの読み取り)
- テストコード作成

実行環境(gitlab)および、grafana環境の作成は省略。
(今回は、gitlabのgrafanaを流用)

### influxDBの構築

※influxv2には未対応なので、バージョンは1.8にすること。

```yaml:docker-compose.yml
version: '3.8'

services:
  influxdb:
    image: influxdb:1.8
    ports:
      - "8086:8086"
    environment:
      - INFLUXDB_DB=k6
```

### grafanaダッシュボードの作成

Data SourceにInfluxを追加する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/161516/495d59fc-3e54-cb78-87f3-050a112a5852.png)


ダッシュボード2587(k6 Load Testing Results Dashboard)を下記の通りimportする。
https://grafana.com/grafana/dashboards/2587

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/161516/e98fe173-8282-ac3b-e4a5-314bd8730e60.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/161516/cb62c9c7-e903-418c-3928-625a21ff7fe2.png)


## サンプルテストコード

### テストスクリプト

```javascript:performance-test.js
import http from 'k6/http';
import { check } from 'k6';
import {htmlReport} from "https://raw.githubusercontent.com/benc-uk/k6-reporter/main/dist/bundle.js";
import {jUnit, textSummary} from "https://jslib.k6.io/k6-summary/0.0.1/index.js";

const puturl = 'http://ci-service-counter:9080/counter/increment';
const geturl = 'http://ci-service-counter:9080/counter';

export const options = {
  stages: [
    { target: 10, duration: '5m' },
  ],
};

export default function () {
    const headers = { 'Content-Type': 'application/json' };
    const putJsonPayLoad = {"value": 1};

    http.put(puturl, JSON.stringify(putJsonPayLoad), { headers: headers });
    let countResponse = http.get(geturl);

    check(countResponse, { "status is 200": (r) => r.status === 200 });
}

// レポート出力設定
export function handleSummary(data) {
    console.log('Preparing the end-of-test summary...');

    return {
        stdout: textSummary(data, { indent: " ", enableColors: true }),
        "summary.html": htmlReport(data),
        'summary.xml': jUnit(data), // but also transform it and save it as a JUnit XML...
        'summary.json': JSON.stringify(data), // and a JSON with all the details...
    }
}

```

### CI定義

```yaml:gitlab-ci.yaml
nativeit-k6:
  stage: it
  when: manual
  tags:
    - docker
  image:
    name: loadimpact/k6:latest
    entrypoint: ['']
  services:
    - name: ${CI_REGISTRY}/${CI_PROJECT_PATH}:1.2
      alias: ci-service-counter
  script:
    - echo "executing local k6 in k6 container..."
    - k6 run performance-test.js --out influxdb=http://192.**.**.**:8086/k6
  artifacts:
    paths:
      - summary.html
      - summary.xml
      - summary.json
    reports:
      junit: summary.xml
```

## テスト出力

### grafana出力

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/161516/5e5652c5-2f4e-9f6d-83d1-660048739b2b.png)


### k6テストレポート

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/161516/1ce65c54-e0ba-b7c0-c3f4-83d80f88b042.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/161516/6a43cbc5-727b-30a6-3a39-4f6fd896ea61.png)

### ログ出力

```text:log
     ✓ status is 200
     checks.........................: 100.00% ✓ 107788     ✗ 0     
     data_received..................: 33 MB   110 kB/s
     data_sent......................: 32 MB   107 kB/s
     http_req_blocked...............: avg=8.82µs  min=800ns  med=1.5µs   max=1.47s    p(90)=2.5µs   p(95)=3.1µs  
     http_req_connecting............: avg=182ns   min=0s     med=0s      max=4.91ms   p(90)=0s      p(95)=0s     
     http_req_duration..............: avg=6.87ms  min=3.47ms med=6.15ms  max=306.01ms p(90)=9.59ms  p(95)=11.52ms
       { expected_response:true }...: avg=6.87ms  min=3.47ms med=6.15ms  max=306.01ms p(90)=9.59ms  p(95)=11.52ms
     http_req_failed................: 0.00%   ✓ 0          ✗ 215576
     http_req_receiving.............: avg=46.72µs min=9.1µs  med=47.1µs  max=6.29ms   p(90)=69.9µs  p(95)=82.9µs 
     http_req_sending...............: avg=11.07µs min=4.2µs  med=8.3µs   max=7.43ms   p(90)=21.1µs  p(95)=28.6µs 
     http_req_tls_handshaking.......: avg=0s      min=0s     med=0s      max=0s       p(90)=0s      p(95)=0s     
     http_req_waiting...............: avg=6.81ms  min=3.42ms med=6.09ms  max=305.94ms p(90)=9.52ms  p(95)=11.45ms
     http_reqs......................: 215576  718.541797/s
     iteration_duration.............: avg=13.9ms  min=7.63ms med=12.69ms max=1.48s    p(90)=18.54ms p(95)=21.72ms
     iterations.....................: 107788  359.270898/s
     vus............................: 9       min=1        max=9   
     vus_max........................: 10      min=10       max=10  

```