TaintとToleration

NodeアフィニティPodの属性であり、あるNode群を引きつけます(優先条件または必須条件)。反対に taint はNodeがある種のPodを排除できるようにします。

toleration はPodに適用され、一致するtaintが付与されたNodeへPodがスケジューリングされることを認めるものです。ただしそのNodeへ必ずスケジューリングされるとは限りません。

taintとtolerationは組になって機能し、Podが不適切なNodeへスケジューリングされないことを保証します。taintはNodeに一つまたは複数個付与することができます。これはそのNodeがtaintを許容しないPodを受け入れるべきではないことを示します。

コンセプト

Nodeにtaintを付与するにはkubectl taintコマンドを使用します。 例えば、次のコマンドは

kubectl taint nodes node1 key1=value1:NoSchedule

node1にtaintを設定します。このtaintのキーはkey1、値はvalue1、taintの効果はNoScheduleです。 これはnode1にはPodに合致するtolerationがなければスケジューリングされないことを意味します。

上記のコマンドで付与したtaintを外すには、下記のコマンドを使います。

kubectl taint nodes node1 key1=value1:NoSchedule-

PodのtolerationはPodSpecの中に指定します。下記のtolerationはどちらも、上記のkubectl taintコマンドで追加したtaintと合致するため、どちらのtolerationが設定されたPodもnode1へスケジューリングされることができます。

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"
tolerations:
- key: "key1"
  operator: "Exists"
  effect: "NoSchedule"

tolerationを設定したPodの例を示します。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  tolerations:
  - key: "example-key"
    operator: "Exists"
    effect: "NoSchedule"

operatorのデフォルトはEqualです。

tolerationがtaintと合致するのは、keyeffectが同一であり、さらに下記の条件のいずれかを満たす場合です。

  • operatorExistsvalueを指定すべきでない場合)
  • operatorEqualであり、かつvalueが同一である場合
備考:

2つ特殊な場合があります。

空のkeyと演算子Existsは全てのkeyvalueeffectと一致するため、すべてのtaintと合致します。

空のeffectkey1が一致する全てのeffectと合致します。

上記の例ではeffectNoScheduleを指定しました。代わりに、effectPreferNoScheduleを指定することができます。 これはNoScheduleの「ソフトな」バージョンであり、システムはtaintに対応するtolerationが設定されていないPodがNodeへ配置されることを避けようとしますが、必須の条件とはしません。3つ目のeffectの値としてNoExecuteがありますが、これについては後述します。

同一のNodeに複数のtaintを付与することや、同一のPodに複数のtolerationを設定することができます。 複数のtaintやtolerationが設定されている場合、Kubernetesはフィルタのように扱います。最初はNodeの全てのtaintがある状態から始め、Podが対応するtolerationを持っているtaintは無視され外されていきます。無視されずに残ったtaintが効果を及ぼします。 具体的には、

  • effect NoScheduleのtaintが無視されず残った場合、KubernetesはそのPodをNodeへスケジューリングしません。
  • effect NoScheduleのtaintは残らず、effect PreferNoScheduleのtaintは残った場合、KubernetesはそのNodeへのスケジューリングをしないように試みます。
  • effect NoExecuteのtaintが残った場合、既に稼働中のPodはそのNodeから排除され、まだ稼働していないPodはスケジューリングされないようになります。

例として、下記のようなtaintが付与されたNodeを考えます。

kubectl taint nodes node1 key1=value1:NoSchedule
kubectl taint nodes node1 key1=value1:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule

Podには2つのtolerationが設定されています。

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"

この例では、3つ目のtaintと合致するtolerationがないため、PodはNodeへはスケジューリングされません。 しかし、これらのtaintが追加された時点で、そのNodeでPodが稼働していれば続けて稼働することが可能です。 これは、Podのtolerationと合致しないtaintは3つあるtaintのうちの3つ目のtaintのみであり、それがNoScheduleであるためです。

一般に、effect NoExecuteのtaintがNodeに追加されると、合致するtolerationが設定されていないPodは即時にNodeから排除され、合致するtolerationが設定されたPodが排除されることは決してありません。 しかし、effectNoExecuteに対するtolerationはtolerationSecondsフィールドを任意で指定することができ、これはtaintが追加された後にそのNodeにPodが残る時間を示します。例えば、

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"
  tolerationSeconds: 3600

この例のPodが稼働中で、対応するtaintがNodeへ追加された場合、PodはそのNodeに3600秒残り、その後排除されます。仮にtaintがそれよりも前に外された場合、Podは排除されません。

ユースケースの例

