Taint と Toleration で Pod の配置を制御する

v1.37 時点(2026年10月26日 確認)

Kubernetes 基本仕様第32回 / 全45回 ・ 第6部 スケジューリングとリソース
目次

この記事ではノードの側から Pod を遠ざける Taint と Toleration の仕組みを、図で説明します。Taint を付けたノードに Pod が置かれなくなる理由と、Toleration を書いた Pod でもそのノードに置かれるとは限らない理由がわかります。3 つの効果の違いと、ノードに問題が起きたときに Kubernetes が自動で付ける Taint、Pod に自動で足される Toleration も扱います。後半では kind のマルチノードのクラスタでワーカーのノードに Taint を付け、Pod が置かれなくなる様子と、NoExecute の Taint で動いている Pod が追い出される様子を確かめる手順を示します。

第31回では Pod の側に条件を書いて置くノードを選ぶ nodeSelector と Node Affinity を扱い、特定のノードから Pod を遠ざけるもう 1 つの方法として Taint を挙げました。この回はその続きです。まだの方は先にこちらをどうぞ。

基本仕様 ・ 第31回 nodeSelector と Node Affinity の違いPod を置くノードをノードのラベルで選ぶ nodeSelector と Node Affinity の違いを図で説明します。required と preferred、演算子、IgnoredDuringExecution の意味、nodeName との違いを押さえ、kind のマルチノードのクラスタでワーカーにラベルを付けて Pod を寄せる・避ける手順を紹介します。

以下の図は後半の「手元で試す」で 1 つ目のワーカーのノードにラベル dedicated=group-a と NoSchedule の Taint を付け、4 つの Pod を作ったところを表しています。

Taint のあるノードに置かれた Pod、Taint を避けて置かれた Pod、置けずに Pending のままの Pod

nginx-before は Taint を付ける前に 1 つ目のワーカーに置いた Pod で、NoSchedule の Taint を付けたあとも動き続けます。nginx-toleration はこの Taint を許容する Toleration と、このノードのラベルを求める nodeSelector を書いた Pod です。nginx-plain は Toleration も nodeSelector も書いていない Pod で、Taint の無い 2 つ目のワーカーに置かれます。nginx-no-toleration は nginx-before と同じ nodeSelector で 1 つ目のワーカーを求めていますが、この Taint に合う Toleration が無いので置けるノードが無く、Pending のまま待ちます。図の赤はこの Pod がこのままでは動き出せない状態を表しています。図の Taint は異常な状態を表すものではなく、利用者や kubeadm が意図して付けた印です。この図とあとのコマ送り図の「Toleration なし」「Toleration あり」「NoSchedule だけ」などは、dedicated の Taint に対する Toleration を表しています。どの Pod にも Kubernetes が自動で足す別の Toleration があり、それは「組み込みの Taint と taint-based eviction」で扱います。

参考:Taints and Tolerations | Kubernetes、Implementation details | Kubernetes

Taint と Toleration とは

Taint(読み:テイント)は ノードに付けて、そのノードに Pod を寄せ付けないようにする印 です。第4回と第15回で、kubeadm がコントロールプレーンのノードに付ける Taint として触れました。Toleration(読み:トレレーション)は Pod に書いて、合う Taint のあるノードへのスケジュールを許す設定 です。

公式ドキュメントは Node Affinity を、Pod をノードの集まりに引き寄せる Pod の性質(好みとしても、必須の条件としても書ける)と説明しています。Taint はその逆で、ノードが Pod の集まりを遠ざけられるようにするもの、という説明です。ノードに 1 つ以上の Taint を付けると、そのノードはその Taint を許容しない Pod を受け入れるべきでない(should not)、という印になります。

公式ドキュメントによると、Toleration は Pod に付けるもので、合う Taint のあるノードに Pod をスケジュールすることをスケジューラーに許します。ただし Toleration はスケジュールを許すだけで、そのノードに置かれることは保証しません。スケジューラーはほかの条件も評価してノードを選ぶからです。

参考:Taints and Tolerations | Kubernetes

kubectl taint のリファレンスによると、Taint を付けられるのは今のところノードだけです。第31回の Node Affinity と並べて、書く場所と働きを次の表にまとめます。

仕組み書く場所働き
Node Affinity(第31回)Pod の spec.affinity.nodeAffinity条件に合うノードへ Pod を引き寄せる。必須の条件か、好みの条件として書く
Taintノードの spec.taintsその Taint を許容しない Pod を、そのノードから遠ざける
TolerationPod の spec.tolerations合う Taint のあるノードへのスケジュールを許す

参考:Taints and Tolerations | Kubernetes、kubectl taint | Kubernetes、Node | Kubernetes API Reference、Pod | Kubernetes API Reference

なぜ Taint と Toleration が必要なのか

第31回で見たとおり、Node Affinity の DoesNotExist などを使えば、Pod の側からも特定のノードを避けられます。ただし Node Affinity は Pod の性質なので、避けたい Pod の 1 つ 1 つに条件を書くことになります。Taint ならノードの側に印を付けるだけで、合う Toleration を書いていない新しい Pod はどれも、そのノードにスケジュールされなくなります(効果が NoSchedule の場合)。ノードを特定の用途に取っておきたいときは、Taint のほうが書く場所が少なくて済むと考えられます。

参考:Taints and Tolerations | Kubernetes、Assigning Pods to Nodes | Kubernetes

公式ドキュメントは Taint と Toleration を、Pod をノードから「遠ざける」ことや、動いているべきでない Pod を追い出すことに使える柔軟な方法だとしています。使いどころとして挙がっているのは、次の表の 3 つです。

使いどころ公式ドキュメントの説明
専用のノード(Dedicated Nodes)一部のノードを特定の利用者たちだけで使いたいとき、そのノードに Taint を付け、その利用者たちの Pod に合う Toleration を足す
特別なハードウェアを持つノードGPU などを持つ少数のノードに Taint を付け、そのハードウェアを使う Pod に Toleration を足す。ハードウェアが要らない Pod を遠ざけ、あとから来る、それが要る Pod のために場所を空けておく
Taint based Evictionsノードに問題が起きたときの追い出し方を、Pod ごとに設定する。この記事の「組み込みの Taint と taint-based eviction」で扱う

