OKEのWorker Nodeにプリエンプティブル・インスタンスを利用する

# はじめに
OKE（Container Engine for Kubernetes）のWorker Nodeとして通常のオンデマンド・インスタンスの他に、プリエンプティブル・インスタンスも利用できるようになりました。

https://docs.oracle.com/ja-jp/iaas/Content/ContEng/Tasks/contengusingpreemptiblecapacity.htm

プリエンプティブル・インスタンスは、通常のオンデマンド・インスタンスとできることは同じですが、他のユーザがそのインスタンスのリソースを使用しようとした場合に、インスタンスが停止されてしまいます。
その代わり、オンデマンド・インスタントと比べてコストが半分になります。

AWSだとスポットインスタンスに相当するものですね。

https://docs.oracle.com/ja-jp/iaas/Content/Compute/Concepts/preemptible.htm#preemptible

# プリエンプティブル・インスタンスの設定
OKEのWorker Nodeにプリエンプティブル・インスタンスを使用する場合、クラスタの新規作成と追加の2通りがあります。

## 新規作成
新規作成時にプリエンプティブル・インスタンスを使用する場合は、カスタム作成で作る必要があります。

![スクリーンショット 2023-06-09 15.06.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/fed6ad1e-5e55-5424-0b9e-04f21a6a543f.png)

ノードプールの作成画面で`配置構成`の拡張オプションを表示させて、容量タイプで`プリエンプティブル容量`を選択します。

![スクリーンショット 2023-06-11 15.25.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/739e35d1-01bb-d2cd-61aa-67bce3102a3b.png)

## 追加
作成済みのクラスタにノードプールを追加する際に、プリエンプティブル・インスタンスを追加できます。

なお、ノードプール内にオンデマンド・インスタンスとプリエンプティブル・インスタンスを混在できますが、管理のことを考えると、ノードプール内ではインスタンスタイプを統一しておいた方がいいと思います。
また、一つ以上のノードプールはオンデマンド・インスタンスで構成した方が良いでしょう。マニュアルにもベストプラクティスとして書かれています。

今回はオンデマンド・インスタンスのノードプール（pool1）にプリエンプティブル・インスタンスのノードプール（pool2）を追加します。

![スクリーンショット 2023-06-11 13.56.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/72982d45-8578-36a6-1ab0-4fd1c3b7ee6d.png)

新規作成と同様に拡張オプションを表示させて、`プリエンプティブル容量`を選択します。

![スクリーンショット 2023-06-11 13.57.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/6b0d8ab1-8fdd-e58e-f420-bcd68015d676.png)

追加されたインスタンスをコンソールで確認すると、容量タイプが`プリエンプティブル`となっています。

![スクリーンショット 2023-06-11 14.04.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/e817a39f-e3da-8c52-3167-864a2fc7496f.png)

# 確認
クラスタ上でノードを確認します。
プリエンプティブル・インスタンスにはLabelとTaintが設定されています。

```
$ k describe nodes 10.0.10.216 |grep preemptible
                    oci.oraclecloud.com/oke-is-preemptible=true
Taints:             oci.oraclecloud.com/oke-is-preemptible:NoSchedule
$ k get node -L oci.oraclecloud.com/oke-is-preemptible
NAME          STATUS   ROLES   AGE     VERSION   OKE-IS-PREEMPTIBLE
10.0.10.152   Ready    node    44h     v1.26.2   
10.0.10.216   Ready    node    7m47s   v1.26.2   true
```

## Podのデプロイ
DeploymentでPodをデプロイします。
Taintが設定されているので、オンデマンド・インスタンスの方だけにPodがデプロイされていますね。

```
$ k create deployment nginx --image nginx --replicas 4
deployment.apps/nginx created
$ k get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP            NODE          NOMINATED NODE   READINESS GATES
nginx-748c667d99-cxgw7   1/1     Running   0          12s   10.244.0.12   10.0.10.152   <none>           <none>
nginx-748c667d99-g5qd6   1/1     Running   0          13s   10.244.0.9    10.0.10.152   <none>           <none>
nginx-748c667d99-gl5n7   1/1     Running   0          12s   10.244.0.10   10.0.10.152   <none>           <none>
nginx-748c667d99-jmjqq   1/1     Running   0          12s   10.244.0.11   10.0.10.152   <none>           <none>
```

一旦削除します。

```
$ k delete deployments.apps nginx 
deployment.apps "nginx" deleted
```

Tolerationを設定して、プリエンプティブル・インスタンスにもPodがデプロイされるようにします。

```nginx-tolerations.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: nginx-tolerations
  name: nginx-tolerations
spec:
  replicas: 4
  selector:
    matchLabels:
      app: nginx-tolerations
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx-tolerations
    spec:
      containers:
      - image: nginx
        name: nginx
        resources: {}
      tolerations:
      - key: "oci.oraclecloud.com/oke-is-preemptible"
        operator: "Exists"
        effect: "NoSchedule"
status: {}
```

