Horizontal Pod Autoscalerをミニマムに試してみる

# TL;DR

[Horizontal Pod Autoscaler](https://kubernetes.io/ja/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/)を有効化したDeploymentに簡易的な負荷テストを行い、
Pod数の増減を可視化してみました。

この記事では、負荷テストの手順について解説します。

![Pod数 と 経過秒数.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422592/240664cb-5d64-2cca-4fdf-fbb0cc325bdc.png)

この記事で取り扱うコード類: https://github.com/cacapouh/hpa-example

# 負荷テスト手順

## 1.　環境構築

まずは、Minikubeを用いて、k8s環境を構築します。
Horizontal Pod Autoscalerを動かすために、[metrics-server](https://github.com/kubernetes-sigs/metrics-server)を有効化しています。

```shell
minikube delete --all
minikube start --memory=4096  # メモリのデフォルト値は2GBで気持ちやや少なめなので、4GBで起動
minikube addons enable metrics-server
```

## 2. サンプルアプリケーションを用意

以下は負荷テスト対象のアプリケーションの実装`main.py`およびDockerfileです。

`main.py`では、80ポートでHTTPサービスを提供するようにしています。
また、負荷テストのため故意に処理の重いforループを入れています。

```python:main.py
from bottle import route, run
import math


@route('/')
def index():
    x = 0.0001
    for i in range(0, 1000000):
        x += math.sqrt(i)
    return "OK: {}".format(x)


if __name__ == '__main__':
    run(host='0.0.0.0', port=80)
```

```txt:requirements.txt
bottle
```

```Dockerfile:Dockerfile
FROM python:3.12-slim

WORKDIR /app
COPY requirements.txt /app
RUN pip install -r requirements.txt

COPY main.py /app

ENTRYPOINT python main.py
```

## 3. サンプルアプリケーションをビルド

dockerビルドして、minikube環境にイメージをロードします。

```shell
docker build . -t simple-app:latest
minikube image load simple-app:latest
```

## 4. サンプルアプリのデプロイとHPAの有効化

サンプルアプリは以下のマニフェストファイルを使ってデプロイします。

```yaml:simple-app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: simple-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: simple-app
  template:
    metadata:
      labels:
        app: simple-app
    spec:
      containers:
      - name: simple-app
        image: simple-app:latest
        imagePullPolicy: IfNotPresent
        ports:
          - containerPort: 80
        resources:
          limits:
            cpu: "500m"
          requests:
            cpu: "200m"

---
apiVersion: v1
kind: Service
metadata:
  name: simple-app
spec:
  type: NodePort
  selector:
    app: simple-app
  ports:
    - port: 80
      targetPort: 80
```

以下のコマンドで、アプリケーションをデプロイし、HPAを有効化します。
このコマンドにより、CPU使用率が50%に近づくようにPod数が1~10の範囲で調節されます。

```shell
kubectl apply -f simple-app.yaml
kubectl autoscale deployment simple-app --cpu-percent=50 --min=1 --max=10
```

## 5. 負荷テストの実施

以下のコマンドで、負荷テストとして、
0.01秒毎にwgetで対象アプリにHTTPリクエストを送信します。

```shell
kubectl run -i --tty load-generator --rm --image=busybox --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://simple-app; done"
```

# 負荷テスト結果

以下は、負荷テストの結果です。

![Pod数 と 経過秒数.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422592/3bc22df7-c6b3-c549-8ded-fc655479e1e8.png)

【0~50秒】
アプリケーションをデプロイした直後でして、
負荷テストが開始されていない状況ですので、Pod数は`simple-app.yaml`で指定した通り2つになっています。

【50~650秒】
こちらは負荷テスト中の期間になります。
Pod数が7個まで増えています。

【650秒~】
650秒付近で、負荷テストのwgetコマンドを停止しました。
そのため、CPU使用率が低下し、Pod数も1つまで減少しています。

また、[こちらのリンク](https://github.com/cacapouh/hpa-example/blob/main/result/hpa2.txt
)から、CPU使用率とPod数の遷移の詳細は、下記リンクからチェックすることができます。
`watch 'kubectl get hpa'`コマンドの実行結果になります。

# おわりに

`kubectl autoscale deployment simple-app --cpu-percent=50 --min=1 --max=10`
と...コマンドを1つ実行するだけで、自動スケーリングが実現できました。
非常に簡単でしたね:thumbsup: