IBM Cloud：IBM Kubernetes Services(IKS)のOSアップデート方法 (Classic)

# はじめに
Kubernetes を使用しているとkubenetes componentだけでなく、しばしばnode OSのアップデートが必要になります。
一般的に、サーバーのOSをアップデートする際は同じマシン内で完結しますが、IKS では別のノードを用意し、上で動くワークロードを新しいノードに移すアプローチが必要です。

今回はclassic環境のIBM Kubernetes Servicesにおける、node OSをアップデートする手順を記載します。

# 検証環境
IBM Cloud上に以下のIKS clusterを作成しています。
Ubuntu18がEOSになるので、こちらをアップデートしていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2199676/0d32d1b5-adf7-c458-020c-bceb02bedbc0.png)

cluster には、 php のアプリケーションである`php` pod を作成しています。
アップデート後に正常にアクセスできるかをこちらで確認します。

```
$ kubectl get all
pod/php-6fc49798b5-bztmt
                   1/1     Running            0          53s   172.30.97.228   10.193.189.11    <none>           <none>
pod/php-6fc49798b5-mbkwt                   1/1     Running            0          53s   172.30.168.32   10.132.238.102   <none>           <none>

NAME                                TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE   SELECTOR
service/php-svc                     ClusterIP   172.21.86.195    <none>        80/TCP           8d    app=php-sample,run=php

NAME                                  READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS            IMAGES                  SELECTOR
deployment.apps/php                   2/2     2            2           4d22h   php-sample        sooo612/php-sample        app=php-sample,run=php

NAME                                  DESIRED   CURRENT   READY   AGE     CONTAINERS            IMAGES                 SELECTOR
replicaset.apps/php-6fc49798b5          2         2         2       4d22h   php-sample      sooo612/php-sample        app=php-sample,pod-template-hash=6fc49798b5,run=php
```

`php` pod へは CIS を経由してカスタムドメインでアクセスします。
カスタムドメイン用のingress を作成しています。
![Screen Shot 2023-04-20 at 15.34.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2199676/c43361ad-403a-81cf-2cdd-2c678d506ef1.png)


# OS アップデート手順
今回の検証は以下のIBM Cloud docs を元に進めています。
- IBM Cloud docs: [新しい Ubuntu バージョンへのマイグレーション](https://cloud.ibm.com/docs/containers?topic=containers-ubuntu-migrate&locale=ja)

docsではCLIベースで進めていますが、今回はGUIで実施したいと思います。
また、pod の移行については各クラスターの運用指針に従ってください。

大まかな手順は以下の通りです。
1. **新しいOSのノードをオーダーする。**
1. **Podを新しいノードへ退避させる**（アプリケーションの稼働場所を変える）
1. **古いOSのノードを削除する**

# 作業
## プール作成
クラスター内に、 Ubuntu 20 ワーカー・ノード用の新しいワーカー・プールを作成します。
cluster 詳細画面の「Worker pool」より、「追加」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2199676/4b858d9e-fab2-5823-adfc-634ddc6308d6.png)

現環境と同じnode数・スペックを選択します。

:::note warn
この時、現環境の worker pool と同じVLANを選択するようにしてください。
システム用pod の移行に失敗する可能性があります。
:::

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2199676/a8d0280d-8214-075d-de55-1dd87d527766.png width=70%>

ワーカー・プールが作成されたことを確認します。
(Worker nodeの設定が完了するまでには5~10分程度かかります)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2199676/6b0a4cc5-cb3c-5d96-32b5-02af3c95716d.png)

新しい node が作成されました。
```
kubectl get nodes -o wide
NAME             STATUS   ROLES    AGE   VERSION       INTERNAL-IP      EXTERNAL-IP       OS-IMAGE             KERNEL-VERSION       CONTAINER-RUNTIME
10.132.238.102   Ready    <none>   50d   v1.25.6+IKS   10.132.238.102   xx.xx.xx.xx     Ubuntu 18.04.6 LTS   4.15.0-204-generic   containerd://1.6.15
10.132.238.120   Ready    <none>   13m   v1.25.8+IKS   10.132.238.120   xx.xx.xx.xx     Ubuntu 20.04.6 LTS   5.4.0-139-generic    containerd://1.6.20
10.192.153.106   Ready    <none>   15m   v1.25.8+IKS   10.192.153.106   xx.xx.xx.xx   Ubuntu 20.04.6 LTS   5.4.0-139-generic    containerd://1.6.20
10.192.153.78    Ready    <none>   50d   v1.25.6+IKS   10.192.153.78    xx.xx.xx.xx   Ubuntu 18.04.6 LTS   4.15.0-204-generic   containerd://1.6.15
10.193.189.11    Ready    <none>   50d   v1.25.6+IKS   10.193.189.11    xx.xx.xx.xx     Ubuntu 18.04.6 LTS   4.15.0-204-generic   containerd://1.6.15
10.193.189.3     Ready    <none>   14m   v1.25.8+IKS   10.193.189.3     xx.xx.xx.xx     Ubuntu 20.04.6 LTS   5.4.0-139-generic    containerd://1.6.20
```


