k8sでdrain/cordonを実行するとTaints `node.kubernetes.io/unschedulable:NoSchedule` が付与される

Taintsの使い道についてあまりピンとこなくて、使い道を調べてたらこのことを知ったので記事にした。

Taintsのユースケース

k8sのドキュメントにユースケースが書かれている。

kubernetes.io

  • 専有NodeにTaintsを付与する
  • 特殊なハードウェアを備えたNodeにTaintsを付与して、そのハードウェアを必要としないPodが配置されないようにする
  • Nodeに問題が起きたときにPodがそのNodeに配置されないようにする

さらに読みすすめると「taintを基にした排除」の項にこんなことが書かれていた。

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

...

  • node.kubernetes.io/unschedulable: Nodeがスケジューリングできない場合。

スケジューリングできない場合ということは、もしかしてdrainやcordonを使うときにtaintsが付与されるのかな? と思ったので実際に確認してみた。

drain/cordonでもTaintsが使われている

drainを実行するとPodを退避する前にcordonが実行されて、Taintにnode.kubernetes.io/unschedulable:NoScheduleが付与される。 ざっくりこんな流れ。

  1. 対象Nodeでcordonが実行される
  2. 対象NodeのTaintsにnode.kubernetes.io/unschedulable:NoScheduleが付与されて、新しいPodが配置されないようにする
  3. Podの退避or停止が実行される
  4. 退避されたPodは別Nodeで起動する

kindで検証してみる

まずは環境用意

kindでローカルにクラスタを構築して検証する。

こんなcluster.yamlを用意する。 ちなみに https://kind.sigs.k8s.io/docs/user/configuration/#name-your-cluster から引っ張ってきた。

$ cat cluster.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
- role: worker
$ kind create cluster --name sample --config=cluster.yaml
Creating cluster "sample" ...
 ✓ Ensuring node image (kindest/node:v1.24.0) 🖼
 ✓ Preparing nodes 📦 📦 📦 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
 ✓ Joining worker nodes 🚜
Set kubectl context to "kind-sample"
You can now use your cluster with:

kubectl cluster-info --context kind-sample

Thanks for using kind! 😊

出来上がったノード。

$ kubectl get nodes
NAME                   STATUS   ROLES           AGE   VERSION
sample-control-plane   Ready    control-plane   69s   v1.24.0
sample-worker          Ready    <none>          32s   v1.24.0
sample-worker2         Ready    <none>          32s   v1.24.0
sample-worker3         Ready    <none>          32s   v1.24.0

各sample-workerのLabelsとTaintsはこんな感じ。 3台ともすべて同じ。

$ kubectl describe node sample-worker
Name:               sample-worker
Roles:              <none>
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/os=linux
                    kubernetes.io/arch=amd64
                    kubernetes.io/hostname=sample-worker
                    kubernetes.io/os=linux
Annotations:        kubeadm.alpha.kubernetes.io/cri-socket: unix:///run/containerd/containerd.sock
                    node.alpha.kubernetes.io/ttl: 0
                    volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp:  Tue, 12 Jul 2022 23:28:35 +0900
Taints:             <none>    👈node作成直後なので何も付与されていない
Unschedulable:      false

...

全sample-workerに適当なラベルenv=testingを付与。

$ kubectl label node sample-worker env=testing
node/sample-worker labeled

$ kubectl label node sample-worker2 env=testing
node/sample-worker2 labeled

$ kubectl label node sample-worker3 env=testing
node/sample-worker3 labeled

nginxのPodをenv=testingなNodeで起動する。

$ cat nginx-pod.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    env: testing
  name: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      env: testing
  template:
    metadata:
      labels:
        env: testing
    spec:
      containers:
      - image: nginx
        name: nginx
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                - key: env
                  operator: In
                  values:
                  - testing

$ kubectl apply -f nginx-pod.yaml
deployment.apps/nginx created

$ kubectl get deployment -o wide
NAME    READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES   SELECTOR
nginx   3/3     3            3           28s   nginx        nginx    env=testing

$ kubectl get pods -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP           NODE             NOMINATED NODE   READINESS GATES
nginx-575bc5fb56-bc5cj   1/1     Running   0          48s   10.244.3.2   sample-worker2   <none>           <none>
nginx-575bc5fb56-q4g2v   1/1     Running   0          48s   10.244.1.2   sample-worker    <none>           <none>
nginx-575bc5fb56-srsx4   1/1     Running   0          48s   10.244.2.2   sample-worker3   <none>           <none>

これで準備ができた。

sample-workerを対象にdrainを実行してみる

