KubernetesのTaintsとTolerationsについて


TaintsとTolerationsについて

Kubernetesには最初、特定のnodeに特定のpodをscheduleする方法としてnode selectorを使う方法があった。(node selectorの他にも特定のnodeに特定のpodをscheduleするための仕組みとしてNode Affinityがある)

しかし後からもっと柔軟にscheduleするためのメカニズムが導入された。

それがTaintsTolerations

(taintは"汚れ"という意味。tolerationは"容認"という意味。つまり"汚れ"を"容認"できるならscheduleできる仕組み)

TaintsTolerationsNode SelectorNode Affinityと似ているが性質は正反対で、

Node SelectorNode Affinityが特定のnodeに特定のpodをscheduleするための仕組みに対し、

TaintsTolerationsは特定のnodeにpodをscheduleしないための仕組みだ。

TaintsTolerationsは互いに協調しあい、podが不適当なnodeにscheduleされないようにする。

Node Affinityなどの場合には、それらが未指定に場合はどこのNodeにでscheduleされてしまうが、TaintsTolerationsの場合は指定しない限りそのNodeにscheduleされることはない。

1つもしくは複数のTaintsをnodeに設定することができ、podには1つもしくは複数のtoleraitonsを設定することができる。

例えば、production用のNodeには他のPodをscheduleさせたくないとか、GPUやFPGAなどの特殊なデバイスを持つNodeには特定のPod以外はscheduleさせたくないとかいった場合に有効。

例えばCLIからnodeにTaintsを設定する場合、

kubectl taint nodes node1 key=value:effect

のような形式で実行する。

Taintskeyvalueeffectの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させない
(すでにscheduleされているPodはそのまま)

PreferNoSchedule
taintが許容できるnodeを探し、なければ許容できないnodeであってもscheduleする

NoExecute
scheduling時に影響があるNoSchedulePreferNoScheduleと違って、
NoExecuteはnode上で実行中のpodへも影響がある。
もしNoExecuteエフェクトをもったtaintをnodeへ追加した場合で
かつpodがそのtaintを許容できない場合、
その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できるようになる。

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ができる。

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に。

$ 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を作成

$ 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


参考