GKEのゾーンクラスタにおけるUpgradeの挙動を観察する

初投稿です。
これから職場が変わるので、はじめてみました。

# Summary
GKEのゾーンクラスタにおけるUpgradeを試してみました。

# TL;DR
当たり前のことですが、改めて。

* ゾーンクラスタの場合はMasterのUpgrade時に操作できなくなるので、Productionではリージョンクラスタにすべき（SLAの観点からも当然）
* デフォルトで作成するとNodeは1coreだが、デフォルトのPodを考えると少なくとも2core以上必要
* Upgradeは時間がかかる上、何が起こるかわからないので、用心するならBlue-Greenで実施すべき（ゾーンごとに実施するなど）

## ゾーンクラスタの作成
約5分ほどかかります。

```
$ gcloud container clusters create zone-cluster --zone us-central1-a
```

## ゾーンクラスタのバージョン確認
```
$ kubectl get nodes
NAME                                          STATUS    ROLES     AGE       VERSION
gke-zone-cluster-default-pool-ea203fc9-0t05   Ready     <none>    1m        v1.11.7-gke.12
gke-zone-cluster-default-pool-ea203fc9-hjz6   Ready     <none>    1m        v1.11.7-gke.12
gke-zone-cluster-default-pool-ea203fc9-qws1   Ready     <none>    1m        v1.11.7-gke.12
```

## ゾーンクラスタにサンプルPodをデプロイ
```
$ kubectl run nginx --image=nginx --port=80 
$ kubectl scale deploy nginx --replicas=3
$ kubectl expose deploy nginx --type=LoadBalancer
```

## LoadBalancer経由でアクセスを確認
<img width="699" alt="スクリーンショット 2019-04-01 22.38.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/390509/a720e4b3-2f97-742a-2d72-d000c9f76a06.png">

これで準備完了です。

## Upgradeできるバージョンを確認
```
$ gcloud container get-server-config
...snip...
validImageTypes:
- UBUNTU
- COS_CONTAINERD
- COS
validMasterVersions:
- 1.12.6-gke.7
- 1.12.5-gke.5
...snip...
validNodeVersions:
- 1.12.6-gke.7
- 1.12.5-gke.10
- 1.12.5-gke.5
...snip...
```

# ゾーンクラスタのMasterのUpgrade中の挙動
## MasterをUpgradeする
```
$ gcloud container clusters upgrade zone-cluster --master --cluster-version 1.12.5-gke.5 --zone us-central1-a
```
約10分ほどかかります。

## Upgrade中はMasterへの接続が不可となる
```
kubectl get nodes
No resources found.
The connection to the server XXX.XXX.XXX.XXX was refused - did you specify the right host or port?
```

または、CLIがハングすることも。

```
$ kubectl get nodes
hung up...
```

## LoadBalancerからのPodのアクセスは可能
<img width="699" alt="スクリーンショット 2019-04-01 22.38.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/390509/a720e4b3-2f97-742a-2d72-d000c9f76a06.png">

## Upgrade完了後はCLIでのAPIアクセスが可能となる
```
Upgrading zone-cluster...done.                                                 
Updated

$ kubectl get nodes
NAME                                          STATUS    ROLES     AGE       VERSION
gke-zone-cluster-default-pool-ea203fc9-0t05   Ready     <none>    17m       v1.11.7-gke.12
gke-zone-cluster-default-pool-ea203fc9-hjz6   Ready     <none>    17m       v1.11.7-gke.12
gke-zone-cluster-default-pool-ea203fc9-qws1   Ready     <none>    17m       v1.11.7-gke.12
```

## gcloudでクラスタを確認すると、NodeがUpgrade可能な旨が表示される
```
$ gcloud container clusters describe zone-cluster --zone us-central1-a
...snip...
* - There is an upgrade available for your cluster(s).

To upgrade nodes to the latest available version, run
  $ gcloud container clusters upgrade zone-cluster
```

# ゾーンクラスタのNodeのUpgrade中の挙動
## NodeをUpgradeする
### ちょっと寄り道
Masterより先のバージョンを指定してUpgradeしようとすると、以下のエラーとなる。

```
$ gcloud container clusters upgrade zone-cluster --cluster-version 1.12.6-gke.7 --zone us-central1-a
ERROR: (gcloud.container.clusters.upgrade) ResponseError: code=400, message=Desired node version "1.12.6-gke.7" cannot be greater than current master version "1.12.5-gke.5".
```

* Masterはバージョンを一つ飛ばしてUpgradeできない
* NodeはMasterより先のバージョンにはできない