参考:Taints and Tolerations | Kubernetes

専用のノードの説明には続きがあります。Toleration のある Pod は、Taint の付いた専用のノードだけでなく、クラスタのほかのノードも使えます。専用のノードだけを使わせたいなら、同じノードに Taint と同じようなラベル(例:dedicated=groupName)も付け、そのラベルのノードにしかスケジュールできないよう Node Affinity を Pod に足すべき(should)だ、と書かれています。後半の手順ではこの組み合わせを手で試します。

参考:Taints and Tolerations | Kubernetes

Taint と Toleration の仕組み

Taint の形と kubectl taint

Taint はキー、値、効果(effect)の 3 つでできています。kubectl taint の引数では キー=値:効果 の形で書きます。以下は公式ドキュメントの例で、ノード node1 に Taint を付けるコマンドです。

console
$ kubectl taint nodes node1 key1=value1:NoSchedule

このコマンドは、キーが key1、値が value1、効果が NoSchedule の Taint を node1 に付けます。公式ドキュメントによると、これで合う Toleration を持たない Pod は node1 にスケジュールできなくなります。外すには、同じ形の最後に - を付けます。

console
$ kubectl taint nodes node1 key1=value1:NoSchedule-

参考:Taints and Tolerations | Kubernetes

kubectl taint のリファレンスと API リファレンスによると、キーと効果は必須で、値は省略できます。効果は NoSchedule、PreferNoSchedule、NoExecute のどれかです。付けた Taint はノードの spec.taints に入ります。

参考:kubectl taint | Kubernetes、Node | Kubernetes API Reference

3 つの効果

効果はその Taint を許容しない Pod に Taint がどう働くかを決めます。公式ドキュメントの説明を次の表にまとめます。

効果公式ドキュメントの説明
NoSchedule合う Toleration を持たない新しい Pod は、そのノードにスケジュールされない。いま動いている Pod は追い出されない
PreferNoScheduleNoSchedule の「好み」「ゆるい」版。許容しない Pod をそのノードに置くのをコントロールプレーンが避けようとするが、保証はしない
NoExecuteすでにそのノードで動いている Pod にも働く。許容しない Pod はすぐに追い出される

参考:Taints and Tolerations | Kubernetes

追い出し(eviction)はノードの上の Pod を 1 つ以上終わらせる処理のことです。追い出された Pod はそこで終わり、同じ Pod が別のノードに移るわけではありません(第8回)。

参考:Glossary | Kubernetes、Pod Lifecycle | Kubernetes

第31回で見たとおり、kube-scheduler は Pod を置けるノードに絞り込む filtering と、残ったノードに点数を付ける scoring でノードを選びます。API リファレンスによると、NoSchedule と PreferNoSchedule を守らせるのはスケジューラーです。Taint と Toleration を実装する TaintToleration というプラグインは絞り込みと点数付けの両方で働くので、NoSchedule は絞り込みで、PreferNoSchedule は点数付けで効くと読めます。

参考:Toleration | Kubernetes API Reference、Scheduler Configuration | Kubernetes、Kubernetes Scheduler | Kubernetes

公式ドキュメントによると、既定のスケジューラーはノードを選ぶときに Taint と Toleration を考えに入れます。ただし Pod の .spec.nodeName を手で書くとスケジューラーを飛ばすので(第31回)、選んだノードに NoSchedule の Taint があっても、Pod はそのノードに割り当てられます。公式ドキュメントはこの場合について、そのノードに NoExecute の Taint もあれば、適切な Toleration が無い限り kubelet が Pod を追い出す(eject)、と書いています。

参考:Taints and Tolerations | Kubernetes

Toleration の書き方と Taint に合う条件

Toleration は Pod の spec.tolerations に並べます。以下は公式ドキュメントの例の 2 つの Toleration です。公式ドキュメントによると、どちらもさきほどの key1=value1:NoSchedule の Taint に合うので、どちらかを書いた Pod は node1 にスケジュールできます。

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

参考:Taints and Tolerations | Kubernetes

Toleration の各フィールドを、API リファレンスに沿って次の表にまとめます。

フィールド内容
key許容する Taint のキー。空にする場合は表の下で説明する
operatorキーと値の関係。主に使うのは Equal(値が同じ)と Exists(値を問わない)で、既定は Equal
value許容する Taint の値。operator が Exists なら空にするべき(should)
effect許容する Taint の効果。空ならすべての効果に合う
tolerationSecondsNoExecute の Taint を許容し続ける秒数。「NoExecute と tolerationSeconds」で扱う

参考:Toleration | Kubernetes API Reference

公式ドキュメントによると、Toleration が Taint に「合う」のは、キーと効果がどちらも同じで、さらに operator が Exists であるか(このとき value は書かないようにする(should))、operator が Equal で値も同じときです。上の 1 つ目の例は Equal なので、キーが key1 で値が value1、効果が NoSchedule の Taint に合います。2 つ目の例は Exists なので、キーが key1 で効果が NoSchedule なら、値を問わず合います。

公式ドキュメントは特別な場合として、key を空にする場合を挙げています。key が空なら operator は Exists でなければならず、すべてのキーと値に合います。このとき effect を書いた場合は、その効果の Taint だけに合います(effect が空ならすべての効果に合うのは、表のとおりです)。

参考:Taints and Tolerations | Kubernetes

複数の Taint と Toleration

1 つのノードに複数の Taint を、1 つの Pod に複数の Toleration を付けられます。公式ドキュメントによると、Kubernetes はこれをフィルターのように扱います。ノードのすべての Taint から始め、Pod が合う Toleration を持つ Taint を無視し、無視されずに残った Taint の効果が Pod に働きます。残った Taint ごとの働きは次の表のとおりです。

残った TaintPod への働き
NoSchedule が 1 つ以上あるKubernetes はそのノードに Pod をスケジュールしない
NoSchedule は無く、PreferNoSchedule が 1 つ以上あるKubernetes はそのノードに Pod をスケジュールしないよう試みる
NoExecute が 1 つ以上あるすでにそのノードで動いている Pod は追い出され、まだ動いていない Pod はそのノードにスケジュールされない