taintとtolerationは、実行されるべきではないNodeからPodを遠ざけたり、排除したりするための柔軟な方法です。いくつかのユースケースを示します。

  • 専有Node: あるNode群を特定のユーザーに専有させたい場合、そのNode群へtaintを追加し(kubectl taint nodes nodename dedicated=groupName:NoSchedule) 対応するtolerationをPodへ追加します(これを実現する最も容易な方法はカスタム アドミッションコントローラーを書くことです)。 tolerationが設定されたPodはtaintの設定された(専有の)Nodeと、クラスターにあるその他のNodeの使用が認められます。もしPodが必ず専有Nodeのみを使うようにしたい場合は、taintと同様のラベルをそのNode群に設定し(例: dedicated=groupName)、アドミッションコントローラーはNodeアフィニティを使ってPodがdedicated=groupNameのラベルの付いたNodeへスケジューリングすることが必要であるということも設定する必要があります。

  • 特殊なハードウェアを備えるNode: クラスターの中の少数のNodeが特殊なハードウェア(例えばGPU)を備える場合、そのハードウェアを必要としないPodがスケジューリングされないようにして、後でハードウェアを必要とするPodができたときの余裕を確保したいことがあります。 これは特殊なハードウェアを持つNodeにtaintを追加(例えば kubectl taint nodes nodename special=true:NoSchedule または kubectl taint nodes nodename special=true:PreferNoSchedule)して、ハードウェアを使用するPodに対応するtolerationを追加することで可能です。 専有Nodeのユースケースと同様に、tolerationを容易に適用する方法はカスタム アドミッションコントローラーを使うことです。 例えば、特殊なハードウェアを表すために拡張リソース を使い、ハードウェアを備えるNodeに拡張リソースの名称のtaintを追加して、 拡張リソースtoleration アドミッションコントローラーを実行することが推奨されます。Nodeにはtaintが付与されているため、tolerationのないPodはスケジューリングされません。しかし拡張リソースを要求するPodを作成しようとすると、拡張リソースtoleration アドミッションコントローラーはPodに自動的に適切なtolerationを設定し、Podはハードウェアを備えるNodeへスケジューリングされます。 これは特殊なハードウェアを備えたNodeではそれを必要とするPodのみが稼働し、Podに対して手作業でtolerationを追加しなくて済むようにします。

  • taintを基にした排除: Nodeに問題が起きたときにPodごとに排除する設定を行うことができます。次のセクションにて説明します。

taintを基にした排除

FEATURE STATE: Kubernetes v1.18 [stable]

上述したように、effect NoExecuteのtaintはNodeで実行中のPodに次のような影響を与えます。

  • 対応するtolerationのないPodは即座に除外される
  • 対応するtolerationがあり、それにtolerationSecondsが指定されていないPodは残り続ける
  • 対応するtolerationがあり、それにtolerationSecondsが指定されているPodは指定された間、残される

Nodeコントローラーは特定の条件を満たす場合に自動的にtaintを追加します。 組み込まれているtaintは下記の通りです。

  • node.kubernetes.io/not-ready: Nodeの準備ができていない場合。これはNodeCondition ReadyFalseである場合に対応します。
  • node.kubernetes.io/unreachable: NodeがNodeコントローラーから到達できない場合。これはNodeConditionReadyUnknownの場合に対応します。
  • node.kubernetes.io/out-of-disk: Nodeのディスクの空きがない場合。
  • node.kubernetes.io/memory-pressure: Nodeのメモリーが不足している場合。
  • node.kubernetes.io/disk-pressure: Nodeのディスクが不足している場合。
  • node.kubernetes.io/network-unavailable: Nodeのネットワークが利用できない場合。
  • node.kubernetes.io/unschedulable: Nodeがスケジューリングできない場合。
  • node.cloudprovider.kubernetes.io/uninitialized: kubeletが外部のクラウド事業者により起動されたときに設定されるtaintで、このNodeは利用不可能であることを示します。cloud-controller-managerによるコントローラーがこのNodeを初期化した後にkubeletはこのtaintを外します。

Nodeから追い出すときには、Nodeコントローラーまたはkubeletは関連するtaintをNoExecute効果の状態で追加します。 不具合のある状態から通常の状態へ復帰した場合は、kubeletまたはNodeコントローラーは関連するtaintを外すことができます。

備考: コントロールプレーンは新しいtaintをNodeに加えるレートを制限しています。 このレート制限は一度に多くのNodeが到達不可能になった場合(例えばネットワークの断絶)に、退役させられるNodeの数を制御します。

PodにtolerationSecondsを指定することで不具合があるか応答のないNodeに残る時間を指定することができます。

例えば、ローカルの状態を多数持つアプリケーションとネットワークが分断された場合を考えます。ネットワークが復旧して、Podを排除しなくて済むことを見込んで、長時間Nodeから排除されないようにしたいこともあるでしょう。 この場合Podに設定するtolerationは次のようになります。

tolerations:
- key: "node.kubernetes.io/unreachable"
  operator: "Exists"
  effect: "NoExecute"
  tolerationSeconds: 6000
備考:

Kubernetesはユーザーまたはコントローラーが明示的に指定しない限り、自動的にnode.kubernetes.io/not-readynode.kubernetes.io/unreachableに対するtolerationをtolerationSeconds=300にて設定します。

自動的に設定されるtolerationは、taintに対応する問題がNodeで検知されても5分間はそのNodeにPodが残されることを意味します。

DaemonSetのPodは次のtaintに対してNoExecuteのtolerationがtolerationSecondsを指定せずに設定されます。

  • node.kubernetes.io/unreachable
  • node.kubernetes.io/not-ready

これはDaemonSetのPodはこれらの問題によって排除されないことを保証します。

条件によるtaintの付与

NodeのライフサイクルコントローラーはNodeの状態に応じてNoSchedule効果のtaintを付与します。 スケジューラーはNodeの状態ではなく、taintを確認します。 Nodeに何がスケジューリングされるかは、そのNodeの状態に影響されないことを保証します。ユーザーは適切なtolerationをPodに付与することで、どの種類のNodeの問題を無視するかを選ぶことができます。

DaemonSetのコントローラーは、DaemonSetが中断されるのを防ぐために自動的に次のNoScheduletolerationを全てのDaemonSetに付与します。

  • node.kubernetes.io/memory-pressure
  • node.kubernetes.io/disk-pressure
  • node.kubernetes.io/out-of-disk (重要なPodのみ)
  • node.kubernetes.io/unschedulable (1.10またはそれ以降)
  • node.kubernetes.io/network-unavailable (ホストネットワークのみ)

これらのtolerationを追加することは後方互換性を保証します。DaemonSetに任意のtolerationを加えることもできます。

次の項目