Kubernetes theory
September 24, 2022

Taint/Tolerations

Taint - это внутренний механизм, распределения подов по нодам кластера. Позволяет ограничить для подов запуск и шедуллинг на определённых нодах. Устанавливается на ноду кластера. Tolerations - позволяет сделать исключение для отдельных подов и позволяющий им запускаться на нодах с установленным taints.

Taint – это NodeAffinity наоборот. Если nodeAffinity говорит scheduler-у, где он должен размещать pod-ы, то taint говорит, где pod-ы размещать нельзя. Любой taint запрещает размещение на машине любых подов (есть одно исключение, про него дальше). Однако можно создать под, который будет игнорировать tolerations этот запрет – и данный pod запустится на данной машине.

Особенности.

1. На master nodes тоже есть taint. Это taint node-role.kubernetes.io/master, запрещающий аллоцировать на них любой под.

2. Taint никак не связан с безопасностью в кластере. Это просто механизм ограничивающий запуск и шедулинг подов. Защитить кластер он никак не может.

3. Taint and tolerations не предписывают поду размещаться на конкретном узле. На котором установлен taint и для которого у пода установлен tolerations. Такой под может разместиться и на других узлах кластера.

Посмотрим список нод в кластере.

kubectl get nodes
NAME                        STATUS   ROLES    AGE    VERSION
cl10sucmg9fveg2v9mka-ijym   Ready    <none>   15d    v1.22.6
cl16hmah0j04fn6h944k-ilis   Ready    <none>   66m    v1.22.6
cl18jmsftl4879u5tuu0-izym   Ready    <none>   4h9m   v1.22.6

Установим на одну из них политику taint.

kubectl taint nodes cl18jmsftl4879u5tuu0-izym cam=taint:NoSchedule
kubectl taint nodes cl18jmsftl4879u5tuu0-izym cam=taint:PreferNoSchedule
kubectl taint nodes cl18jmsftl4879u5tuu0-izym cam=taint:NoExecute

Здесь cam=taint - это просто метка, которая в общем-то может быть совершенно произвольная.

Создадим Deployment и посмотрим будут ли на ноду с taint аллоцироваться поды.

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cam-nginx
spec:
  replicas: 16
  selector:
    matchLabels:
      cam: nginx
  template:
    metadata:
      labels:
        cam: nginx
    spec:
      containers:
        - name: cam-nginx
          image: nginx:latest
          imagePullPolicy: IfNotPresent 
      restartPolicy: Always
EOF

Посмотрим на дескрайб ноды.

kubectl describe no cl18jmsftl4879u5tuu0-izym
Name:               cl18jmsftl4879u5tuu0-izym
Roles:              <none>
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/instance-type=standard-v3
                    beta.kubernetes.io/os=linux
                    cam=taint
                    failure-domain.beta.kubernetes.io/zone=ru-central1-c
                    kubernetes.io/arch=amd64
                    kubernetes.io/hostname=cl18jmsftl4879u5tuu0-izym
                    kubernetes.io/os=linux
                    node.kubernetes.io/instance-type=standard-v3
                    node.kubernetes.io/kube-proxy-ds-ready=true
                    node.kubernetes.io/masq-agent-ds-ready=true
                    node.kubernetes.io/node-problem-detector-ds-ready=true
                    topology.kubernetes.io/zone=ru-central1-c
                    yandex.cloud/node-group-id=catlsf230utg39omk0nc
                    yandex.cloud/pci-topology=k8s
                    yandex.cloud/preemptible=true
Annotations:        csi.volume.kubernetes.io/nodeid:
                      {"disk-csi-driver.mks.ycloud.io":"ef36k7398e4u22vp3oji","io.ycloud.mks.disk-csi-driver":"ef36k7398e4u22vp3oji"}
                    node.alpha.kubernetes.io/ttl: 0
                    projectcalico.org/IPv4Address: 10.130.0.33/24
                    volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp:  Fri, 23 Sep 2022 17:35:42 +0300
Taints:             cam=taint:NoExecute
                    cam=taint:NoSchedule
Unschedulable:      false
Lease:
  HolderIdentity:  cl18jmsftl4879u5tuu0-izym
  AcquireTime:     <unset>
  RenewTime:       Sat, 24 Sep 2022 13:05:23 +0300