参考:Taints and Tolerations | Kubernetes

公式ドキュメントの例を見ます。以下のコマンドで、ノード node1 に Taint を 3 つ付けたとします。

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

Pod には次の 2 つの Toleration があります。

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

参考:Taints and Tolerations | Kubernetes

公式ドキュメントによると、この Pod は node1 にスケジュールできません。3 つ目の Taint(key2=value2:NoSchedule)に合う Toleration が無いからです。一方、Taint を付けたときにこの Pod がすでに node1 で動いていれば、動き続けられます。許容されずに残るのは 3 つ目の Taint だけで、その効果は NoSchedule だからです。

NoExecute と tolerationSeconds

公式ドキュメントによると、NoExecute の Taint は、そのノードですでに動いている Pod に次のように働きます。

  • Taint を許容しない Pod は、すぐに追い出される
  • Taint を許容し、tolerationSeconds を書いていない Pod は、いつまでもノードに割り当てられたまま
  • Taint を許容し、tolerationSeconds を書いた Pod は、その時間だけ割り当てられたままで、時間がたつと追い出される

参考:Taints and Tolerations | Kubernetes

以下は公式ドキュメントの tolerationSeconds を書いた Toleration の例です。

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

公式ドキュメントによると、この Toleration を持つ Pod が動いているノードに合う Taint が付くと、Pod は 3600 秒のあいだノードに割り当てられたままで、そのあと追い出されます。それより前に Taint が外されれば、Pod は追い出されません。API リファレンスによると、tolerationSeconds は効果が NoExecute の Toleration でなければ無視されます。0 や負の値は 0(すぐに追い出す)として扱われます。

参考:Taints and Tolerations | Kubernetes、Toleration | Kubernetes API Reference

Pod を追い出すコンポーネントは、公式ドキュメントのページによって書き方が違います。Taint のページは tolerationSeconds の時間がたったあとに node lifecycle controller が追い出すとし、API リファレンスは NodeController が守らせるとしています。Nodes のページも、node controller が NoExecute の Taint のあるノードの Pod を追い出すとしています。一方、同じ Taint のページの注記によると、v1.29 より後は taint-based eviction の実装が taint-eviction-controller という独立したコンポーネントに移り、kube-controller-manager の --controllers で無効にできます。kube-controller-manager のリファレンスでは、taint-eviction-controller は既定で無効なコントローラーに入っていません。

役割をまとめると、ノードに問題が起きたときに Taint を付けるのは、主にコントロールプレーンの node controller です(公式ドキュメントは、kubelet が付ける場合もあるとしています)。NoExecute の Taint を見て Pod を追い出すのは、v1.37 時点では既定で kube-controller-manager の中の taint-eviction-controller だと読めます(次の図の左のアイコン)。

参考:Taints and Tolerations | Kubernetes、Taints and Tolerations | Kubernetes、Toleration | Kubernetes API Reference、kube-controller-manager | Kubernetes、Nodes | Kubernetes

以下の図は、後半の手順と同じく、NoSchedule の Taint が付いた 1 つ目のワーカーに NoExecute の Taint を足したとき、そこで動いている 4 つの Pod がどうなるかをコマ送りで表しています。4 つの Pod はそれぞれ違う Toleration を持っています。

(初期)1 つ目のワーカーには dedicated=group-a:NoSchedule の Taint だけがあり、4 つの Pod が動いている。NoSchedule は動いている Pod を追い出さない
(1/3)利用者が kubectl taint で dedicated=group-a:NoExecute の Taint を足す
(2/3)NoExecute を許容しない nginx-before と nginx-toleration が、すぐに追い出される(追い出すのは taint-eviction-controller と読める)。残りの 2 つは、まだ動いている
(3/3)Taint を足してから 60 秒たつと、nginx-tolerate-60s も追い出される。tolerationSeconds の無い nginx-tolerate-forever は動き続ける

追い出しは異常な状態ではなく、Taint が求めたとおりの動きなので、追い出された Pod は赤ではなく薄く描いています。追い出しの線はノードの枠に向けていますが、追い出されるのは薄く描いた Pod だけです。4 つの Pod はどれも ReplicaSet などのコントローラーが管理していない Pod です。そのため、追い出されたあと代わりの Pod は作られないと考えられます(第11回)。

参考:Taints and Tolerations | Kubernetes、Pod Lifecycle | Kubernetes

組み込みの Taint と taint-based eviction

公式ドキュメントによると、特定の条件が真になると、node controller がノードに自動で Taint を付けます。v1.37 時点の公式ドキュメントが挙げる組み込みの Taint は、次の表のとおりです。

Taint のキー公式ドキュメントの説明効果
node.kubernetes.io/not-readyノードの準備ができていない。ノードの状態(NodeCondition)の Ready が False に当たる既定で NoExecute
node.kubernetes.io/unreachablenode controller からノードに届かない。Ready が Unknown に当たる既定で NoExecute
node.kubernetes.io/memory-pressureノードのメモリが足りなくなっているNoSchedule
node.kubernetes.io/disk-pressureノードのディスクが足りなくなっているNoSchedule
node.kubernetes.io/pid-pressureノードのプロセスの数が足りなくなっているNoSchedule
node.kubernetes.io/network-unavailableノードのネットワークが使えないNoSchedule
node.kubernetes.io/unschedulableノードがスケジュールできない状態になっている(第36回)NoSchedule

参考:Taints and Tolerations | Kubernetes、Taints and Tolerations | Kubernetes、Well-Known Labels, Annotations and Taints | Kubernetes、Well-Known Labels, Annotations and Taints | Kubernetes

