IKS CA + HPA でCPU使用率反応型のオートスケールの検証

**HPA(Horizontal Pod Autoscaler)** は、ポットのCPUの使用率を監視しながら、ポッドのレプリカ数を増減するものです。一方、 **CA(Cluster Autoscaler)** は、リソース要求に対してノードの資源が不足している状態を解消するために、ノードを追加します。

この二つの組み合わせで、ワークロードすなわち、CPU使用率に応じたノード増減について、**IKS(IBM Cloud Kubernetes Service)** で検証してみます。


## オートスケール環境の設定

* CA環境の設定は、[IKS クラスタ・オートスケーラーの検証](https://qiita.com/MahoTakara/items/088b4b9014074b99aca8)を参照してください。
* HPAの設定は、コントローラーに対して、CPU使用率に応じてポッド数を変更する方式になりますから、デプロイメントやステートフルセットなど、コントローラーをデプロイした後に有効化します。

コンテナをデプロイして、HPAを有効化するマニフェストは、GitHubの https://github.com/takara9/container-workload/blob/master/deploy-hpa.yml においてあります。そのなかで、HPA部分を抜き出したのが以下です。　この中で、「scaleTargetRef.name」デプロイメントのオブジェクト名を記述してオートスケール対象とします。その他の項目は、読んで解ると思います。

~~~
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: workload
  namespace: default
spec:
  maxReplicas: 20
  minReplicas: 1
  scaleTargetRef:
    apiVersion: extensions/v1beta1
    kind: Deployment
    name: workload
  targetCPUUtilizationPercentage: 50
~~~


## 初期状態

マニフェストを適用した直後の状態は、ノード数が２です。

~~~console
$ kubectl get node
NAME            STATUS    ROLES     AGE       VERSION
10.74.139.152   Ready     <none>    7d5h      v1.12.5+IKS
10.74.139.154   Ready     <none>    5d18h     v1.12.5+IKS
~~~

直後では、このターミナル出力の一番したの行で、メトリックスサーバーによって、CPUの使用率が算出されていませんから、HPAは働きません。したがって、ポッド数は、マニフェストに記述された５個のままです。

~~~console
$ kubectl get all
NAME                            READY     STATUS    RESTARTS   AGE
pod/workload-7fb9688459-jnwdv   1/1       Running   0          8s
pod/workload-7fb9688459-qr85m   1/1       Running   0          8s
pod/workload-7fb9688459-rjb6p   1/1       Running   0          8s
pod/workload-7fb9688459-rx4tf   1/1       Running   0          8s
pod/workload-7fb9688459-v689s   1/1       Running   0          8s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   172.21.0.1   <none>        443/TCP   7d5h

NAME                       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/workload   5         5         5            5           8s

NAME                                  DESIRED   CURRENT   READY     AGE
replicaset.apps/workload-7fb9688459   5         5         5         9s

NAME                                           REFERENCE             TARGETS         MINPODS   MAXPODS   REPLICAS   AGE
horizontalpodautoscaler.autoscaling/workload   Deployment/workload   <unknown>/50%   1         20        0          8s
~~~

## ワークロード実行

HPAは、CPU使用率のターゲット ５０％ になるように、ポッドのレプリカ数を徐々に増やすように動作します。ここでは、無限ループのコンテナ [maho/testwkld](https://cloud.docker.com/u/maho/repository/docker/maho/testwkld)を実行していますから、一気に最大数までレプリカ数を上げることになります。

次の実行状態では、まだ、ノードが追加されていませんから、２台のノードで対応できる７つのポッドが実行している状態です。７つの理由は、[IKS クラスタ・オートスケーラーの検証](https://qiita.com/MahoTakara/items/088b4b9014074b99aca8) に書いてあります。

このように、HPAによって、ポッド数を増やすことで、ポッド一つあたりのCPU使用率を下げるように、そして、コンテナのスペックによりリソースを占有して能力を増強するように動作します。しかし、ノードのリソースが足りなくなると、ポッドはデプロイされず、ペンディング状態となります。　ここでは、HPAは、19個のポッドが必要と判断して、７個のポッドが実行状態となりましたが、残りの１２個は、ペンディング状態になっています。そのため、これからの CA が働き始めます。

~~~console
$ kubectl get all
NAME                            READY     STATUS    RESTARTS   AGE
pod/workload-7fb9688459-6f892   0/1       Pending   0          78s
pod/workload-7fb9688459-7wdn8   0/1       Pending   0          77s
pod/workload-7fb9688459-c64p9   1/1       Running   0          93s
pod/workload-7fb9688459-d694x   0/1       Pending   0          93s
pod/workload-7fb9688459-dd4qt   0/1       Pending   0          32s
pod/workload-7fb9688459-dhgmm   0/1       Pending   0          93s
pod/workload-7fb9688459-j2v4l   0/1       Pending   0          78s
pod/workload-7fb9688459-j5mvw   0/1       Pending   0          32s
pod/workload-7fb9688459-jnwdv   1/1       Running   0          2m34s
pod/workload-7fb9688459-k25cc   0/1       Pending   0          78s
pod/workload-7fb9688459-kzx7k   0/1       Pending   0          32s
pod/workload-7fb9688459-m9pm5   1/1       Running   0          93s
pod/workload-7fb9688459-q9jgz   0/1       Pending   0          32s
pod/workload-7fb9688459-qr85m   1/1       Running   0          2m34s
pod/workload-7fb9688459-rjb6p   1/1       Running   0          2m34s
pod/workload-7fb9688459-rx4tf   1/1       Running   0          2m34s
pod/workload-7fb9688459-tw9xb   0/1       Pending   0          32s
pod/workload-7fb9688459-v689s   1/1       Running   0          2m34s
pod/workload-7fb9688459-x6t77   0/1       Pending   0          93s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   172.21.0.1   <none>        443/TCP   7d5h

NAME                       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/workload   19        19        19           7           2m35s

NAME                                  DESIRED   CURRENT   READY     AGE
replicaset.apps/workload-7fb9688459   19        19        7         2m35s

NAME                                           REFERENCE             TARGETS    MINPODS   MAXPODS   REPLICAS   AGE
horizontalpodautoscaler.autoscaling/workload   Deployment/workload   131%/50%   1         20        19         2m34s
~~~


## スケールアップ結果

次は、CA によるスケールアップ過程で、IaaSのコマンドを実行して、増設された仮想サーバーがプロビジョニングの進行状態を確認したものです。この動作は、データセンターによって差がありますが、新しデータセンターでは、2～3分でプロビジョニングが完了します。

~~~console
$ bx ks workers iks2
ID                        プライベート IP  状態           状況
kube-dal12-cra95d5d38-w1  10.74.139.152   normal        Ready
kube-dal12-cra95d5d38-w13 10.74.139.155   provisioning  Preparing to meter worker
kube-dal12-cra95d5d38-w14 10.74.139.176   provisioning  Waiting for IBM Cloud infrastructure: Setup provision configuration
kube-dal12-cra95d5d38-w15 10.74.139.158   provisioning  Waiting for IBM Cloud infrastructure: Publish server data
kube-dal12-cra95d5d38-w4  10.74.139.154   normal        Ready
~~~

最終的に、Kubernetesのクラスタに組み込まれ、最大値まで増強された結果が、以下です。今回の検証では無限ループのポッドを利用しましたから、最大値までスケールアップしました。

~~~console
$ kubectl get node
NAME            STATUS    ROLES     AGE       VERSION
10.74.139.152   Ready     <none>    7d5h      v1.12.5+IKS
10.74.139.154   Ready     <none>    5d18h     v1.12.5+IKS
10.74.139.155   Ready     <none>    4m59s     v1.12.5+IKS
10.74.139.158   Ready     <none>    3m6s      v1.12.5+IKS
10.74.139.176   Ready     <none>    33s       v1.12.5+IKS

$ kubectl get all
NAME                            READY     STATUS    RESTARTS   AGE
pod/workload-7fb9688459-4qdlg   1/1       Running   0          3m12s
pod/workload-7fb9688459-622dt   1/1       Running   0          5m5s
pod/workload-7fb9688459-66wmr   1/1       Running   0          3m34s
pod/workload-7fb9688459-6f892   1/1       Running   0          24m
pod/workload-7fb9688459-7wdn8   1/1       Running   0          24m
pod/workload-7fb9688459-8j92f   1/1       Running   0          5m5s
pod/workload-7fb9688459-c64p9   1/1       Running   0          25m
pod/workload-7fb9688459-j2v4l   1/1       Running   0          24m
pod/workload-7fb9688459-j5mvw   1/1       Running   0          24m
pod/workload-7fb9688459-jnwdv   1/1       Running   0          26m
pod/workload-7fb9688459-k25cc   1/1       Running   0          24m
pod/workload-7fb9688459-kzx7k   1/1       Running   0          24m
pod/workload-7fb9688459-m9pm5   1/1       Running   0          25m
pod/workload-7fb9688459-q9jgz   1/1       Running   0          24m
pod/workload-7fb9688459-qr85m   1/1       Running   0          26m
pod/workload-7fb9688459-rjb6p   1/1       Running   0          26m
pod/workload-7fb9688459-rx4tf   1/1       Running   0          26m
pod/workload-7fb9688459-tw9xb   1/1       Running   0          24m
pod/workload-7fb9688459-v689s   1/1       Running   0          26m
pod/workload-7fb9688459-x6t77   1/1       Running   0          25m

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   172.21.0.1   <none>        443/TCP   7d5h

NAME                       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/workload   20        20        20           20          26m

NAME                                  DESIRED   CURRENT   READY     AGE
replicaset.apps/workload-7fb9688459   20        20        20        26m

NAME                                           REFERENCE             TARGETS    MINPODS   MAXPODS   REPLICAS   AGE
horizontalpodautoscaler.autoscaling/workload   Deployment/workload   111%/50%   1         20        20         26m
~~~


## ワークロード停止とスケールダウン結果

次は、スケールダウンを確認してみます。デプロイメントを消すわけには行かないので、ポッドの仕様のコンテナを変更して、無限ループのコンテナから、Nginxに変更してワークロードを減らします。
最終的な確認の記録が、翌朝になってしまったので、この結果から時間が判らないですが、コンテナを変更後、約１０分でノードのキャンセルが始めります。

~~~
$ kubectl get all
NAME                            READY     STATUS    RESTARTS   AGE
pod/workload-55568bc7fb-gsz8w   1/1       Running   0          11h

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   172.21.0.1   <none>        443/TCP   7d17h

NAME                       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/workload   1         1         1            1           12h

NAME                                  DESIRED   CURRENT   READY     AGE
replicaset.apps/workload-55568bc7fb   1         1         1         11h
replicaset.apps/workload-7fb9688459   0         0         0         12h

NAME                                           REFERENCE             TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
horizontalpodautoscaler.autoscaling/workload   Deployment/workload   0%/50%    1         20        1          12h

$ kubectl get no
NAME            STATUS    ROLES     AGE       VERSION
10.74.139.152   Ready     <none>    7d17h     v1.12.5+IKS
10.74.139.154   Ready     <none>    6d6h      v1.12.5+IKS
~~~


## まとめ

これで、HPAとCAを組み合わせることで、クラウドベンダーに依存しない k8s の機能でCPU使用率ベースのオートスケールが実現できることが、確認できました。
テスト中に発見したことですが、CAを設定すると、クラウドのコンソールから自由にノード数を変更できなくなり、ConfigMapを修正してくださいとのメッセージが表示されるようになります。