などの制約は[公式ガイド](https://cloud.google.com/kubernetes-engine/docs/how-to/upgrading-a-cluster?hl=ja)に記載されています。
というか今回の検証自体ほぼ全てドキュメント通りですが。

### 改めてNodeをUpgradeする
```
$ gcloud container clusters upgrade zone-cluster --cluster-version 1.12.5-gke.5 --zone us-central1-a
```
約10分〜15分ほどかかります。

## Nodeが1つずつスケジュール対象から外れる
```
kubectl get nodes
NAME                                          STATUS                     ROLES     AGE       VERSION
gke-zone-cluster-default-pool-ea203fc9-0t05   Ready,SchedulingDisabled   <none>    24m       v1.11.7-gke.12
gke-zone-cluster-default-pool-ea203fc9-hjz6   Ready                      <none>    24m       v1.11.7-gke.12
gke-zone-cluster-default-pool-ea203fc9-qws1   Ready                      <none>    24m       v1.11.7-gke.12
```

## Upgrade対象のNode上のPodがEvictされ、別Podにスケジュールされる

```
$ kubectl get pods -o wide
NAME                     READY     STATUS        RESTARTS   AGE       IP 
  NODE                                          NOMINATED NODE
nginx-6f858d4d45-56msx   0/1       Terminating   0          25m       10.60.2.4
  gke-zone-cluster-default-pool-ea203fc9-qws1   <none>
nginx-6f858d4d45-67f5w   0/1       Pending       0          2s        <none>
  <none>                                        <none>
nginx-6f858d4d45-9brxt   0/1       Terminating   0          25m       10.60.2.5
  gke-zone-cluster-default-pool-ea203fc9-qws1   <none>
nginx-6f858d4d45-g5nq4   0/1       Pending       0          1s        <none>
  <none>                                        <none>
nginx-6f858d4d45-l67xc   0/1       Pending       0          1s        <none>
  <none>                                        <none>
nginx-6f858d4d45-sjc64   0/1       Terminating   0          25m       10.60.1.4
  gke-zone-cluster-default-pool-ea203fc9-hjz6   <none>
```

```
$ kubectl get pods -o wide
kubectl get pods -o wide
NAME                     READY     STATUS              RESTARTS   AGE       IP        NODE                                          NOMINATED NODE
nginx-6f858d4d45-phccv   0/1       ContainerCreating   0          20s       <none>    gke-zone-cluster-default-pool-ea203fc9-hjz6   <none>
nginx-6f858d4d45-sv8tm   0/1       ContainerCreating   0          20s       <none>    gke-zone-cluster-default-pool-ea203fc9-hjz6   <none>
nginx-6f858d4d45-wvjq4   0/1       ContainerCreating   0          20s       <none>    gke-zone-cluster-default-pool-ea203fc9-hjz6   <none>
➜  ~ kubectl get pods -o wide
```

3 Nodeに対して 3 Replicaにしていたせいか、偏りが出たのか全てのPodがOutになってしまいました。
NginXにも繋がったり、繋がらなかったりしたので、Nodeに対してのAntiAffinityの設定が必要かもしれないです。

## 原因
Nodeのスペックの問題でした。

```
$ kubectl describe node gke-zone-cluster-default-pool-ea203fc9-qws1 | grep "Non-terminated Pods" -A15
Non-terminated Pods:         (6 in total)
  Namespace                  Name                                                      CPU Requests  CPU Limits  Memory Requests  Memory Limits
  ---------                  ----                                                      ------------  ----------  ---------------  -------------
  default                    nginx-6f858d4d45-cb4np                                    100m (10%)    0 (0%)      0 (0%)           0 (0%)
  default                    nginx-6f858d4d45-cns2v                                    100m (10%)    0 (0%)      0 (0%)           0 (0%)
  default                    nginx-6f858d4d45-rs6nq                                    100m (10%)    0 (0%)      0 (0%)           0 (0%)
  kube-system                fluentd-gcp-v3.2.0-4rfr7                                  100m (10%)    1 (106%)    200Mi (7%)       500Mi (18%)
  kube-system                kube-proxy-gke-zone-cluster-default-pool-ea203fc9-qws1    100m (10%)    0 (0%)      0 (0%)           0 (0%)
  kube-system                prometheus-to-sd-qdpr9                                    1m (0%)       3m (0%)     20Mi (0%)        20Mi (0%)
```

```
$ kubectl describe node gke-zone-cluster-default-pool-ea203fc9-0t05 | grep "Non-terminated Pods" -A15 
Non-terminated Pods:         (10 in total)
  Namespace                  Name                                                      CPU Requests  CPU Limits  Memory Requests  Memory Limits
  ---------                  ----                                                      ------------  ----------  ---------------  -------------
  kube-system                event-exporter-v0.2.3-85644fcdf-xjqqj                     0 (0%)        0 (0%)      0 (0%)           0 (0%)
  kube-system                fluentd-gcp-scaler-8b674f786-sm5hc                        0 (0%)        0 (0%)      0 (0%)           0 (0%)
  kube-system                fluentd-gcp-v3.2.0-rjqf8                                  100m (10%)    1 (106%)    200Mi (7%)       500Mi (18%)
  kube-system                heapster-v1.6.0-beta.1-54f596f79d-4tg6k                   138m (14%)    138m (14%)  301856Ki (11%)   301856Ki (11%)
  kube-system                kube-dns-7df4cb66cb-4tvz5                                 260m (27%)    0 (0%)      110Mi (4%)       170Mi (6%)
  kube-system                kube-dns-7df4cb66cb-94b24                                 260m (27%)    0 (0%)      110Mi (4%)       170Mi (6%)
  kube-system                kube-dns-autoscaler-76fcd5f658-jgfml                      20m (2%)      0 (0%)      10Mi (0%)        0 (0%)
  kube-system                kube-proxy-gke-zone-cluster-default-pool-ea203fc9-0t05    100m (10%)    0 (0%)      0 (0%)           0 (0%)
  kube-system                metrics-server-v0.3.1-54699c9cc8-ng46d                    53m (5%)      148m (15%)  154Mi (5%)       404Mi (15%)
  kube-system                prometheus-to-sd-m8d5n                                    1m (0%)       3m (0%)     20Mi (0%)        20Mi (0%)
```

一つのNodeにPodが集中してしまうと、GKEのデフォルトで利用されるPodでrequestsがいっぱいになってしまうみたいです。

デフォルトで作成した場合は1coreになりますが、少なくとも2core以上で作った方が良いですね。

# 備忘：PodAntiAffinityの設定
この設定を入れると、Podがよしなに別ノードに作成されるようになります。

```
affinity:
  podAntiAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
      - podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: run
              operator: In
              values:
              - nginx
          topologyKey: kubernetes.io/hostname
        weight: 100
```

# あとでやる
* PVCを設定している時のUpgrade
* リージョンクラスタでのUpgrade
* リージョンクラスタでのゾーンごとのBlue-Green Upgrade