表の効果の列は公式ドキュメントの次の説明によります。node.kubernetes.io/not-ready と node.kubernetes.io/unreachable には既定で NoExecute の効果が付き、ノードの状態に対応する Taint は、コントロールプレーンが node controller を使って NoSchedule の効果で作ります。node.kubernetes.io/network-unavailable と node.kubernetes.io/unschedulable の効果は、Well-Known Labels, Annotations and Taints のページの例の値です。問題が直れば、kubelet か node controller が Taint を外すことができます。スケジューラーはノードの状態ではなく Taint を見てスケジュールを決めます。たとえばノードの状態の DiskPressure が有効になると、コントロールプレーンは node.kubernetes.io/disk-pressure の Taint を付け、そのノードに新しい Pod をスケジュールしません。表に挙げたほかに、クラウドの環境で使う組み込みの Taint もあります。

参考:Taints and Tolerations | Kubernetes、Taints and Tolerations | Kubernetes、Well-Known Labels, Annotations and Taints | Kubernetes、Well-Known Labels, Annotations and Taints | Kubernetes

NoExecute の Taint によって、ノードに問題が起きたときに Pod を追い出す仕組みが taint-based eviction で、v1.18 から安定版(Stable)です。tolerationSeconds を書くと、問題の起きたノードに Pod がどれだけ割り当てられたままでいるかを Pod ごとに決められます。公式ドキュメントは例として、ローカルな状態を多く持つアプリケーションを、ネットワークが分断されたときに長くノードに残したい場合を挙げています。分断が直って追い出しを避けられることを期待する、という場面です。以下はその例の Toleration です。

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

参考:Taints and Tolerations | Kubernetes

この例のような Toleration を書かなくても、Pod には node.kubernetes.io/not-ready と node.kubernetes.io/unreachable の 2 つの Toleration が自動で足されます。公式ドキュメントによると、tolerationSeconds は 300 で、利用者かコントローラーがこれらの Toleration をはっきり書いていれば足されません。これらの Toleration によって、こうした問題が見つかってから 5 分のあいだ、Pod はノードに割り当てられたままになります。

足しているのは DefaultTolerationSeconds というアドミッションコントローラー(第30回)で、v1.37 時点では既定で有効です。

参考:Taints and Tolerations | Kubernetes、Admission Control in Kubernetes | Kubernetes、Admission Control in Kubernetes | Kubernetes

第15回で見たとおり、DaemonSet の Pod は違います。公式ドキュメントによると、DaemonSet の Pod はこの 2 つの Taint に対する NoExecute の Toleration を tolerationSeconds 無しで持った状態で作られるので、これらの問題で追い出されることはありません。

公式ドキュメントによると、ノードに届かないときは Pod を削除する決定が kubelet に伝わらず、削除が決まった Pod が分断されたノードで動き続けることがあります(may)。

参考:Taints and Tolerations | Kubernetes

Node Affinity との使い分け

ここまでの内容と第31回をもとに、やりたいことと使うものの対応を次の表にまとめます。

やりたいこと使うもの
Pod を特定のノードに寄せたいNode Affinity か nodeSelector(第31回)
特定のノードから、決めた Pod 以外を遠ざけたいそのノードに Taint を付け、許す Pod に Toleration を書く
特定のノードを決めた Pod だけに使わせ、その Pod もそのノードだけを使うようにしたいTaint と Toleration に、ノードのラベルと Node Affinity を組み合わせる(専用のノードの例)
ノードに問題が起きたとき、Pod をどれだけ残すかを決めたいNoExecute の Toleration の tolerationSeconds

参考:Taints and Tolerations | Kubernetes、Taints and Tolerations | Kubernetes、Assigning Pods to Nodes | Kubernetes

Pod を寄せるのが Node Affinity で、ノードから遠ざけるのが Taint です。

手元で試す

ここまでの内容を手元の kind のクラスタで試す手順を示します。公式ドキュメントの専用のノードの例に沿って、1 つ目のワーカーのノードにラベルと NoSchedule の Taint を付け、Toleration の無い Pod がそのノードに置かれなくなることと、Toleration を書いた Pod は置けることを確かめます。最後に NoExecute の Taint を足し、動いている Pod が Toleration の違いによって、すぐに追い出されるもの、60 秒後に追い出されるもの、動き続けるものに分かれることを確かめます。

各段で何が表示されるかは、どれも公式ドキュメントから予想される結果で、手元で実行して確かめたものではありません。各段の最後に、成功かどうかを見分ける目安を書きます。

コマンドは macOS や Linux のシェル(bash や zsh)で実行する前提で書いています。Windows では WSL(Windows の中で Linux を動かす仕組み)などを使ってください。試す環境は次のとおりです。

項目内容
kindv0.33.0
Kubernetesv1.37.0(kind v0.33.0 の既定のノードイメージ)
ノード構成マルチノード(クラスタ名 multi-node):コントロールプレーン 1、ワーカー 2
必要なものDocker、kubectl(v1.36〜v1.38)、テキストエディタ、端末(ターミナル)2 つ

参考:Release v0.33.0 · kubernetes-sigs/kind、Version Skew Policy | Kubernetes

kind でクラスタを作る手順は、以下の記事で解説しています。この回は第31回と同じマルチノードのクラスタを使います。1 つ目のワーカーに Taint を付け、Taint の無い 2 つ目のワーカーと比べるためです。

基本仕様 ・ 第2回 kind で検証用のクラスタを作るDocker のコンテナをノードにして、手元のマシンに Kubernetes のクラスタを作るツール kind の仕組みを図で説明します。kind create cluster で何が起きるかを押さえ、シングルノードとマルチノードのクラスタを作って、ノードの正体が Docker のコンテナであることを確かめる手順を紹介します。

マルチノードのクラスタを用意する

第2回と同じ次の設定ファイルを使います。公式ドキュメントの例をそのまま引用しています。kind-multi-node.yaml という名前で、コマンドを実行するディレクトリに保存します。前の回までに保存したファイルが残っていれば、それを使えます。

yaml
# three node (two workers) cluster config
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker

参考:Quick Start | kind

まず、前の回のクラスタが残っていないかを確かめます。

console
$ kind get clusters

multi-node が表示されたら、作り直さずにそのまま使えるので、クラスタを作る次のコマンドは飛ばします。表示されなければ、クラスタを作ってから、もう一度一覧します。前の回の kind が表示されても、この回では使いません。手元のマシンの資源(メモリなど)に余裕がなければ、kind delete cluster --name kind で消してから進めてください。

