以下の内容はhttps://numb86-tech.hatenablog.com/entry/2025/11/05/200519より取得しました。


Pod が配置される Node を Taint と Toleration で制御する

Kubernetes には Taint と Toleration という仕組みがあり、これらを使うことで Pod が配置される Node を制御できる。この Node にはこの Pod しか配置を許可しない、といった指定が可能になる。この記事では Taint と Toleration の基本的な使い方を見ていく。

この記事の内容は Kubernetes の1.33で動作確認した。

Taint

Kubernetes では Node に Pod が配置されるわけだが、 Node の属性のひとつに Taint というものがある。
これを使うと、特定の条件を満たした Pod 以外はその Node に配置されないようになる。

Taint はkey,value,effectの組み合わせで設定する。
keyvalueは Taint のラベルのようなもので、 Taint を識別するために使う。valueはオプションなので指定しなくてもよい。
effectは 3 種類あり、具体的にどのようなルールを課すか指定する。

  • NoExecute
    • 新規 Pod のスケジューリングをブロックし、既存の Pod も、条件を満たさない場合は退去させる
      • 後述する Toleration でTolerationSecondsが設定されている場合、即座には退去されず、指定された秒数だけ退去が猶予される
  • NoSchedule
    • 新規 Pod のスケジューリングをブロックする。既存の Pod には影響しない。
  • PreferNoSchedule
    • 可能な限り新規 Pod のスケジューリングを避けるが、他に選択肢がない場合は配置を許可する

例を示していくための環境として、 2 つの Node があるクラスタを Amazon EKS に用意する。

terraform {
  cloud {
    organization = "sample-org"
    workspaces {
      name = "eks-workspace"
    }
  }
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "ap-northeast-1"
}

# IAM ロールやサブネットは既に別途作成済みであり、 Terraform では定義していない

data "aws_iam_role" "eks_cluster_role" {
  name = "my-eks-cluster-role"
}

data "aws_iam_role" "eks_node_role" {
  name = "my-eks-node-role"
}

locals {
  subnet_ids = [
    "subnet-111",
    "subnet-222"
  ]
}

resource "aws_eks_cluster" "my_cluster" {
  name     = "my-cluster"
  role_arn = data.aws_iam_role.eks_cluster_role.arn

  vpc_config {
    subnet_ids = local.subnet_ids
  }
}

resource "aws_eks_node_group" "node_x" {
  cluster_name    = aws_eks_cluster.my_cluster.name
  node_group_name = "node-x"
  node_role_arn   = data.aws_iam_role.eks_node_role.arn
  subnet_ids      = local.subnet_ids

  scaling_config {
    min_size     = 1
    max_size     = 1
    desired_size = 1
  }

  labels = {
    node_id = "x"
  }

  instance_types = ["t3.small"]
}

resource "aws_eks_node_group" "node_y" {
  cluster_name    = aws_eks_cluster.my_cluster.name
  node_group_name = "node-y"
  node_role_arn   = data.aws_iam_role.eks_node_role.arn
  subnet_ids      = local.subnet_ids

  scaling_config {
    min_size     = 1
    max_size     = 1
    desired_size = 1
  }

  labels = {
    node_id = "y"
  }

  instance_types = ["t3.small"]
}

上記の内容を$ terraform applyすると、 Node が 2 つ作られる。

$ kubectl get nodes -L node_id
NAME                                           STATUS   ROLES    AGE   VERSION               NODE_ID
ip-10-0-0-10.ap-northeast-1.compute.internal   Ready    <none>   12m   v1.33.5-eks-113cf36   x
ip-10-0-1-34.ap-northeast-1.compute.internal   Ready    <none>   12m   v1.33.5-eks-113cf36   y

どちらにも Taint は設定されていない。

$ kubectl get nodes -l node_id=x -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
NAME                                           TAINTS
ip-10-0-0-10.ap-northeast-1.compute.internal   <none>