Conditions:
  Type                          Status    LastHeartbeatTime                 LastTransitionTime                Reason                          Message
  ----                          ------    -----------------                 ------------------                ------                          -------
  KernelDeadlock                False     Sat, 24 Sep 2022 13:03:08 +0300   Fri, 23 Sep 2022 17:35:58 +0300   KernelHasNoDeadlock             kernel has no deadlock
  ReadonlyFilesystem            False     Sat, 24 Sep 2022 13:03:08 +0300   Fri, 23 Sep 2022 17:35:58 +0300   FilesystemIsNotReadOnly         Filesystem is not read-only
  FrequentUnregisterNetDevice   Unknown   Sat, 24 Sep 2022 13:03:08 +0300   Fri, 23 Sep 2022 17:35:59 +0300   NoFrequentUnregisterNetDevice   node is functioning properly
  FrequentKubeletRestart        Unknown   Sat, 24 Sep 2022 13:03:08 +0300   Fri, 23 Sep 2022 17:35:59 +0300   NoFrequentKubeletRestart        kubelet is functioning properly
  FrequentDockerRestart         Unknown   Sat, 24 Sep 2022 13:03:08 +0300   Fri, 23 Sep 2022 17:35:59 +0300   NoFrequentDockerRestart         docker is functioning properly
  FrequentContainerdRestart     Unknown   Sat, 24 Sep 2022 13:03:08 +0300   Fri, 23 Sep 2022 17:35:59 +0300   NoFrequentContainerdRestart     containerd is functioning properly
  CorruptDockerOverlay2         Unknown   Sat, 24 Sep 2022 13:03:08 +0300   Fri, 23 Sep 2022 17:35:59 +0300   NoCorruptDockerOverlay2         docker overlay2 is functioning properly
  NetworkUnavailable            False     Fri, 23 Sep 2022 17:35:50 +0300   Fri, 23 Sep 2022 17:35:50 +0300   RouteCreated                    RouteController created a route
  MemoryPressure                False     Sat, 24 Sep 2022 13:02:55 +0300   Fri, 23 Sep 2022 17:35:41 +0300   KubeletHasSufficientMemory      kubelet has sufficient memory available
  DiskPressure                  False     Sat, 24 Sep 2022 13:02:55 +0300   Fri, 23 Sep 2022 17:35:41 +0300   KubeletHasNoDiskPressure        kubelet has no disk pressure
  PIDPressure                   False     Sat, 24 Sep 2022 13:02:55 +0300   Fri, 23 Sep 2022 17:35:41 +0300   KubeletHasSufficientPID         kubelet has sufficient PID available
  Ready                         True      Sat, 24 Sep 2022 13:02:55 +0300   Fri, 23 Sep 2022 17:36:01 +0300   KubeletReady                    kubelet is posting ready status. AppArmor enabled
Addresses:
  InternalIP:  10.130.0.33
  ExternalIP:  51.250.46.162
  Hostname:    cl18jmsftl4879u5tuu0-izym
Capacity:
  cpu:                2
  ephemeral-storage:  99034708Ki
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             4026060Ki
  pods:               32
Allocatable:
  cpu:                1930m
  ephemeral-storage:  49394455606
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             2772684Ki
  pods:               32
System Info:
  Machine ID:                 230000073c6888b69deb8cf2a12dc048
  System UUID:                23000007-3c66-a1c6-9438-9e10bf91e272
  Boot ID:                    a6d70f56-d00b-4ddb-b4e8-43fbf4b06a88
  Kernel Version:             5.4.0-124-generic
  OS Image:                   Ubuntu 20.04.4 LTS
  Operating System:           linux
  Architecture:               amd64
  Container Runtime Version:  containerd://1.6.7
  Kubelet Version:            v1.22.6
  Kube-Proxy Version:         v1.22.6
PodCIDR:                      192.168.2.64/26
PodCIDRs:                     192.168.2.64/26
ProviderID:                   yandex://ef36k7398e4u22vp3oji
Non-terminated Pods:          (5 in total)
  Namespace                   Name                         CPU Requests  CPU Limits  Memory Requests  Memory Limits  Age
  ---------                   ----                         ------------  ----------  ---------------  -------------  ---
  kube-system                 calico-node-bddbg            250m (12%)    0 (0%)      0 (0%)           0 (0%)         19h
  kube-system                 ip-masq-agent-klbsq          10m (0%)      0 (0%)      16Mi (0%)        0 (0%)         19h
  kube-system                 kube-proxy-92pkl             100m (5%)     0 (0%)      0 (0%)           0 (0%)         19h
  kube-system                 npd-v0.8.0-x79nm             20m (1%)      200m (10%)  20Mi (0%)        100Mi (3%)     19h
  kube-system                 yc-disk-csi-node-v2-76phn    30m (1%)      600m (31%)  96Mi (3%)        600Mi (22%)    19h
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests    Limits
  --------           --------    ------
  cpu                410m (21%)  800m (41%)
  memory             132Mi (4%)  700Mi (25%)
  ephemeral-storage  0 (0%)      0 (0%)
  hugepages-1Gi      0 (0%)      0 (0%)
  hugepages-2Mi      0 (0%)      0 (0%)