console
$ kind create cluster --name multi-node --config kind-multi-node.yaml
$ kind get clusters

kind get clusters に multi-node が表示されれば、クラスタができています。第3回で決めたとおり、ここからのコマンドには --context kind-multi-node を付けて、操作するクラスタをはっきりさせます。

参考:Quick Start | kind

続けて、ノードを一覧します。

console
$ kubectl get nodes --context kind-multi-node

ノードが 3 つ表示され、STATUS 列が Ready になっていれば準備ができています。Ready になっていなければ、少し待ってからもう一度実行します。

参考:kubectl get | Kubernetes、Node Status | Kubernetes

次に、第31回と同じく、node-role.kubernetes.io/control-plane のラベルを持たないノード、つまりワーカーのノードだけを一覧します。

console
$ kubectl get nodes -l '!node-role.kubernetes.io/control-plane' --context kind-multi-node

参考:Labels and Selectors | Kubernetes、Well-Known Labels, Annotations and Taints | Kubernetes

ノードが 2 つ表示されるはずです。名前は multi-node-worker や multi-node-worker2 のような形になると考えられます(公式ドキュメントでは確かめられなかった推測です)。ここからは表示された順に「1 つ目のワーカー」「2 つ目のワーカー」と呼びます。2 つの名前をメモしておいてください。このあとのコマンドの <1 つ目のワーカーの名前> は、< と > も含めて、メモした名前に置き換えます。

前の回のクラスタを使い続けている場合は、default の Namespace に Pod が残っていないことと、ノードに dedicated のラベルが無いことを確かめます。-L は第11回で使った、指定したラベルの値を列にして表示する指定です。

console
$ kubectl get pods --context kind-multi-node
$ kubectl get nodes -L dedicated --context kind-multi-node

1 つ目のコマンドで見つからないことを知らせる 1 行(第8回で推測した No resources found in default namespace. のような表示)が出て、2 つ目のコマンドで加わる DEDICATED 列がどのノードでも空なら、そのまま進めます。Pod が残っていたら、自分で作ったものかを確かめてから、kubectl delete pod <名前> --context kind-multi-node で消します。

参考:kubectl get | Kubernetes

ノードの Taint を見る

各ノードの Taint を、キー、値、効果の列にして表示します。公式ドキュメントの kubectl のクイックリファレンスにあるコマンドをそのまま使います。-o custom-columns は 列の名前:フィールド の形で列を指定する出力の形式です(第6回)。.spec.taints[*].key はノードの spec.taints に並んだすべての Taint のキーを指します。第15回では -o jsonpath でキーだけを表示しましたが、このコマンドでは値と効果も表示します。

console
$ kubectl get nodes -o='custom-columns=NodeName:.metadata.name,TaintKey:.spec.taints[*].key,TaintValue:.spec.taints[*].value,TaintEffect:.spec.taints[*].effect' --context kind-multi-node

参考:kubectl Quick Reference | Kubernetes、Node | Kubernetes API Reference

コントロールプレーンのノードの行は、キーの列(コマンドで TaintKey と名付けた列)が node-role.kubernetes.io/control-plane、効果の列(TaintEffect)が NoSchedule になるはずです。kubeadm がコントロールプレーンのノードに付ける Taint には値が無いので、値の列(TaintValue)は <none> のような表示になると考えられます。ワーカーの 2 つの行は、Taint が無いと考えられるので、3 つの列がどれも <none> のような表示になると考えられます。<none> の表示は公式ドキュメントでは確かめられなかった推測です。

参考:Implementation details | Kubernetes、Well-Known Labels, Annotations and Taints | Kubernetes、kubectl taint | Kubernetes、kubeadm Configuration (v1beta4) | Kubernetes

Pod を監視する

ここからは端末を 2 つ使います。第31回と同じく、1 つ目の端末で default の Namespace の Pod の変化を追い続け、2 つ目の端末でノードに Taint を付けたりマニフェストを渡したりします。2 つ目の端末を開いたら、cd でマニフェストを保存するディレクトリに移動しておきます。

1 つ目の端末で、Pod を監視します。-o wide を付けて、Pod が置かれたノードの名前(NODE 列)も表示します。この監視は片付けの前まで止めずに続けます。

console
$ kubectl get pods --watch -o wide --context kind-multi-node

参考:kubectl get | Kubernetes

Pod がまだ無いので、見つからないことを知らせる 1 行が出たあと、止まったように見えると考えられます。

1 つ目のワーカーにラベルを付けて Pod を置く

Taint を付ける前に、1 つ目のワーカーで Pod を 1 つ動かしておきます。NoSchedule の Taint を付けたあと、この Pod がどうなるかを見るためです。2 つ目の端末で、公式ドキュメントの専用のノードの例に倣い、1 つ目のワーカーにラベル dedicated=group-a を付けます。公式ドキュメントの例ではこのラベルを Node Affinity で求めますが、この手順では第31回で required の Node Affinity と同じ条件になると見た nodeSelector で求めます。

console
$ kubectl label nodes <1 つ目のワーカーの名前> dedicated=group-a --context kind-multi-node

参考:kubectl label | Kubernetes、Taints and Tolerations | Kubernetes

第31回と同じく、node/<1 つ目のワーカーの名前> labeled のような 1 行が出るはずです。

以下は nodeSelector で dedicated=group-a のノードを求める Pod のマニフェストです。第31回の nginx-selector から、名前と nodeSelector のラベルを変えました。nginx-before.yaml という名前で保存します。

yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-before
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    dedicated: group-a

参考:Assign Pods to Nodes | Kubernetes、Pod | Kubernetes API Reference

2 つ目の端末で、マニフェストを渡します。

console
$ kubectl apply -f nginx-before.yaml --context kind-multi-node

pod/nginx-before が作られたことを知らせる 1 行が出るはずです。1 つ目の端末には nginx-before の行が書き足されていき、NODE 列は 1 つ目のワーカーになり、STATUS 列は Pending、ContainerCreating を経て Running になるはずです。dedicated=group-a のラベルを持つノードは 1 つ目のワーカーだけだからです。クラスタを作ったばかりなら、イメージの取得に時間がかかるので、ContainerCreating のまましばらく止まって見えることがあります(第9回)。