$ kubectl get nodes -l node_id=y -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
NAME                                           TAINTS
ip-10-0-1-34.ap-northeast-1.compute.internal   <none>

この状態で以下の内容のmanifest.yamlを用意し$ kubectl apply -f manifest.yamlすると、 Pod が 2 つ作られる。

apiVersion: v1
kind: Pod
metadata:
  name: pod-a
spec:
  containers:
  - name: busybox
    image: docker.io/library/busybox:1.36
    command: ["sleep", "infinity"]
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-b
spec:
  containers:
  - name: busybox
    image: docker.io/library/busybox:1.36
    command: ["sleep", "infinity"]

この際、pod-apod-bがそれぞれどの Node に配置されるかは、制御できない。Kubernetes によって配置されるが、pod-anode-xpod-bnode-yになることもあれば、両方の pod がnode-yに配置されることもある。

Terraform の定義内容のうちnode_xを以下に書き換える。具体的にはtaintフィールドを追加する。NoExecuteではなくNO_EXECUTEと書くので注意する。

resource "aws_eks_node_group" "node_x" {
  cluster_name    = aws_eks_cluster.my_cluster.name
  node_group_name = "node-x"
  node_role_arn   = data.aws_iam_role.eks_node_role.arn
  subnet_ids      = local.subnet_ids

  scaling_config {
    min_size     = 1
    max_size     = 1
    desired_size = 1
  }

  labels = {
    node_id = "x"
  }

  taint {
    key    = "key1"
    value  = "value1"
    effect = "NO_EXECUTE"
  }

  instance_types = ["t3.small"]
}

$ terraform applyしてから確認するとnode-xに Taint が付与されている。

$ kubectl get nodes -l node_id=x -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
NAME                                           TAINTS
ip-10-0-0-10.ap-northeast-1.compute.internal   [map[effect:NoExecute key:key1 value:value1]]

$ kubectl get nodes -l node_id=y -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
NAME                                           TAINTS
ip-10-0-1-34.ap-northeast-1.compute.internal   <none>

これでもうnode-xには Pod は配置されなくなるはずである。

$ kubectl delete -f manifest.yamlしてから再度$ kubectl apply -f manifest.yamlをして Pod を作り直す。
そうすると以下のように Pod は 2 つともnode-yに配置される。Pod を何度作り直しても同じ結果になる。

$ kubectl get pods -o wide
NAME    READY   STATUS    RESTARTS   AGE   IP           NODE                                           NOMINATED NODE   READINESS GATES
pod-a   1/1     Running   0          8s    10.0.1.183   ip-10-0-1-34.ap-northeast-1.compute.internal   <none>           <none>
pod-b   1/1     Running   0          8s    10.0.1.82    ip-10-0-1-34.ap-northeast-1.compute.internal   <none>           <none>

似たような機能としてnodeSelectorがあるがこれは Pod 側が指定するもの。Taint は逆に Node 側が指定する。

Toleration

先程「特定の条件を満たした Pod 以外はその Node に配置されないようになる」と書いたが、その「特定の条件」が Toleration 。
Toleration は Pod の属性。Toleration が設定されている Pod はその設定内容に応じて、 Taint が設定されている Node への配置が特別に許可されるようになる。

設定できるフィールドは 5 つあるが、省略可能なものも多く、全てを設定することは稀。

  • key
    • 対象となる Taint の key
    • 後述のoperatorExistsの場合は省略可能
  • value
    • 対象となる Taint の value
    • 後述のoperatorExistsの場合は不要
  • operator
    • どのようなロジックで対象となる Taint を決定するかを指定する
    • EqualもしくはExistsを指定する
      • Equal
        • keyvalueの両方が一致する Taint を対象とする
      • Exists
        • keyが指定されている場合、そのkeyを持つ Taint を対象とする
        • keyが指定されていない場合、すべての Taint を対象とする
  • effect
    • どの Effect (PreferNoSchedule or NoSchedule or NoExecute)の Taint を許容するかを指定する
    • 省略した場合は全ての Effect を許容したことになる
  • tolerationSeconds
    • NoExecuteの Taint が付与されてから、実際に退去されるまでの猶予時間

