【 Kubernetes 】 Horizontal Pod Autoscaler 入門

# Pod の Autoscaling について
Kubernetes で動いている Pod は基本的に `spec.Replicas` で指定した個数に制限されます。それ以上もそれ以下もありません。なので急激なアクセス増加に対応できずに Pod が `OOM(Out of Memory)` で Kill されてしまったり、逆にアクセスが少ない時間に無駄な Pod を動かすことになったりします。

そこで使うのが `Horizontal Pod Autoscaler` です。 `水平スケール` とも言います。これは水平方向へのスケールイン/アウトなので同じ性能の Pod が増減します。

実際に動きを見てみましょう。

# YAML
今回は `hpa-sample` というネームスペースで作業します。

```yaml:namespace.yml
apiVersion: v1
kind: Namespace
metadata:
  name: hpa-sample
```

私が作成したわざと重い処理を行わせる PHP ベースのイメージを利用します。
`spec.template.spec.containers[].resources` は必須です。
`requests` と `limits` を設定しておきます。

```yaml:deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hpa-deploy
  namespace: hpa-sample
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hpa-deploy
  template:
    metadata:
      labels:
        app: hpa-deploy
    spec:
      containers:
        - name: hpa-deploy
          image: iedred7584/hpa-sample
          resources:
            requests:
              memory: 128Mi
              cpu: 100m
            limits:
              memory: 128Mi
              cpu: 500m
          ports:
            - containerPort: 80
```

Service も作成してアクセスできるようにします。

```yaml:service.yml
apiVersion: v1
kind: Service
metadata:
  name: hpa-svc
  namespace: hpa-sample
spec:
  type: LoadBalancer
  selector:
    app: hpa-deploy
  ports:
    - port: 80
      targetPort: 80
```

次の YAML が今回の肝の `HorizontalPodAutoscaler` です。
対象の Deployment の指定や、 Pod の最小/最大個数の指定、 Pod がどの程度 CPU を使っていたら発火するかの条件を指定します。

```yaml:hpa.yml
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: hpa-hpa
  namespace: hpa-sample
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: hpa-deploy
  minReplicas: 1
  maxReplicas: 10
  targetCPUUtilizationPercentage: 50
```

これらの YAML を食わせます。

```sh
$ kubectl apply -f .
namespace/hpa-sample created
deployment.apps/hpa-deploy created
service/hpa-svc created
horizontalpodautoscaler.autoscaling/hpa-hpa created
```

# 動作確認
まずは平常時の Pod 数を確認します。

```sh
$ kubectl get po -n hpa-sample -o wide
NAME                          READY   STATUS    RESTARTS   AGE   IP          NODE        NOMINATED NODE   READINESS GATES
hpa-deploy-7d5f8bbb54-ds9gl   1/1     Running   0          38s   10.46.0.3   worker005   <none>           <none>
```

`deployment.yml` の `Replicas` を 1 にしたので Pod が1つだけ動いています。
では実際にスケールさせてみましょう。

まずは Service を確認します。

```sh
kubectl get svc -n hpa-sample -o wide
NAME      TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE     SELECTOR
hpa-svc   LoadBalancer   10.106.97.142   10.0.0.201    80:31129/TCP   4m14s   app=hpa-deploy
```

`service.yml` で `type: LoadBalancer` にしたので `EXTERNAL-IP` に書かれている IP アドレスにアクセスします。

```sh
$ curl 10.0.0.201
<br />
<b>Fatal error</b>:  Maximum execution time of 30 seconds exceeded in <b>/var/www/html/index.php</b> on line <b>5</b><br />
```

わざと30秒の制限に到達するようにしているのでエラーが出ます。
高負荷状態を作り出したので自動でスケールされているはずです。確認してみましょう。

```sh
$ kubectl get po -n hpa-sample -o wide
NAME                          READY   STATUS    RESTARTS   AGE     IP            NODE        NOMINATED NODE   READINESS GATES
hpa-deploy-7d5f8bbb54-6fw9w   1/1     Running   0          70s     10.33.128.5   worker014   <none>           <none>
hpa-deploy-7d5f8bbb54-7lp8r   1/1     Running   0          100s    10.36.0.3     worker013   <none>           <none>
hpa-deploy-7d5f8bbb54-9dq25   1/1     Running   0          70s     10.32.0.4     worker010   <none>           <none>
hpa-deploy-7d5f8bbb54-dbqcd   1/1     Running   0          85s     10.40.0.4     worker012   <none>           <none>
hpa-deploy-7d5f8bbb54-ds9gl   1/1     Running   0          8m15s   10.46.0.3     worker005   <none>           <none>
hpa-deploy-7d5f8bbb54-g2wlj   1/1     Running   0          85s     10.38.0.4     worker011   <none>           <none>
hpa-deploy-7d5f8bbb54-gv4kh   1/1     Running   0          85s     10.43.128.4   worker007   <none>           <none>
hpa-deploy-7d5f8bbb54-ml9gv   1/1     Running   0          100s    10.41.128.4   worker003   <none>           <none>
hpa-deploy-7d5f8bbb54-nkhp4   1/1     Running   0          100s    10.34.0.3     worker006   <none>           <none>
hpa-deploy-7d5f8bbb54-tskwj   1/1     Running   0          85s     10.38.128.2   worker004   <none>           <none>
```

`hpa.yml` で書いた最大数まで Pod がスケールアウトされました。

このまま放置していると負荷がない状態が続くため、自動でスケールインされます。

```sh
$ kubectl get po -n hpa-sample -o wide
NAME                          READY   STATUS    RESTARTS   AGE   IP          NODE        NOMINATED NODE   READINESS GATES
hpa-deploy-7d5f8bbb54-ds9gl   1/1     Running   0          13m   10.46.0.3   worker005   <none>           <none>
```

しばらく放置すると最小個数の 1 になりました。

# まとめ
このように簡単に自動スケールさせることができます。これを使うことで Pod が落ちることによる機会損失や、無駄な Pod の管理に悩まされることは減らせるかもしれません。