成功の目安は、nginx-before の NODE 列が 1 つ目のワーカーの名前で、STATUS 列が Running になっていることです。

NoSchedule の Taint を付ける

2 つ目の端末で、1 つ目のワーカーに、キーが dedicated、値が group-a、効果が NoSchedule の Taint を付けます。

console
$ kubectl taint nodes <1 つ目のワーカーの名前> dedicated=group-a:NoSchedule --context kind-multi-node

参考:kubectl taint | Kubernetes、Taints and Tolerations | Kubernetes

Taint を付けたことを知らせる 1 行が出ると考えられます。1 つ目の端末では、nginx-before の行は Running のままで、Terminating の行は現れないはずです。「3 つの効果」の表のとおり、NoSchedule は動いている Pod を追い出さないからです。

「ノードの Taint を見る」と同じコマンドで、Taint が付いたことを確かめます。

console
$ kubectl get nodes -o='custom-columns=NodeName:.metadata.name,TaintKey:.spec.taints[*].key,TaintValue:.spec.taints[*].value,TaintEffect:.spec.taints[*].effect' --context kind-multi-node

1 つ目のワーカーの行が、キーの列が dedicated、値の列が group-a、効果の列が NoSchedule になっているはずです。

成功の目安は、1 つ目のワーカーの行に dedicated の Taint があり、nginx-before が Running のままであることです。

Toleration の無い Pod を作る

ここでは Toleration を書いていない Pod を 2 つ作ります。1 つは nodeSelector も書いていない Pod、もう 1 つは nginx-before と同じ nodeSelector を書いた Pod です。

以下は Toleration も nodeSelector も書いていない Pod のマニフェストです。nginx-plain.yaml という名前で保存します。

yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-plain
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent

以下は nginx-before.yaml から名前だけを変えたマニフェストです。nginx-no-toleration.yaml という名前で保存します。

yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-no-toleration
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    dedicated: group-a

参考:Pod | Kubernetes API Reference

2 つ目の端末で、2 つのマニフェストを渡します。

console
$ kubectl apply -f nginx-plain.yaml --context kind-multi-node
$ kubectl apply -f nginx-no-toleration.yaml --context kind-multi-node

どちらも Pod が作られたことを知らせる 1 行が出るはずです。置けるノードが無くても、Pod はまず Pending で受け付けられ、ノードが決まるのを待つからです(第9回)。

1 つ目の端末には nginx-plain の行が書き足されていき、NODE 列は 2 つ目のワーカーになって、やがて Running になるはずです。コントロールプレーンのノードにも 1 つ目のワーカーにも、この Pod が許容しない NoSchedule の Taint があり、Taint が無いのは 2 つ目のワーカーだけだからです。

nginx-no-toleration の行も現れますが、STATUS 列は Pending のままで、NODE 列にもノードの名前が入らない(<none> のような表示になる)と考えられます。nodeSelector に合うのは 1 つ目のワーカーだけですが、そこには許容しない NoSchedule の Taint があるので、スケジュールできるノードが残らないからです。同じマニフェストの nginx-before が動いているのは、Taint を付ける前にスケジュールされたからです。

参考:Taints and Tolerations | Kubernetes、Kubernetes Scheduler | Kubernetes、Pod Lifecycle | Kubernetes

理由を見たい人は 2 つ目の端末で nginx-no-toleration を詳しく表示します。この確認は飛ばしても、あとの手順に影響しません。

console
$ kubectl describe pod nginx-no-toleration --context kind-multi-node

参考:kubectl describe | Kubernetes、Resource Management for Pods and Containers | Kubernetes

表示の末尾の Events: には、第31回と同じく FailedScheduling(From が default-scheduler)の行があると考えられます。その行のメッセージに taint や dedicated といった語が含まれていれば、Taint が理由だと見分けられると考えられます。ただしメッセージの例はこのメディアで確かめた範囲の公式ドキュメントにはありません。

成功の目安は、nginx-plain の NODE 列が 2 つ目のワーカーの名前で Running になり、nginx-no-toleration が Pending のままであることです。

自動で足された Toleration を見る

nginx-plain のマニフェストには Toleration を書いていません。それでも「組み込みの Taint と taint-based eviction」で見た DefaultTolerationSeconds によって、Toleration が足されているはずです。2 つ目の端末で、Pod の内容を YAML で表示します(第5回)。

console
$ kubectl get pod nginx-plain -o yaml --context kind-multi-node

参考:kubectl get | Kubernetes

spec の中の tolerations: の下に、キーが node.kubernetes.io/not-ready と node.kubernetes.io/unreachable の 2 つの項目があり、どちらも effect が NoExecute、tolerationSeconds が 300 になっているはずです。kind のクラスタも kubeadm で作られ(第15回)、kubeadm は kube-apiserver で DefaultTolerationSeconds を有効にするからです。表示の中での項目の順番やほかのフィールドの書き方は、公式ドキュメントでは確かめられませんでした。

参考:Taints and Tolerations | Kubernetes、Admission Control in Kubernetes | Kubernetes、Implementation details | Kubernetes

成功の目安は、マニフェストに書いていない node.kubernetes.io/not-ready と node.kubernetes.io/unreachable の Toleration が、tolerationSeconds: 300 で表示されることです。この 2 つの Toleration は nginx-plain だけでなく、この回で作るどの Pod にも足されます(どのマニフェストにもこれらのキーを書いていないため)。

Toleration を書いた Pod を作る

以下は dedicated=group-a:NoSchedule の Taint を許容する Toleration と、dedicated=group-a のラベルを求める nodeSelector を書いた Pod のマニフェストです。Toleration は「Toleration の書き方と Taint に合う条件」の 1 つ目の例の形(Equal)で、キーと値をこの手順に合わせました。nginx-toleration.yaml という名前で保存します。

yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-toleration
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    dedicated: group-a
  tolerations:
  - key: "dedicated"
    operator: "Equal"
    value: "group-a"
    effect: "NoSchedule"

参考:Taints and Tolerations | Kubernetes、Toleration | Kubernetes API Reference

