IKS クラスタ・オートスケーラーの検証

ポッドのリソース要求に応じて、クラスタのノード数を自動的に増やす、また、ポッドの減少に応じて、ノードを削減する機能として、Cluster Autoscaler[1] が CNCFのKubernetesプロジェクトで開発が進んでおり、IBM Cloud Kubernetes Service (IKS) でもリリースされています。 [2,3]

注意
この記事は、ベータテスト段階のIKS Cluster Autoscaler の検証であり、本番提供と異なる場合があります。


## 検証として必要なこと

以下にCluster Autoscaler を利用するまでの準備作業とほ方法が書かれたリンクを挙げておきます。

* Kubernetesクラスタの理解として[Kubernetes オートスケーリング機能の自習メモ](https://qiita.com/MahoTakara/items/2daab16117ce73b41d1b)
* [IBM Cloud のユーザーアカウント取得](https://cloud.ibm.com/registration)
* [パソコンへの IBM Client CLI のインストール](https://console.bluemix.net/docs/cli/index.html#overview)
* [ibmcloud ログイン](https://console.bluemix.net/docs/cli/reference/ibmcloud/bx_cli.html#ibmcloud_login)
* [IKS クラスターの起動　標準クラスタの１ノード構成](https://console.bluemix.net/docs/containers/container_index.html#container_index)
* [Slackからの CA 有効化依頼](https://cloud.ibm.com/docs/containers/cs_cluster_scaling.html#ca)
* [Helmのインストール](https://cloud.ibm.com/docs/containers/cs_integrations.html#helm)

## Cluster Autoscaler の起動


HELM から IBM-IKS-CLUSTER-AUTOSCALER をインストールします。HELM に IBMのリポジトリが登録されていない場合、パッケージが発見できないので、前述の[Helmのインストール](https://cloud.ibm.com/docs/containers/cs_integrations.html#helm)を見直してください。

~~~
helm install ibm/ibm-iks-cluster-autoscaler --namespace kube-system --name ibm-iks-cluster-autoscaler
~~~

次に、IBM CA のコンフィグマップをファイルに書き出して、編集、再適用を進めます。

~~~
kubectl get cm iks-ca-configmap -n kube-system -o yaml > iks-ca-configmap.yaml
~~~

コンフィグマップのファイルの編集は、次の箇所です。

~~~iks-ca-configmap.yaml:編集前
apiVersion: v1
data:
  workerPoolsConfig.json: |
    [
     {"name": "default","minSize": 1,"maxSize": 2,"enabled":false}
    ]
kind: ConfigMap
＜以下省略＞
~~~

~~~iks-ca-configmap.yaml:編集後
[
 {"name": "default","minSize": 1,"maxSize": 5,"enabled":enable}
]
~~~

上記の編集箇所で、「default」は、オートスケール対象の「ワーカー・プール」です。この設定ではk8sクラスタを作成した時にデフォルトで作られる「ワーカー・プール」を指定したことになります。そして、「maxSize｣は、2では縮小の動きが判らないので、5に設定しました。　「enabled｣は、デフォルトでは無効になっていますから、これを enable (有効)に変更します。

~~~
kubectl apply -f iks-ca-configmap.yaml -n kube-system
~~~

これで、ポッドの ibm-iks-cluster-autoscalerｰXXXXX が、ConfigMapの変更を検知して、再読み込みすれば、変更完了です。もしも、オートスケーラーのポッドに問題があって、コンフィグマップの更新を検知して、再読み込みしてくれなければ、ポッドを一旦削除して、自己回復させることで、設定変更が反映されます。

これで、クラスタ・オートスケーラーの準備が完了しました。ここから、検証用のコンテナを準備して検証を進めます。


## 擬似ワークロード発生用コンテナの作成

クラスタ・オートスケーラーは、実際のCPU使用率をモニタリングせずに、マニフェストに書かれたリソース要求とノードでの割り当て状態を鑑みて、ノードの追加を決定します。そのため、わざわざ、ポッドを開発しなくても良いのですが、HPAとCAを組み合わせたオートスケールを検証することを視野に入れて、コンテナを準備しておきます。
コンテナのソースコードは、[takara9/container-workload](https://github.com/takara9/container-workload)においてあり、コンテナイメージは、DockerHubの[maho/testwkld](https://hub.docker.com/r/maho/testwkld)に置きました。コンテナを実行すると無限ループで、CPUを消費します。

## マニフェスト

前述の擬似ワークロード発生コンテナをデプロイして、オートスケールを発生させるためのマニフェストを作って、ポッドをデプロイします。
このマニフェストでは、.spec.containers[0].resources.requess.cpu の値が、300ミリ秒として、ポッドをデプロイする際に、CPU時間の要求量を毎秒0.3秒を要求しています。

~~~file:deploy.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: workload
spec:
  replicas: 10
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: load
        image: maho/testwkld:0.1
        resources:
          requests:
            memory: "100Mi"
            cpu: "300m"
          limits:
            memory: "100Mi"
            cpu: "400m"
~~~


これは、ノードに２コアある場合、すべてのコアを利用できるとした場合、２コア x 1000ミリ秒 / 300ミリ秒　-> ６個の ポッドが割り当て可能となります。
しかし、現実はノードには、クラスタを維持するために必要なポッドが起動しており、それらの確保分を計算に入れなければなりません。

クラスタのメンバーをリストしておき、ワークロードを与えていない状態で、空きCPUの時間を確認しておきます。

~~~
$ kubectl get no
NAME            STATUS    ROLES     AGE       VERSION
10.74.139.152   Ready     <none>    6d23h     v1.12.5+IKS
10.74.139.154   Ready     <none>    5d12h     v1.12.5+IKS
$ kubectl get po
No resources found.
~~~

ここで、前述のマニフェストを適応することで、それぞれのノードに幾つのポッドがデプロイされ、幾つのノードが追加されるか、計算しておき、論理的な結果を予測しておきます。

このノードでは次の状態から、
(２コア x 1000ミリ秒 - 既消費割当量 913ミリ秒) / コンテナ当たり要求 300ミリ秒 -> 3.62
なので、ポッドは３個まで、このノードにデプロイできます。

~~~
$ kubectl describe no 10.74.139.152
Name:               10.74.139.152
＜中略＞
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource  Requests        Limits
  --------  --------        ------
  cpu       913m (47%)      1448m (75%)
  memory    910866Ki (25%)  2176226Ki (61%)
~~~

こちらのノードでは、
(２コア x 1000ミリ秒 - 既消費割当量 575ミリ秒) / コンテナ当たり要求 300ミリ秒 -> 4.75
となりますから、４個のポッドをデプロイできます。

~~~
$ kubectl describe no 10.74.139.154
Name:               10.74.139.154
＜中略＞
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource  Requests       Limits
  --------  --------       ------
  cpu       575m (29%)     600m (31%)
  memory    302610Ki (8%)  1490257920 (41%)
~~~

そうすると、これらの計算結果から、既存ノードに、３個＋４個デプロイされ、のこりの３個がペンディング状態となり、
ノードが追加され、残り３個がデプロイされることが、予測されます。

それでは、そのとおりになるか、確認してみましょう。

~~~
$ kubectl apply -f deploy-pod.yml
deployment.apps/workload created

$ kubectl get po -o wide
NAME                        READY     STATUS    RESTARTS   AGE       IP               NODE            NOMINATED NODE
workload-7fb9688459-4k486   1/1       Running   0          51s       172.30.205.115   10.74.139.152   <none>
workload-7fb9688459-bnxwp   0/1       Pending   0          51s       <none>           <none>          <none>
workload-7fb9688459-d9wpq   0/1       Pending   0          51s       <none>           <none>          <none>
workload-7fb9688459-fpqg2   1/1       Running   0          51s       172.30.58.18     10.74.139.154   <none>
workload-7fb9688459-fpxcd   1/1       Running   0          51s       172.30.58.17     10.74.139.154   <none>
workload-7fb9688459-hwvhb   1/1       Running   0          51s       172.30.205.117   10.74.139.152   <none>
workload-7fb9688459-jdq5h   1/1       Running   0          51s       172.30.205.116   10.74.139.152   <none>
workload-7fb9688459-kbmmv   1/1       Running   0          51s       172.30.58.15     10.74.139.154   <none>
workload-7fb9688459-mthpf   1/1       Running   0          51s       172.30.58.16     10.74.139.154   <none>
workload-7fb9688459-s8754   0/1       Pending   0          51s       <none>           <none>          <none>
~~~

上記の結果にように、３個のポッドがペンディング状態となり、３個と４個に分かれて、想定どおりに、デプロイされたことがわかります。
ここから、オートスケールが始まります。

時間間隔をあけながら、コマンドを実行して、ノードが追加され、ペンディンク状態のポッドが無くなるまでの様子を確認したものです。

~~~
$ kubectl get no
NAME            STATUS    ROLES     AGE       VERSION
10.74.139.152   Ready     <none>    6d23h     v1.12.5+IKS
10.74.139.154   Ready     <none>    5d12h     v1.12.5+IKS

$ bx ks workers iks2
OK
ID                          プライベート IP   マシン・タイプ      状態           状況                        ゾーン   バージョン
kube-dal12-cra95d5d380-w1   10.74.139.152    u2c.2x4.encrypted  normal        Ready                       dal12    1.12.5_1537
kube-dal12-cra95d5d380-w4   10.74.139.154    u2c.2x4.encrypted  normal        Ready                       dal12    1.12.5_1537
kube-dal12-cra95d5d380-w9   10.74.139.155    u2c.2x4.encrypted  provisioning  Preparing to meter worker   dal12    1.12.5_1537

$ bx ks workers iks2
OK
ID                          プライベート IP   マシン・タイプ      状態       状況    ゾーン   バージョン
kube-dal12-cra95d5d380-w1   10.74.139.152    u2c.2x4.encrypted  normal     Ready   dal12    1.12.5_1537
kube-dal12-cra95d5d380-w4   10.74.139.154    u2c.2x4.encrypted  normal     Ready   dal12    1.12.5_1537
kube-dal12-cra95d5d380-w9   10.74.139.155    u2c.2x4.encrypted  deployed   -       dal12    1.12.5_1537

$ kubectl get no
NAME            STATUS    ROLES     AGE       VERSION
10.74.139.152   Ready     <none>    7d        v1.12.5+IKS
10.74.139.154   Ready     <none>    5d12h     v1.12.5+IKS
10.74.139.155   Ready     <none>    4m11s     v1.12.5+IKS

$ kubectl get po -o wide
NAME                        READY     STATUS    RESTARTS   AGE  NODE
workload-7fb9688459-4k486   1/1       Running   0          20m  10.74.139.152
workload-7fb9688459-bnxwp   1/1       Running   0          20m  10.74.139.155
workload-7fb9688459-d9wpq   1/1       Running   0          20m  10.74.139.155
workload-7fb9688459-fpqg2   1/1       Running   0          20m  10.74.139.154
workload-7fb9688459-fpxcd   1/1       Running   0          20m  10.74.139.154
workload-7fb9688459-hwvhb   1/1       Running   0          20m  10.74.139.152
workload-7fb9688459-jdq5h   1/1       Running   0          20m  10.74.139.152
workload-7fb9688459-kbmmv   1/1       Running   0          20m  10.74.139.154
workload-7fb9688459-mthpf   1/1       Running   0          20m  10.74.139.154
workload-7fb9688459-s8754   1/1       Running   0          20m  10.74.139.155
~~~
(出力項目は見やすくするため編集を加えています）

次はスケールダウンを確認します。ポッドの数を５に減らして、状態を確認します。


~~~
$ kubectl get deploy
NAME       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
workload   5         5         5            5           29m

$ kubectl get po
NAME                        READY     STATUS        RESTARTS   AGE
workload-7fb9688459-4k486   1/1       Running       0          29m
workload-7fb9688459-bnxwp   1/1       Terminating   0          29m
workload-7fb9688459-d9wpq   1/1       Terminating   0          29m
workload-7fb9688459-fpqg2   1/1       Terminating   0          29m
workload-7fb9688459-fpxcd   1/1       Running       0          29m
workload-7fb9688459-hwvhb   1/1       Running       0          29m
workload-7fb9688459-jdq5h   1/1       Running       0          29m
workload-7fb9688459-kbmmv   1/1       Running       0          29m
workload-7fb9688459-mthpf   1/1       Terminating   0          29m
workload-7fb9688459-s8754   1/1       Terminating   0          29m

$ kubectl get po
NAME                        READY     STATUS    RESTARTS   AGE
workload-7fb9688459-4k486   1/1       Running   0          32m
workload-7fb9688459-fpxcd   1/1       Running   0          32m
workload-7fb9688459-hwvhb   1/1       Running   0          32m
workload-7fb9688459-jdq5h   1/1       Running   0          32m
workload-7fb9688459-kbmmv   1/1       Running   0          32m

$ kubectl get no
NAME            STATUS                        ROLES     AGE       VERSION
10.74.139.152   Ready                         <none>    7d        v1.12.5+IKS
10.74.139.154   Ready                         <none>    5d13h     v1.12.5+IKS
10.74.139.155   NotReady,SchedulingDisabled   <none>    34m       v1.12.5+IKS

$ bx ks workers iks2
OK
ID                            プライベート IP   マシン・タイプ      状態      状況    ゾーン   バージョン
kube-dal12-cra95d5d38080-w1   10.74.139.152    u2c.2x4.encrypted  normal    Ready   dal12    1.12.5_1537
kube-dal12-cra95d5d38080-w4   10.74.139.154    u2c.2x4.encrypted  normal    Ready   dal12    1.12.5_1537
kube-dal12-cra95d5d38080-w9   10.74.139.155    u2c.2x4.encrypted  deleted   -       dal12    1.12.5_1537

$ bx sl vs list
id         hostname                                            ドメイン     cpu   メモリー   private_ip      datacenter
70598691   kube-dal12-cra95d5d380807481480cb68c4348fc9ea-w1    cloud.ibm    2     4096      10.74.139.152   dal12
70741627   kube-dal12-cra95d5d380807481480cb68c4348fc9ea-w4    cloud.ibm    2     4096      10.74.139.154   dal12
~~~
(出力項目は見やすくするため編集を加えています）

上記のように、ポッドの数の減少にともない、ノードの数も削減されることが確認できました。

スケールアップの時間は、ペンディング状態のポッドが発生して、２～３分でスケールアップが発動します。仮想サーバーの起動とノードのセットアップで10分くらいで起動します。データセンターの状態にも依存しますから、一概には言えないですが、およそ、こんな感じです。そして、スケールダウンは、10分間待機後にスケールダウンが発動して、５分程度で不要なノードが削減されます。


## まとめ

今回の検証では期待どおりの動作が確認でき、正式提供となる時期も近いと思います。しかし、IKSのクラスタ・オートスケーラーは、ベータテストの段階ですから、これから、仕様に多少の変更が発生するかもしれませんので、留意いただければと思います。　今後、HPAと組み合わせや、Knaiveとの組み合わせなど、応用が考えられ楽しみですね。


[1] Cluster Autoscaler, https://github.com/kubernetes/autoscaler/tree/master/cluster-autoscaler
[2] Scaling clusters (preview beta), https://cloud.ibm.com/docs/containers/cs_cluster_scaling.html#ca
[3] Kubernetes オートスケーリング機能の自習メモ, https://qiita.com/MahoTakara/items/2daab16117ce73b41d1b
[4] Worker Node Auto-Scaling GA in IBM Cloud Kubernetes Service, https://www.ibm.com/blogs/bluemix/2019/03/worker-node-auto-scaling-ga-in-ibm-cloud-kubernetes-service/