【kubernetes】k8s上のシステムに負荷試験をしてみる！

# Kubernetes × k6 で負荷試験を実施してみた！

負荷試験はシステムのスケーラビリティを確認する上で重要な工程です！
今回は Kubernetes上で「k6」を利用して負荷試験を行い、その手順をまとめてみました！

## 1. 環境構築: ローカルk8sを準備
### クラスターの準備
まずローカル環境でk8sを動かせるよう、
Kind を利用してクラスタを作成します。

```
kind create cluster --name kind-control-plane
```

:::note info
#### オプション情報
[--name] でクラスターの名前を決定可能です！
:::

### 作成したクラスターを確認
作成されたクラスタを確認しましょう。

```
kubectl get nodes
```

こんな感じに出てくるはず・・・！

![スクリーンショット 2025-02-22 15.34.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/945342/08d36f44-66a8-4dc7-b850-e3bf7af01dde.png)


## 2. 負荷試験シナリオを JS で記述・ConfigMap登録
### JSでシナリオを作成
k6 は JavaScript でテストシナリオを記述します。
まず「test-script.js」とでも名付け、JSファイル作成します。

```test-script.js
import http from 'k6/http';
import { check, sleep } from 'k6';

export default function () {
    // 作成する環境にHTTPリクエストを送信
    let res = http.get('http://podA-service.default.svc.cluster.local:8080');
    check(res, { 'status is 200': (r) => r.status === 200 });
    sleep(1);
}
```

### configMapのymlを作成
次に、Kubernetes の ConfigMap を作成してシナリオを管理します！


```config-map.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: k6-script
data:
  test-script.js: |
    import http from 'k6/http';
    import { check, sleep } from 'k6';

    export default function () {
        let res = http.get('http://podA-service.default.svc.cluster.local:8080');
        check(res, { 'status is 200': (r) => r.status === 200 });
        sleep(1);
    }
```

### 適用コマンド!!

```
kubectl apply -f k6-configmap.yml
```

## 3. k6 を実行する Job を作成
Kubernetes の Job を利用して負荷試験を実行する Podを作成します。
k6 を Docker で動作させるため、以下の Job を作成します。

```job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: k6-load-test
spec:
  template:
    spec:
      containers:
        - name: k6
          image: grafana/k6
          args: ["run", "/scripts/test-script.js"]
          volumeMounts:
            - name: script-volume
              mountPath: /scripts
      restartPolicy: Never
      volumes:
        - name: script-volume
          configMap:
            name: k6-script
```

適用コマンド:

```
kubectl apply -f k6-job.yml
```

## 4. Job を実行して負荷試験を開始
Job が作成されたことを確認する:

```
kubectl get pods
```

ログを確認し、負荷試験の進行状況をチェック:

```
kubectl logs -f <k6-load-test-pod名>
```

:::note info
Jobが起動されると、JSファイルを実行するので
負荷試験が始まっています。
:::

### k6の実行結果はこんな感じです↓

```
     execution: local
        script: test.js
        output: -

     scenarios: (100.00%) 1 scenario, 1 max VUs, 1m0s max duration (incl. graceful stop):
              * default: 1 looping VUs for 30s (gracefulStop: 30s)


     data_received..................: 7.6 MB 248 kB/s
     data_sent......................: 17 kB  561 B/s
     http_req_blocked...............: avg=15.11ms  min=10.9ms  med=13.21ms max=66.61ms  p(90)=21.56ms  p(95)=26.04ms
     http_req_connecting............: avg=14.97ms  min=10.79ms med=13.09ms max=66.45ms  p(90)=21.41ms  p(95)=25.9ms
     http_req_duration..............: avg=127.24ms min=84.23ms med=94.38ms max=770.35ms p(90)=198.65ms p(95)=349.85ms
       { expected_response:true }...: avg=127.24ms min=84.23ms med=94.38ms max=770.35ms p(90)=198.65ms p(95)=349.85ms
     http_req_failed................: 0.00%  0 out of 214
     http_req_receiving.............: avg=42.37ms  min=11.32ms med=18.32ms max=603.41ms p(90)=74.67ms  p(95)=223.57ms
     http_req_sending...............: avg=101.75µs min=26µs    med=87.5µs  max=1.59ms   p(90)=120µs    p(95)=146µs
     http_req_tls_handshaking.......: avg=0s       min=0s      med=0s      max=0s       p(90)=0s       p(95)=0s
     http_req_waiting...............: avg=84.77ms  min=72.53ms med=75.65ms max=190.75ms p(90)=111.3ms  p(95)=144.79ms
     http_reqs......................: 214    7.013214/s
     iteration_duration.............: avg=142.54ms min=95.64ms med=109.7ms max=789.1ms  p(90)=218.03ms p(95)=366.01ms
     iterations.....................: 214    7.013214/s
     vus............................: 1      min=1        max=1
     vus_max........................: 1      min=1        max=1


running (0m30.5s), 0/1 VUs, 214 complete and 0 interrupted iterations
default ✓ [======================================] 1 VUs  30s
```