2 つ目の端末で、マニフェストを渡します。

console
$ kubectl apply -f nginx-toleration.yaml --context kind-multi-node

pod/nginx-toleration が作られたことを知らせる 1 行が出るはずです。1 つ目の端末には nginx-toleration の行が書き足されていき、NODE 列は 1 つ目のワーカーになって、やがて Running になるはずです。Toleration がキー、値、効果のすべてで Taint に合うので、1 つ目のワーカーの Taint は無視され、nodeSelector に合う 1 つ目のワーカーにスケジュールできるからです。

nodeSelector を書かずに Toleration だけを書いた場合は、1 つ目のワーカーに置かれるとは限りません。Toleration はスケジュールを許すだけなので、Taint の無い 2 つ目のワーカーも候補に残り、どちらになるかはスケジューラーの点数付けで決まると考えられます。

参考:Taints and Tolerations | Kubernetes

成功の目安は、nginx-toleration の NODE 列が 1 つ目のワーカーの名前で、STATUS 列が Running になっていることです。nginx-no-toleration は Pending のままのはずです。冒頭の図はこの時点の様子です。

NoExecute の Taint で Pod を追い出す

NoExecute の Taint を足す前に、NoExecute を許容する Pod を 2 つ、1 つ目のワーカーに置いておきます。

以下は dedicated の Taint を、NoSchedule はいつまでも、NoExecute は 60 秒だけ許容する Pod のマニフェストです。1 つ目のワーカーにはもう NoSchedule の Taint があるので、そこに置かれるには NoSchedule の Toleration も要ります。Toleration は Exists の形にしたので、値は書いていません。nginx-tolerate-60s.yaml という名前で保存します。

yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-tolerate-60s
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    dedicated: group-a
  tolerations:
  - key: "dedicated"
    operator: "Exists"
    effect: "NoSchedule"
  - key: "dedicated"
    operator: "Exists"
    effect: "NoExecute"
    tolerationSeconds: 60

以下は effect を省いた Toleration を 1 つだけ書いた Pod のマニフェストです。effect が空の Toleration はそのキーのすべての効果に合うので、NoSchedule と NoExecute の両方の Taint を許容します。tolerationSeconds は書いていません。nginx-tolerate-forever.yaml という名前で保存します。

yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-tolerate-forever
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    dedicated: group-a
  tolerations:
  - key: "dedicated"
    operator: "Equal"
    value: "group-a"

参考:Taints and Tolerations | Kubernetes、Toleration | Kubernetes API Reference

2 つ目の端末で、2 つのマニフェストを渡します。

console
$ kubectl apply -f nginx-tolerate-60s.yaml --context kind-multi-node
$ kubectl apply -f nginx-tolerate-forever.yaml --context kind-multi-node

1 つ目の端末に 2 つの Pod の行が書き足されていき、どちらも NODE 列が 1 つ目のワーカーで、やがて Running になるはずです。2 つとも Running になるまで待ちます。これで 1 つ目のワーカーでは、nginx-before、nginx-toleration、nginx-tolerate-60s、nginx-tolerate-forever の 4 つの Pod が動いています。コマ送り図の最初の状態です。

次に、2 つ目の端末で、1 つ目のワーカーに同じキーと値で効果が NoExecute の Taint を足します。公式ドキュメントの複数の Taint の例と同じく、キーと値が同じで効果だけが違う Taint を、もう 1 つ付ける形です。このあと 60 秒を数えるので、実行する直前に時刻を控えておきます(date コマンドで表示してもかまいません)。

console
$ kubectl taint nodes <1 つ目のワーカーの名前> dedicated=group-a:NoExecute --context kind-multi-node

参考:kubectl taint | Kubernetes、Taints and Tolerations | Kubernetes

Taint を付けたことを知らせる 1 行が出ると考えられます。1 つ目の端末では、nginx-before と nginx-toleration の行がすぐに書き足され、STATUS 列が Terminating になると考えられます。どちらも NoExecute の Taint を許容していないので、すぐに追い出されるからです。nginx-toleration は NoSchedule の Taint を許容していますが、許容されずに残る NoExecute の Taint が働きます。どちらの Pod にも自動で足された NoExecute の Toleration がありますが、キーが node.kubernetes.io/not-ready と node.kubernetes.io/unreachable なので、キーが dedicated の Taint には合いません。

公式ドキュメントでは、Pod を削除するときに一部の kubectl のコマンドで Terminating が表示されることがある、とされています。追い出しが Pod の削除として行われることは、taint-based eviction の節の「Pod を削除する決定」という書き方から読めることで、Terminating が出るというのはそこからの推測です。

参考:Taints and Tolerations | Kubernetes、Taints and Tolerations | Kubernetes、Pod Lifecycle | Kubernetes

Taint を足してから 60 秒たつと、nginx-tolerate-60s の行も Terminating になると考えられます。NoExecute を許容していても、tolerationSeconds の 60 秒がたつと追い出されるからです。nginx-tolerate-forever は tolerationSeconds が無いので、いつまでも追い出されず、Running のままのはずです。

60 秒ちょうどに追い出されるかどうかは公式ドキュメントには書かれていません。また、追い出された Pod は終了の猶予(terminationGracePeriodSeconds。既定で 30 秒)のあいだ Terminating のまま残ることがあると考えられます。そこで、Taint を足してから 2 分ほど待ちます。kubectl get pods --watch は行を書き足していくだけなので、消えた Pod の行も画面に残ると考えられます。消えたかどうかはあとの kubectl get pods で確かめます。

参考:Taints and Tolerations | Kubernetes、Pod | Kubernetes API Reference、Pod Lifecycle | Kubernetes

2 分ほど待って 1 つ目の端末の表示が落ち着いたら、2 つ目の端末で、ノードの名前と一緒に一覧します。

console
$ kubectl get pods -o wide --context kind-multi-node

参考:kubectl get | Kubernetes

Pod は 3 つ表示されると考えられます。作った 6 つの Pod がそれぞれどうなったかを、次の表にまとめます。

