kubernetes
taints
tolerations

Kubernetesのtaintsとtolerationsについて

taintsとtolerationsについて

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

taintstolerationsnode selectornode affinityと似ているが性質は正反対で、
node selectornode affinityが特定のnodeに特定のpodをscheduleするための仕組みに対し、
taintstolerationsは特定のnodeにpodをscheduleしないための仕組みだ。
taintstolerationsは互いに協調しあい、podが不適当なnodeにscheduleされないようにする。
1つもしくは複数のtaintsをnodeに設定することができ、podには1つもしくは複数のtoleraitonsを設定することができる。
そしてtaintsにマッチする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:       node-role.kubernetes.io/master:NoSchedule

この場合、node-role.kubernetes.io/master:NoScheduleがTaints。
taintsは<key>=<value>:<effect>で構成されていて、
node-role.kubernetes.io/master がkey、
valueはnull、
effectがNoSchedule。
このnodeのtaintsを受け入れられるtolerationをもったpodでないとこのnodeへscheduleすることはできない。

$ kubectl describe pod kube-proxy-80wqm -n kube-system
...
Tolerations:    node-role.kubernetes.io/master=:NoSchedule
                node.alpha.kubernetes.io/notReady=:Exists:NoExecute
                node.alpha.kubernetes.io/unreachable=:Exists:NoExecute
...

node-role.kubernetes.io/master=:NoScheduleというtolerationをもっているので上記のnodeへscheduleできる。

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

podにtolerationsを設定する例

以下のように設定した場合、keyがnode-typeでvalueがproductionでeffectがNoScheduleのtaintsを持ったnodeにこのpodはscheduleできるようになる。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: prod
spec:
  replicas: 3
  template:
    metadata:
      name: prod-pod
    spec:
      containers:
      - image: busybox
      tolerations:
      - key: "node-type"
        operator: "Equal"
        value: "production"
        effect: "NoSchedule"

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

tolerations:
- key: "node-type"
  operator: "Exists"
  effect: "NoSchedule"

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

effectの種類とその効果

effectは以下の3種類がある。

  • NoSchedule
    • taintが許容できなければnodeへscheduleさせない
  • PreferNoSchedule
    • taintが許容できるnodeを探し、なければ許容できないnodeであってもscheduleする
  • NoExecute
    • scheduling時に影響があるNoSchedulePreferNoScheduleと違って、NoExecuteはnode上で実行中のpodへも影響がある。もしNoExecuteエフェクトをもったtaintをnodeへ追加した場合でかつpodがそのtaintを許容できない場合、そのpodはnode上から追い出される。

この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

参考