### k6のメトリクスを整理しよう
上記結果のサンプルを出してみましたが、それぞれのメトリクスには
こんな意味があります！

| メトリクス              | 説明 |
|-------------------------|--------------------------------------------|
| `data_received`        | サーバーから受信したデータの合計量 |
| `data_sent`            | クライアントから送信したデータの合計量 |
| `http_req_blocked`     | リクエストがブロックされた時間 |
| `http_req_connecting`  | リクエストが接続を確立するのに要した時間 |
| `http_req_duration`    | リクエストの完了に要した時間（レイテンシ） |
| `http_req_failed`      | リクエストが失敗した割合 |
| `http_req_receiving`   | レスポンスを受信するのに要した時間 |
| `http_req_sending`     | リクエストを送信するのに要した時間 |
| `http_req_tls_handshaking` | TLSハンドシェイクが行われた時間 |
| `http_req_waiting`     | リクエストがサーバーの応答を待機していた時間 |
| `http_reqs`           | リクエストの総数およびリクエストの実行速度（スループット） |
| `iteration_duration`   | イテレーション（1回のテストサイクル）の完了に要した時間 |
| `iterations`          | イテレーションの総数およびイテレーションの実行速度 |
| `vus`                | 同時に実行されている仮想ユーザー数 |
| `vus_max`            | テスト実行中に最大で同時に実行されていた仮想ユーザー数 |


## 5. CPU やメモリの負荷を確認
負荷試験中に top コマンドを利用して CPU 負荷を確認できます。

```
kubectl exec -it [pod名] -- top
```

### topコマンドの見方
topコマンドをk8s上で実施すると、これまた色々な情報が
出てきます。

```
top - 00:56:07 up 27 min,  1 user,  load average: 0.01, 0.04, 0.01
Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s): 26.3 us,  0.3 sy,  0.0 ni, 73.2 id,  0.0 wa,  0.0 hi,  0.0 si,  0.2 st 
MiB Mem :   7810.1 total,   6339.4 free,    795.1 used,    851.3 buff/cache     
MiB Swap:      0.0 total,      0.0 free,      0.0 used.   7015.0 avail Mem 

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                                                                                                                                    
    831 mysql     20   0 1788052 432308  34944 S  50.7   5.4   1:15.31 mysqld   
```

:::note info
#### 上記の簡単な考察
あくまで上の結果はシステムによりますが、上記の場合は・・・
- 3行目の「%Cpu(%)」はCPUの使用率
- 一番下の行では、MySQLがCPUを50%程度使用している
等の情報を読み取ることができます。
:::

少し古いですが、topコマンドの解説記事です！

https://qiita.com/k0kubun/items/7368c323d90f24a00c2f


## まとめ
いかがでしたでしょうか？
k8sを例に出しましたが、負荷試験を行うことで

- ボトルネックの発見に繋がる
- そもそも何がボトルネックか分かる
- どの程度の負荷がかかってるか分かる

など・・・
業務でも役立つことが多い負荷試験の一例になると思うので
参考になれば幸いです！


## 後片付け

podとconfigMapを消しておきましょう。

### podの削除

```
kubectl delete pod [pod名]
```

### configMapの削除

```
kubectl delete cm [ConfigMap名]
```

### ちゃんと削除できてるかな？

```
# pod情報が何も表示されなければOK
kubectl get pods

# configMapが何も表示されなければOK
kubectl get cm
```