Pod一覧での状態理由
nginx-before表示されないNoExecute を許容していないので、すぐに追い出された
nginx-toleration表示されないNoSchedule だけを許容し、NoExecute は許容していないので、すぐに追い出された
nginx-tolerate-60s表示されないNoExecute を 60 秒だけ許容したので、60 秒後に追い出された
nginx-tolerate-forever1 つ目のワーカーで Runningeffect を省いた Toleration が両方の Taint に合い、tolerationSeconds も無い
nginx-plain2 つ目のワーカーで RunningTaint の無いノードで動いている
nginx-no-tolerationPending置けるノードが無いまま

成功の目安は、nginx-tolerate-forever が 1 つ目のワーカーで Running のまま残り、nginx-before、nginx-toleration、nginx-tolerate-60s が一覧から消えていることです。

片付け

1 つ目の端末で Ctrl+C を押して、監視を止めます。

2 つ目の端末で、マニフェストを指定して、作った Pod を削除します。追い出された 3 つの Pod はすでに無いので、--ignore-not-found を付けて、見つからないことをエラーにせず削除が成功したものとして扱わせます。

console
$ kubectl delete -f nginx-before.yaml --ignore-not-found --context kind-multi-node
$ kubectl delete -f nginx-plain.yaml --ignore-not-found --context kind-multi-node
$ kubectl delete -f nginx-no-toleration.yaml --ignore-not-found --context kind-multi-node
$ kubectl delete -f nginx-toleration.yaml --ignore-not-found --context kind-multi-node
$ kubectl delete -f nginx-tolerate-60s.yaml --ignore-not-found --context kind-multi-node
$ kubectl delete -f nginx-tolerate-forever.yaml --ignore-not-found --context kind-multi-node

参考:kubectl delete | Kubernetes

続けて、1 つ目のワーカーに付けた Taint とラベルを外します。この回は最後にクラスタごと消しますが、クラスタを残して次の回にも使う人のために、元の状態に戻す手順を示します。Pod を先に消したのは、先に Taint を外すと Pending の nginx-no-toleration が 1 つ目のワーカーに置かれて動き出すと考えられるからです。kubectl taint のリファレンスの例のとおり、キーの後ろに - だけを付けると、そのキーの Taint がすべて外れます。NoSchedule と NoExecute の 2 つの Taint が、この 1 行で外れるはずです。ラベルも第31回と同じく、キーの後ろに - を付けて外します。

console
$ kubectl taint nodes <1 つ目のワーカーの名前> dedicated- --context kind-multi-node
$ kubectl label nodes <1 つ目のワーカーの名前> dedicated- --context kind-multi-node

参考:kubectl taint | Kubernetes、kubectl label | Kubernetes

残っていないかを確かめます。

console
$ kubectl get pods --context kind-multi-node
$ kubectl get nodes -L dedicated --context kind-multi-node
$ kubectl get nodes -o='custom-columns=NodeName:.metadata.name,TaintKey:.spec.taints[*].key,TaintValue:.spec.taints[*].value,TaintEffect:.spec.taints[*].effect' --context kind-multi-node

1 つ目のコマンドで Pod が Terminating で表示されていたら、少し待ってからもう一度実行します。準備のときと同じ、見つからないことを知らせる 1 行が出れば、Pod は削除できています。2 つ目のコマンドで DEDICATED 列がどのノードでも空で、3 つ目のコマンドで dedicated の Taint がどのノードにも無く、コントロールプレーンのノードの Taint だけが残っていれば、ラベルと Taint も外せています。

最後に、クラスタを削除し、消えたことを確かめます。

console
$ kind delete cluster --name multi-node
$ kind get clusters

kind delete cluster は存在しないクラスタを指定してもエラーを返しません。kind get clusters に multi-node が表示されなくなったことを確かめます。kubeconfig に kind-multi-node のコンテキストが残っていたら、第3回の片付けと同じく kubectl config delete-context kind-multi-node で消します。

参考:Quick Start | kind、kubectl config | Kubernetes

注意点

Taint と Toleration でつまずきやすい点と、バージョンで変わる点を、次の表にまとめます。

項目内容
Toleration は置き場所を保証しない合う Taint のあるノードへのスケジュールを許すだけ。特定のノードだけに置きたいなら、Node Affinity か nodeSelector と組み合わせる
NoSchedule は動いている Pod を追い出さない動いている Pod を追い出すのは NoExecute だけ
PreferNoSchedule は保証しない許容しない Pod をそのノードに置くのを避けようとするだけで、置かれることもある
tolerationSeconds は NoExecute のときだけほかの効果の Toleration では無視される。0 以下の値はすぐに追い出す扱いになる
Pod に自動で足される Tolerationnode.kubernetes.io/not-ready と node.kubernetes.io/unreachable の Toleration が tolerationSeconds 300 で足される。v1.37 時点では、足す DefaultTolerationSeconds は既定で有効

参考:Taints and Tolerations | Kubernetes、Toleration | Kubernetes API Reference、Admission Control in Kubernetes | Kubernetes

関連記事

ほかの Pod のラベルをもとに Pod を分散させたりまとめたりする Pod 間のアンチアフィニティと、Pod Topology Spread Constraints については、以下の記事で解説しています。

基本仕様 ・ 第33回 Pod Anti-Affinity と Topology Spread の違いほかの Pod のラベルで置き場所を決める Pod 間のアフィニティ・アンチアフィニティと、ドメインごとの Pod の数の偏りを抑える Pod Topology Spread Constraints の違いを図で説明し、kind のマルチノードのクラスタで Pod をまとめる配置と分散させる配置を比べる手順を紹介します。

この回の組み込みの Taint の表にあった、スケジュールできない状態のノードと、ノードの drain については、以下の記事で解説しています。

基本仕様 ・ 第36回 PodDisruptionBudget の仕組みノードの drain のような自発的な中断で、同時に止まる Pod の数を抑える PodDisruptionBudget の仕組みを図で説明します。Eviction API による退避、minAvailable と maxUnavailable、kubectl drain と cordon を押さえ、kind のクラスタで drain が PDB に断られながら Pod を 1 つずつ退避させる様子を観察します。

Kubernetes 基本仕様の目次へ