こちらも例を示す。

まず、node_yを書き換えてこちらにも Taint を設定する。

resource "aws_eks_node_group" "node_y" {
  cluster_name    = aws_eks_cluster.my_cluster.name
  node_group_name = "node-y"
  node_role_arn   = data.aws_iam_role.eks_node_role.arn
  subnet_ids      = local.subnet_ids

  scaling_config {
    min_size     = 1
    max_size     = 1
    desired_size = 1
  }

  labels = {
    node_id = "y"
  }

  taint {
    key    = "key2"
    value  = "value2"
    effect = "NO_SCHEDULE"
  }

  instance_types = ["t3.small"]
}

$ terraform applyすると以下の内容になる。

$ kubectl get nodes -l node_id=x -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
NAME                                           TAINTS
ip-10-0-0-10.ap-northeast-1.compute.internal   [map[effect:NoExecute key:key1 value:value1]]

$ kubectl get nodes -l node_id=y -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
NAME                                           TAINTS
ip-10-0-1-34.ap-northeast-1.compute.internal   [map[effect:NoSchedule key:key2 value:value2]]

node-xnode-yも Taint が設定されているため、対応する Toleration が設定された Pod しか配置できなくなった。

そしてmanifest.yamlを以下の内容にして$ kubectl apply -f manifest.yamlする。

apiVersion: v1
kind: Pod
metadata:
  name: pod-a
spec:
  tolerations:
    - key: "key1"
      value: "value1"
      operator: "Equal"
      effect: "NoExecute"
      tolerationSeconds: 3600
  containers:
    - name: busybox
      image: docker.io/library/busybox:1.36
      command: ["sleep", "infinity"]
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-b
spec:
  tolerations:
    - operator: "Exists"
      effect: "NoSchedule"
  containers:
    - name: busybox
      image: docker.io/library/busybox:1.36
      command: ["sleep", "infinity"]
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-c
spec:
  tolerations:
    - operator: "Exists"
      effect: "PreferNoSchedule"
  containers:
    - name: busybox
      image: docker.io/library/busybox:1.36
      command: ["sleep", "infinity"]

そうすると以下のようになる。

$ kubectl get nodes -L node_id
NAME                                           STATUS   ROLES    AGE    VERSION               NODE_ID
ip-10-0-0-10.ap-northeast-1.compute.internal   Ready    <none>   102m   v1.33.5-eks-113cf36   x
ip-10-0-1-34.ap-northeast-1.compute.internal   Ready    <none>   101m   v1.33.5-eks-113cf36   y

$ kubectl get pods -o wide
NAME    READY   STATUS    RESTARTS   AGE   IP          NODE                                           NOMINATED NODE   READINESS GATES
pod-a   1/1     Running   0          5s    10.0.0.60   ip-10-0-0-10.ap-northeast-1.compute.internal   <none>           <none>
pod-b   1/1     Running   0          5s    10.0.1.82   ip-10-0-1-34.ap-northeast-1.compute.internal   <none>           <none>
pod-c   0/1     Pending   0          5s    <none>      <none>                                         <none>           <none>

pod-anode-xに、pod-bnode-yに配置されている。そしてpod-cはどこにも配置されずPendingのままになっている。