```
$ k apply -f nginx-tolerations.yaml 
deployment.apps/nginx-tolerations created
$ k get pod -o wide
NAME                                 READY   STATUS    RESTARTS   AGE   IP             NODE          NOMINATED NODE   READINESS GATES
nginx-tolerations-6f5b554fbc-cxwfb   1/1     Running   0          24s   10.244.0.134   10.0.10.216   <none>           <none>
nginx-tolerations-6f5b554fbc-dq425   1/1     Running   0          24s   10.244.0.133   10.0.10.216   <none>           <none>
nginx-tolerations-6f5b554fbc-mxsvg   1/1     Running   0          24s   10.244.0.14    10.0.10.152   <none>           <none>
nginx-tolerations-6f5b554fbc-wwghl   1/1     Running   0          24s   10.244.0.15    10.0.10.152   <none>           <none>
```

今度はオンデマンド・インスタンス、プリエンプティブル・インスタンス両方にデプロイされましたね。

# まとめ

プリエンプティブル・インスタンスはいつ落ちるかわかりませんので、インスタンスが落ちても業務が停止しないようにするには、クラスタの設計やDeploymentのreplicasの設定などに注意する必要がありますね。
とはいえ、物理環境に依存することなくコンテナを配置するのはCloud Nativeの基本といえば基本ですので、プリエンプティブル・インスタンスをうまく活用してコストを下げるのはアリかなと思います。

あと、プリエンプティブル・インスタンスがどのくらいの頻度で落ちるか気になるところですが、まだ落ちていないのでわからないですね。落ちたらクラスタやPodがどうなったか更新しようと思います。

# （追記）落ちた時の動作
クラスタを作成して約24時間で2回落ちました。
これはリージョン（今回はLondonリージョン）やタイミングにも依存すると思いますので、あくまで一例として捉えていただければと思います。

こちらはプリエンプティブル・インスタンス停止と再作成のログです。

![スクリーンショット 2023-06-12 14.20.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/6948af9b-d8b2-dc64-3d46-9dd45b5d92e3.png)

一番下の`21：41`の停止は一回で再作成されました。
この場合は以下のように`21:38`に停止して、`21:41`に再作成が完了していますので、約3分ほど縮退していたことになります。

![スクリーンショット 2023-06-12 14.19.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/7f7ed0fa-039b-1302-86bf-180d861eac1d.png)

2回目は`3:08`に停止しましたが、何度か再作成が失敗して、約2時間後に再作成が成功しています。失敗するたびに、再作成を開始する間隔が開くようです。

また、1分間隔でNodeとPodの状態を吐き出していました。
これをみると、プリエンプティブル・インスタンスを削除する前にdrainしてPodを逃してから、スケジューリング対象から外して、ノードを削除していることがわかります。

```
----
Mon Jun 12 03:07:30 GMT 2023
NAME          STATUS   ROLES   AGE     VERSION
10.0.10.152   Ready    node    2d18h   v1.26.2
10.0.10.175   Ready    node    5h26m   v1.26.2
NAME                                 READY   STATUS    RESTARTS   AGE    IP            NODE          NOMINATED NODE   READINESS GATES
nginx-tolerations-6f5b554fbc-4tltw   1/1     Running   0          4h4m   10.244.1.10   10.0.10.175   <none>           <none>
nginx-tolerations-6f5b554fbc-5hmvf   1/1     Running   0          4h4m   10.244.1.9    10.0.10.175   <none>           <none>
nginx-tolerations-6f5b554fbc-86wrj   1/1     Running   0          4h4m   10.244.0.2    10.0.10.152   <none>           <none>
nginx-tolerations-6f5b554fbc-pglxz   1/1     Running   0          4h4m   10.244.0.3    10.0.10.152   <none>           <none>
----
Mon Jun 12 03:08:41 GMT 2023
NAME          STATUS                     ROLES   AGE     VERSION
10.0.10.152   Ready                      node    2d18h   v1.26.2
10.0.10.175   Ready,SchedulingDisabled   node    5h27m   v1.26.2
NAME                                 READY   STATUS    RESTARTS   AGE    IP           NODE          NOMINATED NODE   READINESS GATES
nginx-tolerations-6f5b554fbc-76b28   1/1     Running   0          58s    10.244.0.4   10.0.10.152   <none>           <none>
nginx-tolerations-6f5b554fbc-86wrj   1/1     Running   0          4h5m   10.244.0.2   10.0.10.152   <none>           <none>
nginx-tolerations-6f5b554fbc-pglxz   1/1     Running   0          4h5m   10.244.0.3   10.0.10.152   <none>           <none>
nginx-tolerations-6f5b554fbc-pv26h   1/1     Running   0          58s    10.244.0.5   10.0.10.152   <none>           <none>

・・・

----
Mon Jun 12 03:12:12 GMT 2023
NAME          STATUS   ROLES   AGE     VERSION
10.0.10.152   Ready    node    2d19h   v1.26.2
NAME                                 READY   STATUS    RESTARTS   AGE     IP           NODE          NOMINATED NODE   READINESS GATES
nginx-tolerations-6f5b554fbc-76b28   1/1     Running   0          4m30s   10.244.0.4   10.0.10.152   <none>           <none>
nginx-tolerations-6f5b554fbc-86wrj   1/1     Running   0          4h9m    10.244.0.2   10.0.10.152   <none>           <none>
nginx-tolerations-6f5b554fbc-pglxz   1/1     Running   0          4h9m    10.244.0.3   10.0.10.152   <none>           <none>
nginx-tolerations-6f5b554fbc-pv26h   1/1     Running   0          4m30s   10.244.0.5   10.0.10.152   <none>           <none>
```