kindでHPAを導入してPodをスケールアウトをしてみる

## はじめに

KubernetesのHorizontal Pod Autoscale (以下HPA)は、DeploymentやReplicaSet、StatefulSetなどのPodに対して、CPU使用率などのメトリクスに応じて、Pod数を調整する機能である。
今回はkindにHPAを導入し、Podのスケールアウトをして、その動作確認をしてみる。

## 環境情報

環境情報は以下です。
こちらの環境が予め設定されていることが前提とします。

|Component|Version|
|---|---|
| PC | M1 MacBook Pro |
| OS | macOS 15.4.1 |
| Docker Desktop | 4.41.2 |
| docker | 28.1.1 |
| kind | v0.27.0 |
| kubectl-cli | v1.33.0 |
| helm | v3.17.3 |
| metrics-server chart | 3.12.2 |

## 手順

### kindでKubernetesクラスタを立ち上げる

master nodeが1つ、worker nodeが1つのKubernetesクラスタを作成する。

以下の設定ファイルをkind-sandbox.yamlというファイル名で保存する。
```yaml: kind-sandbox.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: sandbox
nodes:
- role: control-plane
- role: worker
```

上記のyamlファイルを元にKubernetesクラスタを立ち上げる。
```bash
kind create cluster --config kind-sandbox.yaml
```

Kubernetesクラスタが立ち上がったことを確認する。
```bash
❯ k get no                                      
NAME                    STATUS   ROLES           AGE   VERSION
sandbox-control-plane   Ready    control-plane   69s   v1.32.2
sandbox-worker          Ready    <none>          60s   v1.32.2
```

### metrics-serverをインストールする

セットアップ直後のkindにはメトリクスを取得するための仕組みが入っていないため、metrics-serverをインストールします。
今回はhelmでインストールします。

```bash
helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server
```

そのままmetrics-serverをインストールすると、kubeletに接続する際IPアドレスを使用しますが、証明書にはIP SANが含まれていないため検証で失敗し、podが正常に立ち上がらない。
回避するためにTLS証明書を無効化し、ノードの内部IPを優先して接続する。
(ローカルの検証環境なのでこのような方法を取る)
したがって、以下の設定をvalues.yamlに記載する。
```yaml: values.yaml
args:
  - --kubelet-insecure-tls
  - --kubelet-preferred-address-types=InternalIP
```

values.yamlを指定し、metrics-serverをインストールする。
```bash
helm install metrics-server metrics-server/metrics-server --version 3.12.2 -f values.yaml -n kube-system
```

metrics-serverのpodが立ち上がることを確認する。
```bash
❯ k -n kube-system get po | grep metrics-server
metrics-server-88dfd87f4-f8qrq                  1/1     Running   0          7m21s
```

### php-apacheサーバーを作成する

php-apacheサーバーのDeploymentとServiceを作成する。
以下はkubernetes.ioのドキュメントに記載されているサンプルコードを参考にしています。
```yaml: php-apache.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-apache
spec:
  selector:
    matchLabels:
      run: php-apache
  template:
    metadata:
      labels:
        run: php-apache
    spec:
      containers:
      - name: php-apache
        image: registry.k8s.io/hpa-example
        ports:
        - containerPort: 80
        resources:
          limits:
            cpu: 200m
            memory: 256Mi
          requests:
            cpu: 200m
            memory: 256Mi
---
apiVersion: v1
kind: Service
metadata:
  name: php-apache
  labels:
    run: php-apache
spec:
  ports:
  - port: 80
  selector:
    run: php-apache

```

マニフェストをapplyする。

```bash
k apply -f php-apache.yaml
```

DeploymentとServiceが作成されたことを確認する。
```bash
❯ k get deploy                                 
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
php-apache   1/1     1            1           26m
❯ k get svc   
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP   57m
php-apache   ClusterIP   10.96.172.67   <none>        80/TCP    26m
```

### HPAを作成する

以下のマニフェストをhpa.yamlというファイル名で保存する。
```yaml: hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  metrics:
  - resource:
      name: cpu
      target:
        averageUtilization: 50
        type: Utilization
    type: Resource
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
```

上記のマニフェストをapplyする。
```bash
❯ k apply -f hpa.yaml  
```

HPAでcpuのメトリクスが取得できていることを確認する。
```
❯ k get hpa                                    
NAME         REFERENCE               TARGETS       MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache   cpu: 0%/50%   1         10        1          61m
```

### 負荷をかけてスケールアウトするか確認する

php-apacheのpodにwgetで負荷をかけてみる

```bash
k run -i --tty load-generator --rm --image=busybox:1.28 --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"
```

cpu使用率が上がるに従い、replica数が増えていることがわかる。
```bash
❯ kubectl get hpa php-apache --watch
NAME         REFERENCE               TARGETS       MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache   cpu: 0%/50%   1         10        1          66m
php-apache   Deployment/php-apache   cpu: 8%/50%   1         10        1          66m
php-apache   Deployment/php-apache   cpu: 100%/50%   1         10        1          67m
php-apache   Deployment/php-apache   cpu: 100%/50%   1         10        2          67m
php-apache   Deployment/php-apache   cpu: 82%/50%    1         10        2          67m
php-apache   Deployment/php-apache   cpu: 44%/50%    1         10        2          67m
php-apache   Deployment/php-apache   cpu: 56%/50%    1         10        2          68m
php-apache   Deployment/php-apache   cpu: 51%/50%    1         10        3          68m
php-apache   Deployment/php-apache   cpu: 43%/50%    1         10        3          68m
php-apache   Deployment/php-apache   cpu: 35%/50%    1         10        3          68m
php-apache   Deployment/php-apache   cpu: 32%/50%    1         10        3          69m
```

deploymentとpodを確認してもreplica数、pod数が増えている。
```bash
❯ k get deploy php-apache
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
php-apache   3/3     3            3           74m
❯ k get po                             
NAME                          READY   STATUS    RESTARTS   AGE
load-generator                1/1     Running   0          6m7s
php-apache-5657b6bff6-9bjtd   1/1     Running   0          4m19s
php-apache-5657b6bff6-cfssn   1/1     Running   0          67m
php-apache-5657b6bff6-lznfv   1/1     Running   0          5m19s
```

## 最後に

kindでHPAを導入して、cpuの負荷に応じてPodがスケールアウトすることを確認できた。

## 参考情報

* https://kubernetes.io/ja/docs/tasks/run-application/horizontal-pod-autoscale/
* https://kubernetes.io/ja/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/
* https://artifacthub.io/packages/helm/metrics-server/metrics-server
* https://github.com/kubernetes-sigs/metrics-server/issues/196