KubernetesのTaintsとTolerationsについて

# TaintsとTolerationsについて

Kubernetesには最初、特定のnodeに特定のpodをscheduleする方法としてnode selectorを使う方法があった。(node selectorの他にも特定のnodeに特定のpodをscheduleするための仕組みとしてNode Affinityがある)
しかし後からもっと柔軟にscheduleするためのメカニズムが導入された。
それが`Taints`と`Tolerations`。
(taintは"汚れ"という意味。tolerationは"容認"という意味。つまり"汚れ"を"容認"できるならscheduleできる仕組み)

`Taints`と`Tolerations`は`Node Selector`や`Node Affinity`と似ているが性質は正反対で、
`Node Selector`や`Node Affinity`が特定のnodeに特定のpodをscheduleするための仕組みに対し、
`Taints`と`Tolerations`は特定のnodeにpodをscheduleしないための仕組みだ。
`Taints`と`Tolerations`は互いに協調しあい、podが不適当なnodeにscheduleされないようにする。
Node Affinityなどの場合には、それらが未指定の場合はどこのNodeにでもscheduleされてしまうが、`Taints`と`Tolerations`の場合は指定しない限りそのNodeにscheduleされることはない。
1つもしくは複数の`Taints`をnodeに設定することができ、podには1つもしくは複数の`tolerations`を設定することができる。
例えば、production用のNodeには他のPodをscheduleさせたくないとか、GPUやFPGAなどの特殊なデバイスを持つNodeには特定のPod以外はscheduleさせたくないとかいった場合に有効。

例えばCLIからnodeに`Taints`を設定する場合、
`kubectl taint nodes node1 key=value:effect`
のような形式で実行する。
`Taints`は`key`と`value`と`effect`の3つで構成されている。
(effectの説明は後でするとして)もし以下のように実行した場合、
`kubectl taint nodes node1 app=batch:NoSchedule`
`Tolerations`にapp=batchをもったpodではないとこのnodeにpodをscheduleできなくなる。

例えば、

```
$ kubectl describe node master.k8s
Name:         master.k8s
Role:
Labels:       beta.kubernetes.io/arch=amd64
              beta.kubernetes.io/os=linux
              kubernetes.io/hostname=master.k8s
              node-role.kubernetes.io/master=
Annotations:  node.alpha.kubernetes.io/ttl=0
              volumes.kubernetes.io/controller-managed-attach-detach=true
Taints:       app=batch:NoSchedule
```

この場合、`app=batch:NoSchedule`がTaints。
Taintsは`<key>=<value>:<effect>`で構成されていて、
`app` がkey、valueは`batch`、effectが`NoSchedule`。

### effectの種類とその効果

effectには以下の3種類がある。(これらのeffectはTaintsでもTolerationsでも使用できる)

| Effectの種類 | 概要 |
|:---|:-:|
| NoSchedule | taintが許容できなければnodeへscheduleさせない<br>(すでにscheduleされているPodはそのまま) | 
| PreferNoSchedule | taintが許容できるnodeを探し、なければ許容できないnodeであってもscheduleする  |
| NoExecute | scheduling時に影響がある`NoSchedule`や`PreferNoSchedule`と違って、<br>`NoExecute`はnode上で実行中のpodへも影響がある。<br>もし`NoExecute`エフェクトをもったtaintをnodeへ追加した場合で<br>かつpodがそのtaintを許容できない場合、<br>そのpodは停止される(つまりnode上から追い出される )。  |



```
$ kubectl describe pod hoge-pod
...
Tolerations:    app=batch:NoSchedule
...
```

は`app=batch:NoSchedule`というTolerationをもっているので上記のnodeへscheduleできる。

ちなみに`node.alpha.kubernetes.io/notReady=:Exists:NoExecute`というTolerationsの場合、`:Exists`は、値がnullではないことを指す。


# podにTolerationsを設定する例

以下のように設定した場合、keyが`app`でvalueが`batch`でeffectが`NoSchedule`のTaintsを持ったnodeにこのpodはscheduleできるようになる。

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: batch
spec:
  replicas: 3
  template:
    metadata:
      name: batch-pod
    spec:
      containers:
      - image: busybox
      tolerations:
      - key: "app"
        operator: "Equal"
        value: "batch"
        effect: "NoSchedule"
```

また以下のようにvalueを指定しないこともできる。この場合、このkeyとこのeffectが同じTaintsを持ったnodeにscheduleができる。

```yaml
tolerations:
- key: "app"
  operator: "Exists"
  effect: "NoSchedule"
