GKEでCA/HPA/PDBの軽い挙動確認

タイトル通り
細かいしきい値とか実装までは追っていないので軽め
PODの垂直のほうには触れません
なお、検証したのは2021年8月なので、以前以後による変更があるやもしれません

コードブロックに貼ってあるやつは要点以外は削っていることが多いです

## 何を見たかったんですか
ノードのスケールイン/アウトの様子
PODのスケールイン/アウトの様子
PDBの守護神っぷり

## 参考(公式)
https://kubernetes.io/ja/docs/tasks/configure-pod-container/assign-cpu-resource/
https://kubernetes.io/ja/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/
https://cloud.google.com/kubernetes-engine/docs/how-to/horizontal-pod-autoscaling?hl=ja#kubectl-apply
https://cloud.google.com/kubernetes-engine/docs/concepts/cluster-autoscaler?hl=ja
https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/FAQ.md#does-ca-work-with-poddisruptionbudget-in-scale-down

ほぼ日本語なのがすばらしい

## クラスタ構成(要点のみ)
自動スケーリングを有効にして最低1/最高3に設定
インスタンスタイプはn2-std2(2vcpu)
他はデフォルト(アクセス権限関係は必要ならちゃんと設定しよう)

e2系(small以上)だと

```yaml
Capacity:
  cpu: 2

Allocatable:
  cpu: 940m ← !?
```
という、CPU2って書いてあるのに割り当てが2000mじゃないという現象に襲われて検証めんどうくさくなるのでn2系が無難

## deployments
アプリケーションに固有の部分は適当に読み替えてください

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: yourAPP
  labels:
    app: yourAPP
spec:
  replicas: 1
  selector:
    matchLabels:
      app: yourAPP
  template:
    metadata:
      labels:
        app: yourAPP
    spec:
      containers:
        - name: yourAPP
          image: [[your image url]]
          ports:
            - containerPort: xxxxx
          resources:
            requests:
              cpu: 500m
```
要するにレプリカ1のcpuリクエストは実値で500mです

CAはリクエストに呼応するため、リクエストを設定しないとウンともスンともしてくれません
また、リクエストはリソースの確保ではなく、勝手な宣言です
いくら使うと宣言しても実際の使用率には影響しません

## HPA
アプリケーションに固有の部分はry
v2からはCPU以外の指標も扱えるそうですが、とりあえずCPUで事足りるだろうということで
HPAは指定したDeployments(POD)以外のリソースには見向きもしないので結構考えやすいですね

```yaml
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: yourHPA
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: yourAPP
  minReplicas: 1
  maxReplicas: 5
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: AverageValue
          averageValue: 500m
```
UtilizationではなくAverageValueを使います
ノードのCPUコア数が分かってるならこっちのほうが考えやすい気がする

なお、Utilizationを使う場合でDeployments側にrequestsの設定をしていない場合、

```
failed to compute desired number of replicas based on listed metrics for Deployment/yourAPP: invalid metrics (1 invalid out of 1), first error is: failed to get cpu utilization: missing request for cpu
```
こんなのが出てTARGETSが一生unknownのままだったので気を付けましょう
ちなみにrequestsが無くてもAverageValueを使えばHPAは機能はしますがノードの増減が行われません(上述)
PODの数だけスケールしたいってのはあんまりない要件な気はするので、基本は設定して運用するかと思います

## PDB
```yaml
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: yourAPP
spec:
  minAvailable: 5
  selector:
    matchLabels:
      app: yourAPP
```
最低有効であるべきpod数を5に設定

## 検証
### 開幕

```
# node
NAME   STATUS   ROLES
node1  Ready    <none>

# pod
NAME   READY   STATUS    RESTARTS
pod1   1/1     Running   0

# top pod
NAME   CPU(cores)   MEMORY(bytes)
pod1   0m           1Mi

# HPA
NAME  REFERENCE            TARGETS   MINPODS   MAXPODS   REPLICAS
hpa   Deployment/yourAPP   0/500m    1         5         1
```

### pod1に負荷をかける
pod1に接続して`yes > /dev/null`を実行、しばらく後

```
# node
NAME   STATUS   ROLES
node1  Ready    <none>

