KubernetesのTaint / Tolerationの使い方

## Taint / Tolerationとは
Taintとは、PodをNodeへスケジューリングさせないための機能である。
[Node Affinity](https://qiita.com/Esfahan/items/aeddf89428441ec3437c)とは逆の機能になる。

TaintsをNodeに設定し、PodにはTolerationsを設定することができる。
これらにはkey, valueなどを設定し、TaintsとTolerationsの条件がマッチした際に、PodはNodeにスケジューリングされるようになる。
それぞれ１つ、または複数のTaints、Tolerationsを設定することができる。

## 目次
- [Kubernetesハンズオン目次](https://qiita.com/Esfahan/items/4fcf51622fed20dffb88)

## Taints
Taintsは、以下の３つを使って条件をセットする。

- key
- value
- effect

例えば、`key: app`、`value: batch`、`effect: NoSchedule`というように、Node、Podにそれぞれ設定する。key, valueはそのままセットした値が条件比較で使われる。effectについては次項で説明する。

## effectとは
effectとは、条件をどのように受け入れるのかの制約である。
effectは以下の３種類がある。

- NoSchedule
    - TaintとTolerationがマッチした場合にのみに、PodがNodeにスケジューリングされる。
- PreferNoSchedule
    - Nodeは条件が一致しないPodを基本的には拒否するが、該当のPodがどのNodeにもスケジューリングされず行き場所がなくなっている場合、PreferNoScheduleが設定されたNodeは、条件にマッチしなくてもそのPodを受け入れてスケジューリングする。
- NoExecute
    - TaintとTolerationがマッチした場合にのみに、PodがNodeにスケジューリングされる。
    - このTaintがNodeにセットされたときに、条件に合わないPodが既にスケジューリングされていた場合、そのPodはただちにNodeから追い出される。つまり、起動中のPodにも影響が出る。

説明するのが中々難しいので、実際に検証してみる。

## ハンズオン
以下のminikubeというnodeにTaintを設定し、Tolerationを設定したPodをスケジューリングしてみる。

```shell-session
$ sudo kubectl get nodes
NAME       STATUS   ROLES    AGE   VERSION
minikube   Ready    master   3d    v1.14.3
```

## NoScheduleの挙動を検証
### Taintを設定前の状態
nodeをdescribeしてみると、Taintsが`<none>`になっている。

```shell-session
$ sudo kubectl describe node minikube | grep Taints
Taints:             <none>
```

### Taintを設定
`key: app`、`value: batch`、`effect: NoSchedule`というTaintを設定する。

```shell-session
$ sudo kubectl taint nodes minikube app=batch:NoSchedule
node/minikube tainted
```

### Taintを設定後の状態
nodeをdescribeすると、Taintsが設定されていることが確認できる。

```shell-session
$ sudo kubectl describe nodes minikube | grep Taints
Taints:             app=batch:NoSchedule
```

### Tolerationの設定

```yaml:pod.yml
apiVersion: v1
kind: Pod
metadata:
  name: sample-pod
spec:
  containers:
   - name: sample-container
     image: nginx
  tolerations:
    # TaintのKeyを指定
    - key: "app"
      # Equal or Exists。Default: Equal。
      operator: "Equal"
      # TaintのValueを指定。operatorがExistsの場合、valueは省略可能。
      # operatorにExistsを設定した場合、keyとeffectのみが一致したNodeを探すことになる。
      value: "batch"
      # Effectを設定。省略した場合、全てのEffectにマッチする。
      effect: "NoSchedule"
      # effectにNoExecuteを設定した場合、PodがNodeから追い出されるまでの時間を設定する。
      # 0や負の数を設定した場合は即座に追い出される。
      #tolerationSeconds: 0
```

### 条件が一致した場合
Podがスケジューリングされていることを確認する。

```shell-session
$ sudo kubectl get pod
NAME         READY   STATUS    RESTARTS   AGE
sample-pod   1/1     Running   0          4s
```

```shell-session
$ sudo kubectl describe pod sample-pod
# **snip**
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  25s   default-scheduler  Successfully assigned default/sample-pod to minikube
  Normal  Pulling    25s   kubelet, minikube  Pulling image "nginx"
  Normal  Pulled     22s   kubelet, minikube  Successfully pulled image "nginx"
  Normal  Created    22s   kubelet, minikube  Created container sample-container
  Normal  Started    22s   kubelet, minikube  Started container sample-container
```

### 条件が一致しない場合
Nodeに`app=batch:NoSchedule`というTaintを設定、
Podに`app=api:NoSchedule`というTolerationを設定した場合、Podはスケジューリング先のNodeが存在しないため、以下のようになる。


```shell-session
$ sudo kubectl get pod
NAME         READY   STATUS    RESTARTS   AGE
sample-pod   0/1     Pending   0          10s
```

```shell-session
$ sudo kubectl describe pod sample-pod
# **snip**
Events:
  Type     Reason            Age              From               Message
  ----     ------            ----             ----               -------
  Warning  FailedScheduling  5s (x2 over 5s)  default-scheduler  0/1 nodes are available: 1 node(s) had taints that the pod didn't tolerate.
```

## PreferNoScheduleの挙動を検証
### NodeにTaintの設定
`app=batch:PreferNoSchedule`というTaintをminikube Nodeに設定する。

```shell-session
$ sudo kubectl taint nodes minikube app=batch:PreferNoSchedule
node/minikube tainted
```

確認

```shell-session
$ sudo kubectl describe node minikube | grep Taint
Taints:             app=batch:PreferNoSchedule
```

### PodにTolerationの設定
以下のTolerationをPodに設定する。

```yaml:pod.yml
apiVersion: v1
kind: Pod
metadata:
  name: sample-pod
spec:
  containers:
   - name: sample-container
     image: nginx
  tolerations:
    # TaintのKeyを指定
    - key: "sample"
      # Equal or Exists。Default: Equal。
      operator: "Equal"
      # TaintのValueを指定。operatorがExistsの場合、valueは省略可能。
      value: "ThisIsASample"
      # Effectを設定。省略した場合、全てのEffectにマッチする。
      effect: "NoSchedule"
```

apply

````shell-session
$ sudo kubectl apply -f pod.yml
pod/sample-pod configured
```

### スケジューリング状態確認
Taintは`app=batch:PreferNoSchedule`、PodのTolerationsは`sample=ThisIsASample:NoSchedule`で一致していないが、このPodは他に行き場所がないため、minikubeのNodeにスケジューリングされた。

```shell-session
$ sudo kubectl get pod
NAME         READY   STATUS    RESTARTS   AGE
sample-pod   1/1     Running   0          14s
```

```shell-session
$ sudo kubectl describe pod sample-pod
# **snip**
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  25s   default-scheduler  Successfully assigned default/sample-pod to minikube
  Normal  Pulling    24s   kubelet, minikube  Pulling image "nginx"
  Normal  Pulled     21s   kubelet, minikube  Successfully pulled image "nginx"
  Normal  Created    21s   kubelet, minikube  Created container sample-container
  Normal  Started    21s   kubelet, minikube  Started container sample-container
```

## NoExecuteの挙動を検証
先述の[PreferNoScheduleの挙動を検証](#prefernoscheduleの挙動を検証)の環境をそのまま利用する。

### 作業前のNodeを確認
先程セットしたTaintがNodeにセットされていることを確認。

```shell-session
$ sudo kubectl describe node minikube | grep Taints
Taints:             app=batch:PreferNoSchedule
```

### 作業前のPodを確認
先程作成したPodが起動していることを確認。

```shell-session
$ sudo kubectl get pod
NAME         READY   STATUS    RESTARTS   AGE
sample-pod   1/1     Running   0          52s
```

### Taintを削除
先程セットしたTaintを削除する。

```shell-session
$ sudo kubectl taint nodes minikube app-
node/minikube untainted
```

### Taintを設定
minikubeのNodeに`app=batch:NoExecute`というTaintを設定する。

```shell-session
$ sudo kubectl taint nodes minikube app=batch:NoExecute
node/minikube tainted
```

### 作業後のNodeを確認

```shell-session
$ sudo kubectl describe node minikube | grep Taints
Taints:             app=batch:NoExecute
```

### 作業後のPodを確認
先述の[PreferNoScheduleの挙動を検証](#prefernoscheduleの挙動を検証)で作成したPodは、条件に合わないため、新たな`NoExecute`のTaintをセットしたら、ただちに破棄された。

```shell-session
$ sudo kubectl get pod
NAME         READY   STATUS        RESTARTS   AGE
sample-pod   0/1     Terminating   0          83s
```

ちなみに、Taintを`NoSchedule`もしくは`PreferNoSchedule`にセットし直した場合は、既存のPodは破棄されなかった。

```shell-session
$ sudo kubectl taint nodes minikube app=batch:NoSchedule
$ sudo kubectl taint nodes minikube app=batch:PreferNoSchedule
```

## 関連記事
- [【KubernetesのAffinity】その１ - Node Affinityの使い方](https://qiita.com/Esfahan/items/aeddf89428441ec3437c)
- [【KubernetesのAffinity】その２ - Pod Affinity / Pod Anti-Affinityの使い方](https://qiita.com/Esfahan/items/a673317a29ca407e5ae7)

## 参考
- [Taints and Tolerations](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/)
- [Kubernetes道場 18日目 - Affinity / Anti-Affinity / Taint / Tolerationについて](https://cstoku.dev/posts/2018/k8sdojo-18/#taint-toleration)
- [KubernetesのTaintsとTolerationsについて](https://qiita.com/sheepland/items/8fedae15e157c102757f)