```

なおoperatorを省略した場合、`Equal`がデフォルトとして設定される



このTaints/Tolerationsの分かりやすい使用例としては、production nodeに non-productionなpodをscheduleしたくない場合がある。

# 実際に試してみる

まずnodeが2台ある

```
$ kubectl get node
NAME                                     STATUS    ROLES     AGE       VERSION
gke-wdpress-default-pool-1a81bfcb-v7rq   Ready     <none>    16m       v1.8.8-gke.0
gke-wdpress-default-pool-1a81bfcb-w0tj   Ready     <none>    16m       v1.8.8-gke.0
```

それぞれのnodeにTaintsを設定する。片方を`node-type=production`に、もう片方を`node-type=development`に。

```bash
$ kubectl taint nodes gke-wdpress-default-pool-1a81bfcb-v7rq node-type=production:NoSchedule
$ kubectl taint nodes gke-wdpress-default-pool-1a81bfcb-w0tj node-type=development:NoSchedule
```

tolerationを設定したpod yamlを作成

```yaml
$ cat toleration-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: toleration-pod
spec:
  containers:
  - name: main
    image: alpine
    command: ["sleep", "9999999"]
  tolerations:
  - key: "node-type"
    operator: "Equal"
    value: "production"
    effect: "NoSchedule"
```

podを作成する。

```
$ kubectl create -f toleration-pod.yaml
pod "toleration-pod" created
```

`node-type=production`を設定したnodeのほうにscheduleされていることが分かる

```
$ kubectl get pod -o wide
NAME             READY     STATUS    RESTARTS   AGE       IP          NODE
toleration-pod   1/1       Running   0          5s        10.24.3.8   gke-wdpress-default-pool-1a81bfcb-v7rq
```

しかしこれだとたまたま`node-type=production`を設定したnodeのほうにscheduleされた可能性があるので、
`node-type=production`を設定したnodeをpodがscheduleされない状態にする。

```
$ kubectl cordon gke-wdpress-default-pool-1a81bfcb-v7rq
node "gke-wdpress-default-pool-1a81bfcb-v7rq" cordoned
```

`SchedulingDisabled`になっていることを確認

```
$ kubectl get node
NAME                                     STATUS                     ROLES     AGE       VERSION
gke-wdpress-default-pool-1a81bfcb-v7rq   Ready,SchedulingDisabled   <none>    14m       v1.8.8-gke.0
gke-wdpress-default-pool-1a81bfcb-w0tj   Ready                      <none>    14m       v1.8.8-gke.0
```

いったんpodを削除し

```
$ kubectl delete -f curl_pod_toleration.yaml
pod "toleration-pod" deleted
```

そしてpodを作成する

```
$ kubectl create -f curl_pod_toleration.yaml
pod "toleration-pod" created
```

片方のnode(`node-type=development`)のTaintsはこのpodは受け入れられないのでscheduleできない。
もう片方のnode(`node-type=production`)は`SchedulingDisabled`状態なのでscheduleできない。
そのためpodはscheduleされずPending状態になる。

```

$ kubectl get pod -o wide
NAME             READY     STATUS    RESTARTS   AGE       IP        NODE
toleration-pod   0/1       Pending   0          3s        <none>    <none>
```

podがscheduleされない状態にしたnodeをscheduleされる状態に戻す

```
$ kubectl uncordon gke-wdpress-default-pool-1a81bfcb-v7rq
node "gke-wdpress-default-pool-1a81bfcb-v7rq" uncordoned

$ kubectl get node
NAME                                     STATUS    ROLES     AGE       VERSION
gke-wdpress-default-pool-1a81bfcb-v7rq   Ready     <none>    16m       v1.8.8-gke.0
gke-wdpress-default-pool-1a81bfcb-w0tj   Ready     <none>    16m       v1.8.8-gke.0
```

少し待つとpodがPending状態からRunning状態に変わる。つまりscheduleされた。

```
$ kubectl get pod -o wide
NAME             READY     STATUS    RESTARTS   AGE       IP          NODE
toleration-pod   1/1       Running   0          36s       10.24.3.9   gke-wdpress-default-pool-1a81bfcb-v7rq
```


# 参考

* [Kubernetes完全ガイド](https://www.amazon.co.jp/dp/B07HFS7TDT/)(最高に分かりやすく書かれているのでぜひ買いましょう)
* [Kubernetes in Action](http://amzn.asia/80rSDOx) 
* [Taints and Tolerations
](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/)