Events:              <none>

Ни одного пода с наименованием cam-nginx!

Удалим данный deploy:

kubectl delete deploy cam-nginx

Tolerations

Если taint - это замок, то toleration - это ключ от этого замка. Он позволяет отдельным подам аллоцироваться на ноду с установленным taint.

Модернизируем наш Deployment и взглянем на дескрайб ноды.

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cam-nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      cam: nginx
  template:
    metadata:
      labels:
        cam: nginx
    spec:
      containers:
        - name: cam-nginx
          image: nginx:latest
          imagePullPolicy: IfNotPresent 
      restartPolicy: Always
      tolerations:
      - key: "cam"
        operator: "Exists"
        effect: "NoSchedule"
      - key: "cam"
        operator: "Exists"
        effect: "NoExecute"
EOF

Блок toleration здесь разрешает установить поды из этого deployment на ноду с двумя установленными taint-политиками.

      tolerations:
      - key: "cam"
        operator: "Exists"
        effect: "NoSchedule"
      - key: "cam"
        operator: "Exists"
        effect: "NoExecute"

Посмотрим на кусок вывода дескрайба:

Non-terminated Pods:          (6 in total)
  Namespace                   Name                         CPU Requests  CPU Limits  Memory Requests  Memory Limits  Age
  ---------                   ----                         ------------  ----------  ---------------  -------------  ---
  default                     cam-nginx-c77857886-78sb5    0 (0%)        0 (0%)      0 (0%)           0 (0%)         69s
  kube-system                 calico-node-bddbg            250m (12%)    0 (0%)      0 (0%)           0 (0%)         19h
  kube-system                 ip-masq-agent-klbsq          10m (0%)      0 (0%)      16Mi (0%)        0 (0%)         19h
  kube-system                 kube-proxy-92pkl             100m (5%)     0 (0%)      0 (0%)           0 (0%)         19h
  kube-system                 npd-v0.8.0-x79nm             20m (1%)      200m (10%)  20Mi (0%)        100Mi (3%)     19h
  kube-system                 yc-disk-csi-node-v2-76phn    30m (1%)      600m (31%)  96Mi (3%)        600Mi (22%)    19h

Вот теперь под успешно аллоцировался!

Когда нужно использовать taints/tolerations?

Это полезный инструмент, когда необходимо скрыть ноду со специфическими ресурсами от большинства подов. Например, когда есть нода с GPU шедулить на неё поды, которым эта GPU необходима для работы. Например, для каких-то расчётов. Также это может быть какое-то специфическое шифрование на аппаратном уровне.

Особенности взаимодействия taint и pod:

1. Если навесить политику NoSchedule на ноду где уже зашедулены поды, то они никуда не денуться.
2. Если добавить политику NoExecute, то поды на этой ноде перейдут в состояние Pending.
3. У taint наивысший приоритет среди инструкций. Это означает, что даже если у пода установлены nodeName/nodeSelector или podAffinity/podAntiAffinity, то он всё равно не сможет быть зашедулен на ноду с установленным taints. Если нет соответствующего блока tolerations в спецификации пода, разрешающего шедулинг на эту ноду.
4. Есть ещё нюанс работы taint и standalone pod. Об этом позже.

Удалить taint можно так:

kubectl taint nodes cl18jmsftl4879u5tuu0-izym cam=taint:NoSchedule-
kubectl taint nodes cl18jmsftl4879u5tuu0-izym cam=taint:NoExecute-

Другой пример tolerations.

# operator Equal - точное сопоставление значений key=value
tolerations:
- key: cam
  value: app
  operator: Equal
  effect: NoSchedule
- key: cam
  value: app
  operator: Equal
  effect: NoExecute

Полезные ссылки.

Подробно про taints/tolerations:
https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/

Про поды писал здесь: https://teletype.in/@cameda2/Yme-IYqYWB0
Про ноды кластера здесь: https://teletype.in/@cameda2/PVXtu-qHWcv