Hasta ahora, en los artículos anteriores, no he tenido en cuenta en qué nodos se despliegan mis aplicaciones. Hoy quiero contarte que existe la posibilidad de influir sobre cómo se organizan tus pods dentro de tu clúster a través de los taints y tolerations.
Taints
Lo cierto es que no sé cuál sería la traducción al español más acertada, pero me gusta pensar que en este caso taint es «olor» y los taints son los olores que desprende un nodo y hacen que los pods se alejen, a no ser que sean capaces de tolerarlos 😀 Por lo tanto, estos se asignan a los nodos y puedes saber cuáles tienen cada uno haciendo un describe de ellos:
#See the name of your nodes
kubectl get nodes
#Describe one of them
kubectl describe node aks-nodepool1-26643339-vmss000000
Dentro de la descripción, existe un apartado llamado Taints, que por ahora en mi nodo no tiene ningún valor:
➜ kubectl describe node aks-nodepool1-26643339-vmss000000
Name: aks-nodepool1-26643339-vmss000000
Roles: agent
Labels: agentpool=nodepool1
beta.kubernetes.io/arch=amd64
beta.kubernetes.io/instance-type=Standard_DS2_v2
beta.kubernetes.io/os=linux
failure-domain.beta.kubernetes.io/region=northeurope
failure-domain.beta.kubernetes.io/zone=0
kubernetes.azure.com/cluster=MC_AKS-Demos_returngis_northeurope
kubernetes.azure.com/mode=system
kubernetes.azure.com/node-image-version=AKSUbuntu-1604-2020.06.10
kubernetes.azure.com/role=agent
kubernetes.io/arch=amd64
kubernetes.io/hostname=aks-nodepool1-26643339-vmss000000
kubernetes.io/os=linux
kubernetes.io/role=agent
node-role.kubernetes.io/agent=
storageprofile=managed
storagetier=Premium_LRS
Annotations: node.alpha.kubernetes.io/ttl: 0
volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp: Tue, 23 Jun 2020 18:31:12 +0200
Taints: <none>
[...]
Los taints están compuestos de una clave, un valor y un efecto representados como <key>=<value>:effect.
La clave y el valor pueden ser lo que tú quieras pero para el efecto hay tres tipos que puedes usar:
- NoSchedule: evita que se programe el despliegue de pods que no toleren los taints que tienen este efecto.
- PreferNoSchedule: es lo mismo que el anterior pero un poco más soft de cara al scheduler, que es el que tiene en cuenta este mecanismo.
- NoExecute: este efecto es más agresivo ya que no solo restringe la ejecución en los nodos sino que si ya están desplegados en el mismo que acaba de obtener un nuevo taint con este efecto los reubica si no son capaces de tolerarlo.
Para ver todo esto con ejemplos, la forma de asociar un taint a un nodo es la siguiente:
kubectl taint node aks-nodepool1-26643339-vmss000000 type=production:NoSchedule
Si ahora vuelves a hacer un describe de este nodo comprobarás que este tiene un nuevo taint asociado:
➜ kubectl describe node aks-nodepool1-26643339-vmss000000
Name: aks-nodepool1-26643339-vmss000000
Roles: agent
Labels: agentpool=nodepool1
beta.kubernetes.io/arch=amd64
beta.kubernetes.io/instance-type=Standard_DS2_v2
beta.kubernetes.io/os=linux
failure-domain.beta.kubernetes.io/region=northeurope
failure-domain.beta.kubernetes.io/zone=0
kubernetes.azure.com/cluster=MC_AKS-Demos_returngis_northeurope
kubernetes.azure.com/mode=system
kubernetes.azure.com/node-image-version=AKSUbuntu-1604-2020.06.10
kubernetes.azure.com/role=agent
kubernetes.io/arch=amd64
kubernetes.io/hostname=aks-nodepool1-26643339-vmss000000
kubernetes.io/os=linux
kubernetes.io/role=agent
node-role.kubernetes.io/agent=
storageprofile=managed
storagetier=Premium_LRS
Annotations: node.alpha.kubernetes.io/ttl: 0
volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp: Tue, 23 Jun 2020 18:31:12 +0200
Taints: type=production:NoSchedule
[...]
Puedes poner múltiples taints en el mismo nodo o el mismo taint a todos los nodos utilizando el mismo comando pero con nodes y –all:
Si estás trabajando con AKS, otra de las cosas que puedes hacer es, en el momento de añadir un nodepool, que no es más que un conjunto de máquinas de un mismo tipo y versión, asociarle los taints a los nodos que lo componen durante la creación:
az aks nodepool add \
--resource-group myResourceGroup \
--cluster-name myAKSCluster \
--name taintnp \
--node-count 1 \
--node-taints sku=gpu:NoSchedule \
--no-wait
Tolerations
Desde el punto de vista de los pods digamos que estos son capaces de tolerar los taints. ¿Esto qué significa? Pues básicamente que estos pueden tener en su definición cuáles de los supuestos taints podrían aceptar para que el scheduler pueda o no valorar un nodo en concreto para su despliegue.
Para ver algunos ejemplos de tolerations puedes echar un vistazo a alguno de los pods dentro del namespace kube-system.
kubectl get po -n kube-system
Al igual que en los nodos, haz un describe de uno del tipo kube-proxy-* para ver sus tolerancias:
kubectl describe pod kube-proxy-4rqhx -n kube-system
Verás que en la parte inferior de la descripción aparecen todas las definidas para este tipo de pod:

Desplegar pod de prueba
Para terminar vamos a hacer algunas pruebas con unos pods desde cero. Para ello lanza el siguiente despliegue:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
tolerations:
- key: type
operator: Equal #this is the default value
value: production
effect: NoSchedule
Como puedes ver, al final he incluido un apartado llamado tolerations, donde básicamente puedes indicar qué claves con qué valor y qué efecto tolerarán los pods que se creen a raíz de este deployment. Cuando despliegues el mismo en tu clúster te darás cuenta de que las tres réplicas estarán repartidas entre los tres nodos de la misma forma que siempre:
➜ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deployment-6ffd9989d-bhsl8 1/1 Running 0 13s 10.244.2.4 aks-nodepool1-26643339-vmss000002 <none> <none>
nginx-deployment-6ffd9989d-dbvr9 1/1 Running 0 13s 10.244.0.9 aks-nodepool1-26643339-vmss000000 <none> <none>
nginx-deployment-6ffd9989d-hgdfp 1/1 Running 0 13s 10.244.1.5 aks-nodepool1-26643339-vmss000001 <none> <none>
¿Entonces? Esto es así porque aunque un pod tolere ciertos taints esto no significa que no pueda desplegarse en otros nodos que no tengan estas restricciones, ya que por ahora los otros dos nodos no tienen ninguna.
Para comprobar esto vamos a utilizar otro taint en el segundo nodo que además utilice el efecto NoExecute, para que si hay algún pod que no lo tolere sea expulsado del nodo.
kubectl taint node aks-nodepool1-26643339-vmss000001 type=dev:NoExecute
Si ahora compruebas el estado de tus pods, verás que el que se encontraba en este nodo está siendo reubicado en otro:
➜ Kubelet kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deployment-6ffd9989d-268jb 0/1 ContainerCreating 0 5s <none> aks-nodepool1-26643339-vmss000002 <none> <none>
nginx-deployment-6ffd9989d-bhsl8 1/1 Running 0 17m 10.244.2.4 aks-nodepool1-26643339-vmss000002 <none> <none>
nginx-deployment-6ffd9989d-dbvr9 1/1 Running 0 17m 10.244.0.9 aks-nodepool1-26643339-vmss000000 <none> <none>
nginx-deployment-6ffd9989d-hgdfp 0/1 Terminating 0 17m 10.244.1.5 aks-nodepool1-26643339-vmss000001 <none> <none>
La última prueba sería que el tercer nodo tuviera un taint diferente, de tal manera que a los pods no les quedara más remedio que ubicarse todos en el restante, que sería el cero, el cual si toleran.
kubectl taint node aks-nodepool1-26643339-vmss000002 type=dev:NoSchedule
En este caso, verás que los pods que ya estaban desplegados siguen estando en el nodo aks-nodepool1-26643339-vmss000002:
➜ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deployment-6ffd9989d-268jb 1/1 Running 0 4h15m 10.244.2.7 aks-nodepool1-26643339-vmss000002 <none> <none>
nginx-deployment-6ffd9989d-bhsl8 1/1 Running 0 4h32m 10.244.2.4 aks-nodepool1-26643339-vmss000002 <none> <none>
nginx-deployment-6ffd9989d-dbvr9 1/1 Running 0 4h32m 10.244.0.9 aks-nodepool1-26643339-vmss000000 <none> <none>
Esto es así porque este cambio no afecta a los ya desplegados. Para este ejemplo voy a eliminar todos los pods y que el scheduler vuelva a desplegarlos donde corresponda:
kubectl delete po --all
Si vuelves a comprobar tus pods verás que ahora todos están alojados en el nodo cero.
➜ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deployment-6ffd9989d-bksfw 1/1 Running 0 51s 10.244.0.11 aks-nodepool1-26643339-vmss000000 <none> <none>
nginx-deployment-6ffd9989d-mfjwr 1/1 Running 0 51s 10.244.0.10 aks-nodepool1-26643339-vmss000000 <none> <none>
nginx-deployment-6ffd9989d-mfphr 1/1 Running 0 50s 10.244.0.12 aks-nodepool1-26643339-vmss000000 <none> <none>
Si quisieras eliminar un taint de un nodo el comando sería el mismo con un menos al final:
kubectl taint node aks-nodepool1-26643339-vmss000002 type=dev:NoSchedule-
Como ves, gracias a los taints en los nodos y los tolerations en los pods puedes influir en el despliegue de estos de una forma mucho más lógica, por hardware, capacidades, equipos, lo que quieras 🙂
¡Saludos!

Bootcamp DevOps
Si tienes ganas de meterte en el área de DevOps, formo parte del
equipo de docentes del Bootcamp DevOps Lemoncode, ¿Te animas a
aprender con nosotros?