ログ通り、cordonが実行された後に各podが退避されていることがわかる。

$ kubectl drain sample-worker --ignore-daemonsets
node/sample-worker cordoned
WARNING: ignoring DaemonSet-managed Pods: kube-system/kindnet-wzcdv, kube-system/kube-proxy-dwkzb
evicting pod default/nginx-575bc5fb56-q4g2v
pod/nginx-575bc5fb56-q4g2v evicted
node/sample-worker drained

sample-workerのステータスに SchedulingDisabled が追加されており、sample-worker上で動いていたPod 消えて別Nodesample-worker3で新しいPodが起動する。

$ kubectl get nodes -o wide
NAME                   STATUS                     ROLES           AGE     VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE       KERNEL-VERSION              CONTAINER-RUNTIME
sample-control-plane   Ready                      control-plane   6m40s   v1.24.0   172.23.0.2    <none>        Ubuntu 21.10   4.18.0-348.el8.0.2.x86_64   containerd://1.6.4
sample-worker          Ready,SchedulingDisabled   <none>          6m3s    v1.24.0   172.23.0.5    <none>        Ubuntu 21.10   4.18.0-348.el8.0.2.x86_64   containerd://1.6.4
sample-worker2         Ready                      <none>          6m3s    v1.24.0   172.23.0.4    <none>        Ubuntu 21.10   4.18.0-348.el8.0.2.x86_64   containerd://1.6.4
sample-worker3         Ready                      <none>          6m3s    v1.24.0   172.23.0.3    <none>        Ubuntu 21.10   4.18.0-348.el8.0.2.x86_64   containerd://1.6.4

$ kubectl get pods -o wide
NAME                     READY   STATUS    RESTARTS   AGE     IP           NODE             NOMINATED NODE   READINESS GATES
nginx-575bc5fb56-bc5cj   1/1     Running   0          2m18s   10.244.3.2   sample-worker2   <none>           <none>
nginx-575bc5fb56-gx5c7   1/1     Running   0          32s     10.244.2.3   sample-worker3   <none>           <none>
nginx-575bc5fb56-srsx4   1/1     Running   0          2m18s   10.244.2.2   sample-worker3   <none>           <none>

describeを見るとnode.kubernetes.io/unschedulable:NoScheduleというTaintsが付与されていることがわかる。

$ kubectl describe node sample-worker
Name:               sample-worker
Roles:              <none>
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/os=linux
                    env=testing
                    kubernetes.io/arch=amd64
                    kubernetes.io/hostname=sample-worker
                    kubernetes.io/os=linux
Annotations:        kubeadm.alpha.kubernetes.io/cri-socket: unix:///run/containerd/containerd.sock
                    node.alpha.kubernetes.io/ttl: 0
                    volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp:  Tue, 12 Jul 2022 23:28:35 +0900
Taints:             node.kubernetes.io/unschedulable:NoSchedule    👈付与された
Unschedulable:      true

...

Taintsを削除する場合

Taints node.kubernetes.io/unschedulable:NoSchedule を削除するときはuncordonすればOK。

$ kubectl uncordon sample-worker
node/sample-worker uncordoned

$ kubectl get node -o wide
NAME                   STATUS   ROLES           AGE   VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE       KERNEL-VERSION              CONTAINER-RUNTIME
sample-control-plane   Ready    control-plane   11m   v1.24.0   172.23.0.2    <none>        Ubuntu 21.10   4.18.0-348.el8.0.2.x86_64   containerd://1.6.4
sample-worker          Ready    <none>          10m   v1.24.0   172.23.0.5    <none>        Ubuntu 21.10   4.18.0-348.el8.0.2.x86_64   containerd://1.6.4
sample-worker2         Ready    <none>          10m   v1.24.0   172.23.0.4    <none>        Ubuntu 21.10   4.18.0-348.el8.0.2.x86_64   containerd://1.6.4
sample-worker3         Ready    <none>          10m   v1.24.0   172.23.0.3    <none>        Ubuntu 21.10   4.18.0-348.el8.0.2.x86_64   containerd://1.6.4

$ kubectl describe node sample-worker
Name:               sample-worker
Roles:              <none>
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/os=linux
                    env=testing
                    kubernetes.io/arch=amd64
                    kubernetes.io/hostname=sample-worker
                    kubernetes.io/os=linux
Annotations:        kubeadm.alpha.kubernetes.io/cri-socket: unix:///run/containerd/containerd.sock
                    node.alpha.kubernetes.io/ttl: 0
                    volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp:  Tue, 12 Jul 2022 23:28:35 +0900
Taints:             <none>    👈消えてる
Unschedulable:      false

...