Skip to main content

Taint y Tolerations

Taints se aplican a los Nodos y Tolerations se aplican a los Pods.

Los Taints funcionan en los Nodos para evitar que Pods entren en esos Nodos, pero si un Pod tiene una Toleration para este Taint él podrá ser programado para los Nodos con el respectivo Taint.

Pensemos en un Taint como repelente para el Nodo y los Pods como los diferentes tipos de insectos. Tenemos repelentes que funcionan para algunos insectos, pero no para otros. Si un insecto (Pod) es inmune (tolera) a este repelente podrá llegar (picar) a los Nodos que utilizan el repelente (Taint).

Otra analogía que podemos hacer es que los Taints son cerraduras y solamente los Pods que poseen las llaves (Tolerations) consiguen abrir y entrar. Esta cerradura no tiene nada que ver con seguridad o intrusión, es solamente restricción entre Pods y Nodos.

Un Pod que posee una Toleration para un Taint es posible ser programado para el Nodo con este Taint, pero eso no garantiza que eso ocurrirá. Si existe algún otro Nodo sin ningún Taint que sea mejor para recibir el Pod, ese podrá ser el elegido. Solo sería garantía de un Pod entrar en un Nodo con un Taint si todos los Nodos del cluster tuvieran Taints diferentes y el Pod tuviera la Toleration para el Nodo específico. Otros recursos existen para configurar esa garantía.

Podemos usar los Taints para separar:

  • Nodos por ambientes
  • Nodos por tipo de aplicación, por ejemplo, aplicaciones que exigen procesamiento gráfico serían mejor ejecutadas en Nodos con tarjeta gráfica.

Los Taints pueden tener cualquier conjunto de clave-valor en el Nodo, así como Labels, pero si un Taint existe necesita tener un efecto de qué sucede con un Pod si no tiene la Toleration y son 3 posibles valores:

  • NoSchedule: Si un Taint posee este efecto, los nuevos Pods deben solamente ser programados para este Nodo si posee la Toleration respectiva al Taint.
  • PreferNoSchedule: Hace una preferencia de no programar, pero si no existe espacio en otros Nodos para un Pod, este podrá ser programado.
  • NoExecute: Cualquier Pod que no tenga esta Toleration no debe ser programado y los Pods que estén en el Nodo y no tengan la Toleration deben ser removidos.

Para aplicar un Taint en un Nodo necesitamos ejecutar el comando kubectl taint nodes node_name key=values:taint-effect.

# Crear Taint
kubectl taint node k3d-k3d-cluster-server-0 app=test:NoSchedule
# Remover Taint es el mismo comando con el - al final
kubectl taint node k3d-k3d-cluster-server-0 app=test:NoSchedule-

UNA BUENA PRÁCTICA, Y RECOMENDABLE, ES NO IMPLANTAR APLICACIONES EN LOS NODOS MASTERS. LUEGO, ES IMPORTANTE QUE LOS NODOS MASTERS TENGAN UN TAINT QUE NINGÚN POD TOLERE CON EL EFECTO NoExecute.

En el Pod, para aplicar una Toleration, es necesario incluir las configuraciones deseadas dentro del campo tolerations en la especificación del Pod.

apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
tolerations:
- key: app
operator: Equal
value: test
effect: NoSchedule

Para el ejemplo anterior, ningún Nodo posee Taint, pero el Pod tendrá una Toleration.

kubectl apply -f pod-toleration.yaml
pod/nginx created

## En esta secuencia abajo, una vez fue creado en el server, otra vez fue creado en el agent2, o sea, no altera nada
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 4m20s 10.42.3.3 k3d-k3d-cluster-server-0 <none> <none>

kubectl delete pod nginx
pod "nginx" deleted

kubectl apply -f pod-toleration.yaml
pod/nginx created

kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 4m20s 10.42.3.3 k3d-k3d-cluster-agent-1 <none> <none>

kubectl delete pod nginx
pod "nginx" deleted

NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
k3d-k3d-cluster-agent-2 Ready <none> 70d v1.27.4+k3s1 172.18.0.2 <none> K3s dev 6.2.0-39-generic containerd://1.7.1-k3s1
k3d-k3d-cluster-server-0 Ready control-plane,master 70d v1.27.4+k3s1 172.18.0.4 <none> K3s dev 6.2.0-39-generic containerd://1.7.1-k3s1
k3d-k3d-cluster-agent-1 Ready <none> 70d v1.27.4+k3s1 172.18.0.3 <none> K3s dev 6.2.0-39-generic containerd://1.7.1-k3s1
k3d-k3d-cluster-agent-0 Ready <none> 70d v1.27.4+k3s1 172.18.0.5 <none> K3s dev 6.2.0-39-generic containerd://1.7.1-k3s1

## Vamos a aplicar Taints al Nodo server, agent0 agent1, pero no los mismos de la Toleration del Pod
kubectl taint node k3d-k3d-cluster-server-0 run=test:NoSchedule
node/k3d-k3d-cluster-server-0 tainted
kubectl taint node k3d-k3d-cluster-agent-0 run=test:NoSchedule
node/k3d-k3d-cluster-agent-0 tainted
kubectl taint node k3d-k3d-cluster-agent-1 run=test:NoSchedule
node/k3d-k3d-cluster-agent-1 tainted

# Observe que fue para el agent2
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 7s 10.42.2.48 k3d-k3d-cluster-agent-2 <none> <none>

# Aplicando un Taint en el agent2 para no programar y ya remover quien no tenga la Toleration
kubectl taint node k3d-k3d-cluster-agent-2 run=test:NoExecute
node/k3d-k3d-cluster-agent-2 tainted

kubectl get pods -o wide
No resources found in default namespace. # El Pod desapareció y no volvió porque no tiene un ReplicaSet

kubectl apply -f pod-toleration.yaml
pod/nginx created
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 0/1 Pending 0 56s <none> <none> <none> <none>

kubectl describe pods nginx
Name: nginx
Namespace: default
Priority: 0
Service Account: default
Node: <none>
Labels: <none>
Annotations: <none>
Status: Pending
IP:
IPs: <none>
Containers:
nginx:
Image: nginx
Port: <none>
Host Port: <none>
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-2nh84 (ro)
Conditions:
Type Status
PodScheduled False
Volumes:
kube-api-access-2nh84:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: app=test:NoSchedule
node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
## Vea que no hay Nodo disponible
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 3m22s default-scheduler 0/4 nodes are available: 4 node(s) had untolerated taint {run: test}. preemption: 0/4 nodes are available: 4 Preemption is not helpful for scheduling..

## Ahora vamos a aplicar al agent1 el Taint que el Pod tiene la Toleration
files git:(main) ✗ kubectl taint node k3d-k3d-cluster-agent-1 app=test:NoSchedule
node/k3d-k3d-cluster-agent-1 tainted

# ¿Y por qué no entró y aún está en pending? Porque necesitaría tolerar los dos Taints, entonces vamos a remover el primero
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 0/1 Pending 0 5m19s <none> <none> <none> <none>

# Removiendo el Taint que el Pod no soporta
kubectl taint node k3d-k3d-cluster-agent-1 run=test:NoSchedule-
node/k3d-k3d-cluster-agent-1 untainted

kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 6m36s 10.42.1.36 k3d-k3d-cluster-agent-1 <none> <none>

Claro que podríamos haber agregado una Toleration más ya que es una lista en el Pod, pero fue solo para ilustrar.

Alt text

Los DaemonSets están especificados para colocar un Pod en cada Nodo, pero un DaemonSet también respeta los Taints, si no soporta los Taints no entrará en el Nodo.