Toleration の内容を確認し、なぜこうなるのかを解説する。

  • pod-a
    • Toleration の内容
      • tolerations:
          - key: "key1"
            value: "value1"
            operator: "Equal"
            effect: "NoExecute"
        
    • Equalなのでkeyvalueの両方が一致する Taint に配置可能
      • つまりkey1:value1
      • そしてその Taint はnode-xが該当する
    • effect はNoExecuteだがそれもnode-xと一致する
    • node-yの Taint は条件が合わない
    • そのためnode-xに配置される
  • pod-b
    • Toleration の内容
      • tolerations:
          - operator: "Exists"
            effect: "NoSchedule"
        
    • Existskeyが指定されていないので、すべての Taint が対象になる
    • NoSchedulenode-xは異なりnode-yは一致する
    • そのためnode-yに配置される
  • pod-c
    • Toleration の内容
      • tolerations:
          - operator: "Exists"
            effect: "PreferNoSchedule"
        
    • Existskeyが指定されていないので、すべての Taint が対象になる
    • PreferNoSchedulenode-xともnode-yとも異なる
    • そのためどちらの Node にも配置されない

自動付与

Taint も Toleration も、 Kubernetes によって自動付与されることがある。

例えば先程作成したpod-aの Toleration を見てみる。

$ kubectl get pod pod-a -o json | jq '.spec.tolerations'
[
  {
    "effect": "NoExecute",
    "key": "key1",
    "operator": "Equal",
    "tolerationSeconds": 3600,
    "value": "value1"
  },
  {
    "effect": "NoExecute",
    "key": "node.kubernetes.io/not-ready",
    "operator": "Exists",
    "tolerationSeconds": 300
  },
  {
    "effect": "NoExecute",
    "key": "node.kubernetes.io/unreachable",
    "operator": "Exists",
    "tolerationSeconds": 300
  }
]

マニフェストファイルに記載したkey1以外にも 2 つ Toleration があることがわかる。これらが Kubernetes によって自動的に付与されたもの。

Taint も、自動付与されることがある。

それを確認するためにまずnode_xを以下の内容にする。つまりtaintを消す。

resource "aws_eks_node_group" "node_x" {
  cluster_name    = aws_eks_cluster.my_cluster.name
  node_group_name = "node-x"
  node_role_arn   = data.aws_iam_role.eks_node_role.arn
  subnet_ids      = local.subnet_ids

  scaling_config {
    min_size     = 1
    max_size     = 1
    desired_size = 1
  }

  labels = {
    node_id = "x"
  }

  instance_types = ["t3.small"]
}

これで$ terraform applyすると Taint は何も無くなっている。

$ kubectl get nodes -l node_id=x -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
NAME                                           TAINTS
ip-10-0-0-10.ap-northeast-1.compute.internal   <none>

次にmanifest.yamlを以下の内容にして$ kubectl apply -f manifest.yamlする。

apiVersion: v1
kind: Pod
metadata:
  name: disk-hog
spec:
  nodeSelector:
    node_id: "x"
  containers:
    - name: busybox
      image: docker.io/library/busybox:1.36
      command: ["/bin/sh"]
      args:
        ["-c", "dd if=/dev/zero of=/tmp/bigfile bs=1M count=16000; sleep 3600"]

この Pod はnode-xに配置され、指定された容量だけディスクへの書き込みを行う。

そうすると、ディスク容量の圧迫を意味するnode.kubernetes.io/disk-pressureが付与される。
NoScheduleなので、(対応する Toleration が設定された Pod を除いて)この Node には Pod が配置されない。
NoExecuteではないのでdisk-hogが退去させられることもない。

$ kubectl get nodes -l node_id=x -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
NAME                                           TAINTS
ip-10-0-0-10.ap-northeast-1.compute.internal   [map[effect:NoSchedule key:node.kubernetes.io/disk-pressure timeAdded:2025-11-04T14:20:00Z]]

$ kubectl delete -f manifest.yamldisk-hogを消して暫くすると、 Taint は消える。

$ kubectl get nodes -l node_id=x -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
NAME                                           TAINTS
ip-10-0-0-10.ap-northeast-1.compute.internal   <none>



以上の内容はhttps://numb86-tech.hatenablog.com/entry/2025/11/05/200519より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14