GKEノードをオートスケールさせる方法は2つある？

## 概要
GKEのPodのスケールアウト(HPA等）については、いくつか情報があったのですが、
GKEノード(GCE)のスケールアウトについてはあまり情報がなかったので調査。

## 環境
* GKEマスターバージョン：1.14.10-gke.36
* ノードのバージョン：1.14.10-gke.27

# GKEノードをオートスケールさせる方法は2つある？
GKEノードをスケールアウトさせる方法を調査していたところ、以下2つのやり方を見つけました。
1つは、GKEのクラスタの編集画面から設定可能な「**ノードの自動プロビジョニング**」。
<img width="528" alt="スクリーンショット 2020-05-19 14.25.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95324/0ac3fddc-7633-0d7e-3687-a596792e2600.png">

もう1つは、ノードプールの編集画面から設定可能な「**自動スケーリングの有効化**」。
<img width="566" alt="スクリーンショット 2020-05-19 14.35.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95324/73214b81-18b0-5ea4-85f9-b9f4580a6195.png">

この2つの違いは何でしょうか。

## 「自動スケーリングの有効化」とは
GKEのクラスタオートスケーラー機能です。
[こちら](https://cloud.google.com/kubernetes-engine/docs/concepts/cluster-autoscaler#overview)の文献によると、
> GKE のクラスタ オートスケーラーは、ワークロードの需要に応じて、ノードプールのノード数を自動的に変更します。ノードを手動で追加または削除する必要はありません。また、ノードプールを過剰にプロビジョニングすることもありません。ノードプールの最小サイズと最大サイズを指定するだけで、あとは自動的に設定されます。

上記の通り、ノードプールの最小サイズと最大サイズを指定するだけでオートスケールしてくれます。
また、オートスケールのトリガーは、ノードで実行されている Pod のリソースリクエスト数に基づいて行われます。
詳細は、[こちら](https://cloud.google.com/kubernetes-engine/docs/concepts/cluster-autoscaler#overview)をご参照ください。

# 「ノードの自動プロビジョニング」とは
ノード自動プロビジョニングは、上述のクラスタの自動スケーリングの拡張版の位置づけのようです。
[こちら](https://cloud.google.com/kubernetes-engine/docs/how-to/node-auto-provisioning)の文献によると、

> ノード自動プロビジョニング機能が有効な場合は、新しいノードプールの作成と削除が自動的に行われます。

さきほどの自動スケーリングとは違いノードプール単位でスケーリングを行います。つまりスケールアウトする際は新たにノードプールを作成し、スケールインする際はノードグループを削除します。
また、スケールの設定方法はクラスタの自動スケーリングとは違い、トリガーとして CPU, Memory リソース等を最小数、最大数で指定します。

## 結局何が違うのか？
2つの違いに以下の表にまとました。

|項目　　　　　　　　　　　|自動スケーリングの有効化　　　　　　　　　　　　　　　|ノードの自動プロビジョニン　　グ　　　　　　　　　　　　　　　　　|
|  ----  |  ---- | ---- |
|オートスケール時の挙動 |ノードプール内でノードを増減 |新たにノードプールを作成 |
|リソースの指定方法  |ノードの最小、最大数を指定 |CPU,Memoryといったリソースを指定 |
|トリガー  |Podのリソースリクエスト数。詳細は[こちら](https://cloud.google.com/kubernetes-engine/docs/concepts/cluster-autoscaler#overview) |Podのリソースリクエスト数。詳細は[こちら](https://cloud.google.com/kubernetes-engine/docs/how-to/node-auto-provisioning?hl=ja#operation) |
|ワークロードの分離 |できない？ |[できる](https://cloud.google.com/kubernetes-engine/docs/how-to/node-auto-provisioning#workload_separation) |

## オートスケールの注意点
### ノード自動プロビジョニングにてImagePullBackOff
ノード自動プロビジョニングにてスケールアウトした際、スケールアウトしたNode上のPodで`ImagePullBackOff`エラーが発生しました。
これは、[IDのデフォルト](https://cloud.google.com/kubernetes-engine/docs/how-to/node-auto-provisioning#identity)の設定を行っていないことが原因でした。

> 新しいノードプールは、他のノードプールから ID を継承しません。デフォルトの ID を指定しないと、自動プロビジョニングされたノードプールで実行されるワークロードは Google Cloud APIs に一切アクセスできません。たとえば、コンテナ イメージを非公開の Container Registry から pull するには、https://www.googleapis.com/auth/devstorage.read_only スコープを追加する必要があります。

対応としては、以下のような設定を行います。

```shell

$ gcloud container clusters update dev-cluster --zone=us-central1-c \
    --enable-autoprovisioning \
    --autoprovisioning-scopes=https://www.googleapis.com/auth/pubsub,https://www.googleapis.com/auth/devstorage.read_only
```

また、ドキュメントには `--zone` オプションの記載がありませんが、
クラスタがリージョンクラスタの場合は `--region=<リージョン名>` を、ゾーンクラスタの場合は `--zone=<ゾーン名>​` をオプションとして追加する必要あるそうです。
お気をつけを。


### ノードの自動プロビジョニングで作成されるマシンタイプについて
ノード自動プロビジョニングで新たに作成されたノードプールのマシンタイプが、既存のノードプールのマシンタイプとは別のマシンタイプ(少し小さめ)が起動していました。
どのような基準でマシンタイプを選定しているのか、調べた限りでは分からなかったのと、
マシンタイプの指定もできなさそうだったので、不便だなーと思いました。
このあたり、ご存知の方がいればご教示ください。

### リソース不足でpodがデプロイできない？
ノードの自動プロビジョニングにてGKEノードがスケールアウトしたにも関わらず、
Podが`0/4 nodes are available: 4 Insufficient cpu.`といったエラーでこけることがありました。

原因としては、新たに起動したGKEノードのリソース不足でした。
**ではなぜスケールアウトしたにも関わらず、リソース不足だったのでしょうか？**

考えられる原因として、GKEノード全体の合計リソースがPodに定義したrequestsを上回っているが、
**ノード単位で見ると、どのノードのリソースも不足してしており、Podがデプロイができないという状況であったことが考えられます。**
これは、[ノードの自動プロビジョニングで作成されるマシンタイプについて](#ノードの自動プロビジョニングで作成されるマシンタイプについて)にも記載しましたが、既存のノードプールのマシンタイプとは別の少し小さめのマシンタイプがGKEノードとして起動しており、それが原因となっていました。


## 結局どっちを使えいいの？
GKEノードのオートスケールに細かい制御は不要 -> `自動スケーリングの有効化`
GKEノードのオートスケールに細かい制御(ワークロードの分離とか)がしたい -> `ノードの自動プロビジョニング`
といった感じで良い気がします。