## Pod を新しい node にて稼働
古いノードで稼働している pod を退避させます。

:::note warn
こちらの pod 退避方法はサンプルになります。
pod の退避方法については、各クラスター運用に従ってください。
:::

```
$ kubectl drain 10.192.153.78 --ignore-daemonsets --force --delete-emptydir-data
node/10.192.153.78 cordoned
Warning: ignoring DaemonSet-managed Pods: ibm-observe/logdna-agent-8z9bt, ibm-observe/sysdig-agent-w7zsr, kube-system/calico-node-zbdlb, kube-system/ibm-keepalived-watcher-85xfk, kube-system/ibmcloud-block-storage-driver-wx2n7, kube-system/konnectivity-agent-l8x22
evicting pod kube-system/public-crcfu1rbht0h0hp43tnjeg-alb1-56946b8c98-wlpcv
evicting pod ibm-system/ibm-cloud-provider-ip-128-168-86-254-7fc4447879-phgpw
evicting pod ibm-system/ibm-cloud-provider-ip-10-192-46-146-644d4bf8d-v4h5r
evicting pod kube-system/private-crcfu1rbht0h0hp43tnjeg-alb2-68b8947fdf-xrmfc
evicting pod default/php-6fc49798b5-9qgnt
pod/ibm-cloud-provider-ip-10-192-46-146-644d4bf8d-v4h5r evicted
pod/ibm-cloud-provider-ip-128-168-86-254-7fc4447879-phgpw evicted
pod/php-6fc49798b5-9qgnt evicted
pod/private-crcfu1rbht0h0hp43tnjeg-alb2-68b8947fdf-xrmfc evicted
pod/public-crcfu1rbht0h0hp43tnjeg-alb1-56946b8c98-wlpcv evicted
node/10.192.153.78 drained

$ kubectl drain 10.132.238.102 --ignore-daemonsets --force --delete-emptydir-data
$ kubectl drain 10.193.189.11 --ignore-daemonsets --force --delete-emptydir-data

```
(参考) [Upgrading Linux nodes](https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/upgrading-linux-nodes/)


## アプリの稼働確認・接続テスト
アプリケーション pod の稼働を確認すると `Running`になっており、NODEが新しいIPアドレスになっていることが分かります。

```
$ kubectl get pods -o wide
NAME                       READY   STATUS    RESTARTS      AGE     IP              NODE             NOMINATED NODE   READINESS GATES
php-5bb97f8859-fntb7       1/1     Running      0          81s   172.30.81.138   10.192.153.106   <none>           <none>
php-5bb97f8859-rhdzv       1/1     Running      0          69s   172.30.181.74   10.132.238.120   <none>           <none>
```

接続確認します。同じドメインでアクセス出来ました！
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2199676/4a29bcc2-cadb-7706-a4b5-42d84f7a6c26.png width=80%>


## 古いワーカーノードの削除
Ubuntu 18 ワーカー・ノードが含まれている ワーカー・プールを削除します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2199676/e4997441-513e-198d-54ca-d3cb7125e0fc.png)

# よく使うKubernetesコマンド
本記事の一連の作業で使われるコマンドを備忘録として記載します。

## get
Kubernetesのコンポーネントの情報が欲しい場合はとりあえず `get` します。
`-A` オプションをつけると全ての `namespace` で検索します。（つけないとdefault namespace）
`-o` オプションは情報の出力形式を指定します。`-o yaml`だと省略を防げるので、デバッグ等にはおすすめです。

```
$ kubectl get pod (-A) (-o yaml) ：pod一覧を表示する
$ kubectl get node               ：
$ kubectl get svc                ：
```

## cordon/uncordon/drain 
これらのオプションは移行やバージョンアップで使用されることが多いオプションです。
スケジュール不可というのは、podを新規に作成される場合に、割り振り先として指定されることを防ぐ設定です。
例えばDeploymentsにてpodの定義や数を宣言している場合、podを削除しても元の数へ戻そうと自動でpodが復元されます。この復元するときにmater nodeが割り振り先を決めますが、割り振り先がノードが古いOSのノードだと新しいノードでの動作確認がいつまでもできません。そこでこれらのオプションを使用します。

```
$ kubectl cordon NODE1    ：ノードをスケジュール不可にする
$ kubectl uncordon NODE1  ：ノードをスケジュール可能にする
$ kubectl drain NODE1     ：ノードをスケジュール不可 & 稼働中のpodを別ノードへ退避させる
```
drainはpodの退避とスケジュール不可を設定するのに対し、cordonはスケジュール不可のみです。
以下のような環境は旧ノード１にdrainを実行した場合のpod移行先例です。旧ノード2にも一度はスケジュールできてしまいます。drain前に旧ノード2へcordonをしておくとpodの移動が新ノードに限定できます。
```     
 旧ノード1 (drain) -> pod退避 -> 新ノード
      　　↓
       pod退避 (cordonを実行しておくと防げる)
      　　↓
      旧ノード2
```