# pod
NAME   READY   STATUS    RESTARTS
pod1   1/1     Running   0
pod2   1/1     Running   0

# top pod
NAME   CPU(cores)   MEMORY(bytes)
pod1   1000m        2Mi
pod2   0m           1Mi

# HPA
NAME  REFERENCE            TARGETS    MINPODS   MAXPODS   REPLICAS
hpa   Deployment/yourAPP   500m/500m  1         5         2
```
予想通りpodが1個増えてる。

### pod1/2に負荷をかける
続いてpod2にも負荷をかける

```
# node
NAME   STATUS   ROLES
node1  Ready    <none>
node2  Ready    <none>

# pod
NAME   READY   STATUS    RESTARTS
pod1   1/1     Running   0
pod2   1/1     Running   0
pod3   1/1     Running   0
pod4   1/1     Running   0

# top pod
NAME   CPU(cores)   MEMORY(bytes)
pod1   1000m        2Mi
pod2   1000m        1Mi
pod3   0m           1Mi
pod4   0m           1Mi

# HPA
NAME  REFERENCE            TARGETS    MINPODS   MAXPODS   REPLICAS
hpa   Deployment/yourAPP   500m/500m  1         5         4
```
pod3以降がnode1にdeploy出来なくなったため(リソース不足)ノードも増えてる。

### pod1/2/3に負荷をかける

```
node1  Ready    <none>
node2  Ready    <none>
node3  Ready    <none>

# pod
NAME   READY   STATUS    RESTARTS
pod1   1/1     Running   0
pod2   1/1     Running   0
pod3   1/1     Running   0
pod4   1/1     Running   0
pod5   1/1     Running   0

# top pod
NAME   CPU(cores)   MEMORY(bytes)
pod1   1000m        2Mi
pod2   1000m        1Mi
pod3   1000m        1Mi
pod4   0m           1Mi
pod5   0m           1Mi

# HPA
NAME  REFERENCE            TARGETS    MINPODS   MAXPODS   REPLICAS
hpa   Deployment/yourAPP   723m/500m  1         5         5
```

```
# node desc(cpuのみ)
  Resource   Requests     Limits
  --------   --------     ------
  cpu        1639m (84%)  201m (10%)

  Resource   Requests     Limits
  --------   --------     ------
  cpu        1463m (75%)  0 (0%)

  Resource   Requests    Limits
  --------   --------    ------
  cpu        703m (36%)  0 (0%)
```

```
# PDB(一部)
spec:
  minAvailable: 5
  selector:
    matchLabels:
      app: yourAPP
status:
  currentHealthy: 5
  desiredHealthy: 5
  disruptionsAllowed: 0
  expectedPods: 5
  observedGeneration: 3
```

### 負荷解除
解除してしばらく後

```
# node
NAME   STATUS   ROLES
node1  Ready    <none>
node2  Ready    <none>

# pod
NAME   READY   STATUS    RESTARTS
pod1   1/1     Running   0

# top pod
NAME   CPU(cores)   MEMORY(bytes)
pod1   0m           1Mi

# HPA
NAME  REFERENCE            TARGETS   MINPODS   MAXPODS   REPLICAS
hpa   Deployment/yourAPP   0/500m    1         5         1
```

## 結論と余談
### CA/HPA
設定内容さえ分かっていればかなり素直に動いてくれそう
ただ、上記の最終状態からdeploymentを消してもnode2はスケールインされなかったため、1-2台目にはシステム関連のpodがdeployされるのかもしれない。一応、2台目と3台目の初期podで3台目には`kube-dns`がなかったためそれなのか？
なお、素の状態(こっちで定義したアプリケーションを一切deployしていない状態)だとCPUリクエストが
`1台目: 639m` `2台目: 463m` `3台目: 203m`となっており、vcpuの数を変えても変化はなかったため、1vcpuで2台以下だと割とカツカツだった

### PDB
あくまでこいつはノードの計画停止とかでdrainが走る時に面倒を見てくれる機構であり、HPAとは関与しない様子
なのでPDBでminを5にしていてもHPAで1に設定していればごりごりスケールインされる
drain使った検証は他に記事たくさんあったから割愛