AKS で Node 再起動の影響を最小限にするための仕組みを理解する

Kubernetes の運用をしていると避けて通れないのは、サーバー/Kubernetes コンポーネントのアップデート、アップグレードの作業です。それを実行する際には、Node 再起動が必要となります。ここまで Pod が動いている Node を指定する仕組みなどを触れてきましたが、再起動する際にアプリケーションへの影響を最小限にする仕組みを見ていきたいと思います。

#環境準備
過去の記事にしたがって、AKS の検証環境を準備します。
[Azure Kubernetes Services 環境を構築する](https://qiita.com/komiyasa/items/3c1cd47dd0d53e5253ef)

その上で今回は以下のマニフェストファイルで、3つの Node を持つクラスターに対して Nignx の Deployment を作成します。

```nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
```
作成が完了したら、Pod と Node の関係性を以下オプションを付けたコマンドで確認します。

```
$ kubectl get pod -o custom-columns=Pod:metadata.name,Node:spec.nodeName
Pod                    Node
nginx-5c7588df-77kvs   aks-nodepool1-38358128-vmss000001
nginx-5c7588df-rpdbl   aks-nodepool1-38358128-vmss000000
nginx-5c7588df-z64g6   aks-nodepool1-38358128-vmss000002
```
作成したそれぞれの Pod が 3つの Node に分散されて配置していることが分かります。ここまでで準備は完了です。

#Cordon/Uncordon
Cordon は「閉鎖する」という意味です。Cordon を実行することで、特定の Node をスケジュールの対象から除外することが可能です。Uncordon はその逆で、Node をスケジュール対象に戻すコマンドです。実際に上記で確認した `aks-nodepool1-38358128-vmss000001` という名前の Node に対して Cordon を実行してみます。

```
$ kubectl cordon aks-nodepool1-38358128-vmss000001
node/aks-nodepool1-38358128-vmss000001 cordoned
```
これで特定の Node が Cordon の対象になりました。`kubectl get node` コマンドで詳細を確認してみます。

```
$ kubectl get node
NAME                                STATUS                     ROLES   AGE   VERSION
aks-nodepool1-38358128-vmss000000   Ready                      agent   11m   v1.13.12
aks-nodepool1-38358128-vmss000001   Ready,SchedulingDisabled   agent   10m   v1.13.12
aks-nodepool1-38358128-vmss000002   Ready                      agent   11m   v1.13.12
```
指定した Node が `SchedulingDisabled` となっているのが分かります。これはスケジューリングから除外されたことを意味していて、Pod のスケールアップ等から除外されていることを表します。試しにレプリカ数を増やしてみます。

```
$ kubectl scale deployment nginx --replicas=6
deployment.extensions/nginx scaled
```
先ほどの Pod と Node の関係性を表示するコマンドで確認してみます。

```
$ kubectl get pod -o custom-columns=Pod:metadata.name,Node:spec.nodeName
Pod                    Node
nginx-5c7588df-77kvs   aks-nodepool1-38358128-vmss000001
nginx-5c7588df-9nwdd   aks-nodepool1-38358128-vmss000000
nginx-5c7588df-jbwjg   aks-nodepool1-38358128-vmss000002
nginx-5c7588df-jkwck   aks-nodepool1-38358128-vmss000000
nginx-5c7588df-rpdbl   aks-nodepool1-38358128-vmss000000
nginx-5c7588df-z64g6   aks-nodepool1-38358128-vmss000002
```

Cordon した Node には変更が加わっていないことが分かります。これが Cordon の効果です。Cordon されている Node は Uncordon コマンドでスケジュール対象に戻すことが可能です。

```
$ kubectl uncordon aks-nodepool1-38358128-vmss000001
node/aks-nodepool1-38358128-vmss000001 uncordoned

$ kubectl get node
NAME                                STATUS   ROLES   AGE   VERSION
aks-nodepool1-38358128-vmss000000   Ready    agent   15m   v1.13.12
aks-nodepool1-38358128-vmss000001   Ready    agent   15m   v1.13.12
aks-nodepool1-38358128-vmss000002   Ready    agent   15m   v1.13.12
```

#Drain
Drain では、先ほどの Cordon の動きに加えて、Pod の削除と再作成を行います。Cordon の様に CLI で実行できます。

```
$ kubectl drain aks-nodepool1-38358128-vmss000001
node/aks-nodepool1-38358128-vmss000001 cordoned
error: unable to drain node "aks-nodepool1-38358128-vmss000001", aborting command...

There are pending nodes to be drained:
 aks-nodepool1-38358128-vmss000001
error: cannot delete DaemonSet-managed Pods (use --ignore-daemonsets to ignore): kube-system/kube-proxy-d5pqv
```
この際エラーメッセージが出ています。デーモンセットがあるため、オプションを指定して実行してくださいと言わているため、その通りに実行してみます。

```
$ kubectl drain aks-nodepool1-38358128-vmss000001 --ignore-daemonsets
node/aks-nodepool1-38358128-vmss000001 already cordoned
WARNING: ignoring DaemonSet-managed Pods: kube-system/kube-proxy-d5pqv
evicting pod "nginx-5c7588df-77kvs"
pod/nginx-5c7588df-77kvs evicted
node/aks-nodepool1-38358128-vmss000001 evicted
```
この Drain では、`aks-nodepool1-38358128-vmss000001` に配置されている Pod を削除し、他の Node に再作成を行っています。先ほどの Pod と Node の関係性を確認するコマンドを実行して確認してます。

```
$ kubectl get pod -o custom-columns=Pod:metadata.name,Node:spec.nodeName
Pod                    Node
nginx-5c7588df-9nwdd   aks-nodepool1-38358128-vmss000000
nginx-5c7588df-jbwjg   aks-nodepool1-38358128-vmss000002
nginx-5c7588df-jkwck   aks-nodepool1-38358128-vmss000000
nginx-5c7588df-nhzfv   aks-nodepool1-38358128-vmss000002
nginx-5c7588df-rpdbl   aks-nodepool1-38358128-vmss000000
nginx-5c7588df-z64g6   aks-nodepool1-38358128-vmss000002
```
Drain で指定した `aks-nodepool1-38358128-vmss000001` には Pod が配置されていないことが確認できました。Node の状態も確認してみると、Cordon したときと同じように ScheduleDisable の状態になっていることが分かります。何も配置されていない Node なので正常にアップデートできる準備が整った、という事になります。

```
$ kubectl get node
NAME                                STATUS                     ROLES   AGE   VERSION
aks-nodepool1-38358128-vmss000000   Ready                      agent   22m   v1.13.12
aks-nodepool1-38358128-vmss000001   Ready,SchedulingDisabled   agent   22m   v1.13.12
aks-nodepool1-38358128-vmss000002   Ready                      agent   22m   v1.13.12
```

ここまでできればあとは Node をアップデートしたり、再起動することが出来ます。Azure CLI でできるので非常に簡単ですね！

#参考
Cluster Management
https://kubernetes.io/docs/tasks/administer-cluster/cluster-management/

Safely Drain a Node while Respecting the PodDisruptionBudget
https://kubernetes.io/docs/tasks/administer-cluster/safely-drain-node/