Control de la Programación de Pods en Kubernetes: Taints, Tolerations, nodeSelector y Node Affinity. Bloque 1. Tema 1.4 del CKA.


     En Kubernetes, la programación de Pods es una de las funciones clave del "plano de control" (control plane). Por defecto, los Pods se distribuyen automáticamente entre los nodos disponibles, equilibrando la carga y optimizando el rendimiento del clúster. Sin embargo, en ciertos casos, es necesario tener un mayor control sobre dónde se ejecutan los Pods, y es aquí donde entran en juego TaintsTolerations, NodeSelector y Node Affinit. Existen otros conceptos, más avanzados, que se estudirán en otro apartado.

     Taints y Tolerations son mecanismos que trabajan juntos para controlar dónde no se deben ejecutar ciertos Pods, o para garantizar que solo ciertos tipos de Pods puedan ejecutarse en nodos específicos. Un Taint es una restricción que se aplica a un nodo para evitar que Pods no deseados se ejecuten en él, mientras que una Toleration permite a un Pod "tolerar" un Taint y ser programado en un nodo con esa restricción.

      NodeSelector y Node Affinity son mecanismos que permiten especificar en qué nodos se desea que se ejecuten los Pods. nodeSelector es una forma sencilla de restringir la programación de un Pod a nodos específicos que tienen una determinada etiqueta. Por otro lado, Node Affinity ofrece una mayor flexibilidad, permitiendo configurar reglas más avanzadas y personalizadas basadas en etiquetas de los nodos.

Estos mecanismos son esenciales para gestionar la colocación de Pods de manera más controlada y personalizada, asegurando que se respeten las necesidades específicas de cada aplicación dentro del clúster.

 Analogía para entenderlo bien: 

Taints: Serían como las etiquetas que se ponen en las mesas de un restaurante. Por ejemplo, una mesa podría tener una etiqueta que diga "Reservada para el cumpleaños de tal persona". Estas etiquetas indican que esa mesa (nodo) no está disponible para cualquier cliente.

Tolerations: Serían  las características de los clientes. Un cliente (pod) que sea de ese cumpleaños tendría una "tolerancia" a esa mesa. 

NodeSelector: Imagina que cada mesa en un restaurante tiene un letrero indicando su ubicación específica, como "Terraza" o "Interior". Un cliente (Pod) que prefiere sentarse en la terraza puede solicitar específicamente una mesa en la "Terraza". Del mismo modo, nodeSelector permite que un Pod elija nodos que tengan una etiqueta específica (por ejemplo, "ubicación=terraza") para restringir dónde puede ejecutarse.

Node Affinity: Piensa en un evento de cine al aire libre donde las personas pueden tener preferencias sobre qué sección del parque prefieren, como la sección "con sombra" o "sin sombra". Sin embargo, algunos clientes son más flexibles que otros; pueden tener una "preferencia" por la sombra, pero están dispuestos a sentarse en el sol si no hay opción. Node Affinity funciona de manera similar, ya que permite a los Pods especificar preferencias para ejecutarse en ciertos nodos (por ejemplo, "con sombra") pero aún pueden considerar otras opciones si no hay nodos que cumplan todas sus preferencias.

Ahora que entendemos el concepto, vamos al detalle técnico:

1. Taints y Tolerations

Taints y Tolerations trabajan en conjunto para restringir o permitir la programación de Pods en nodos específicos. Un Taint es una restricción aplicada a un nodo que indica que ciertos Pods no deben ejecutarse allí, a menos que tengan una Toleration que coincida.

Taints:

  • Aplicados a los nodos, marcan un nodo como "no deseado" para ciertos Pods.
  • Contienen tres elementos: Key, Value y Effect.
    1. Key: Una clave que representa el motivo del taint.
    2. Value: Un valor asociado con la clave (opcional).
    3. Effect: El efecto que tendrá el taint. Hay tres efectos posibles, cada uno aplica restricciones diferentes al nodo:

    • NoSchedule: Evita que los nuevos pods se programen en el nodo, a menos que toleren el taint.
    • PreferNoSchedule: Intenta evitar programar pods en el nodo, pero no es estrictamente prohibido.
    • NoExecute: Los nuevos pods no se programarán en el nodo, y los pods existentes que no lo toleren serán expulsados del nodo

    Por entenderlo;

  • Key: cumpleaños; Indica que la restricción de la mesa está relacionada con un evento específico: un cumpleaños.
  • Value: Alba; Especifica que el cumpleaños es de Alba. Esto hace que la reserva sea aún más personalizada.
  • Effect: NoSchedule;Estás reservando el nodo mesa1 exclusivamente para la fiesta de cumpleaños de Alba. Ninguna otra aplicación o servicio podrá ejecutarse en ese nodo a menos que esté específicamente diseñado para ello (es decir, tenga una tolerancia que coincida con el taint). Significa que ningún otro cliente, excepto aquellos que también estén celebrando el cumpleaños de Alba (y tengan la tolerancia correspondiente), puede sentarse en esa mesa.

Tolerations:

Las tolerations se aplican a los pods y permiten que esos pods se ejecuten en nodos con taints que coincidan. Una toleration no significa que el pod debe ejecutarse en el nodo con el taint, sino que puede hacerlo si no hay otras restricciones.

Una toleration tiene una estructura similar a la de un taint:

  • Key: La clave del taint que el pod puede tolerar.
  • Operator: Define cómo debe coincidir la clave (igual a Exists o Equal).
  • Value: El valor que el pod tolera (solo si el operador es Equal).
  • Effect: El efecto del taint que el pod puede tolerar (NoSchedule, PreferNoSchedule o NoExecute).
  • TolerationSeconds: Solo aplicable a taints con efecto NoExecute. Define cuánto tiempo el pod puede permanecer en un nodo con ese taint antes de ser expulsado.Se aplican a los Pods y les permiten programarse en nodos que tienen un Taint específico.

 Una Toleration coincide con un Taint si tiene la misma clave y efecto.

En resumen, una tolerancia simplemente le dice al scheduler que el pod puede ser programado en un nodo con un taint específico, pero no garantiza que lo será. El scheduler seguirá considerando otros factores al tomar su decisión.

Cómo funcionan juntos

Un taint en un nodo actúa como una etiqueta de advertencia que dice "no ejecutar aquí" para los pods que no lo toleran.

Un pod con una toleration que coincida con el taint podrá ejecutarse en ese nodo. Es como una excepción que permite a un pod ejecutarse en un nodo que tiene un taint.

Por ejemplo, podrías tener un nodo de alto rendimiento destinado solo para cargas de trabajo específicas, al que le aplicas un taint. Solo los pods que tengan una toleration adecuada podrán ejecutarse en ese nodo.

Ejemplos:

1/ Aplicar un Taint al nodo mediante kubectl:

kubectl taint nodes mesa1 cumpleaños=Alba:NoSchedule

El nodo mesa1 ha sido marcado con un taint para restringir su uso a la fiesta de cumpleaños de Alba. Esta acción impide que cualquier pod sea programado en este nodo, a menos que dicho pod posea una tolerancia que coincida con el taint aplicado (Cumpleaños de Alba)

Esto evita que cualquier Pod sin la tolerancia adecuada se ejecute en el nodo.

Nota: Un taint puede aplicarse a un nodo usando tanto kubectl como directamente en el manifiesto del nodo. En cambio para agregar una Toleration al Pod, siempre lo hacemos en el manifiesto. 

Ejemplo de manifiesto que aplica un toleration a un pod:

apiVersion: v1 kind: Pod metadata: name: pod-cumpleaños spec: tolerations: - key: "cumpleaños" value: "Alba" effect: "NoSchedule"

Nota: Si necesitas agregar una toleration a un pod ya existente, debes modificar
el manifiesto YAML y recrear el pod.

2. NodeSelector

NodeSelector es un método sencillo para restringir la programación de un Pod a nodos específicos con etiquetas concretas. Es útil cuando los nodos tienen características específicas, como GPU, o están ubicados en una zona particular.

Ejemplo: Primero, etiqueta el nodo:

kubectl label nodes nodo1 tipo=gpu 

Luego, hay que definir el Pod con nodeSelector, en el manifiesto, o sea el archivo YAML del Pod:

apiVersion: v1 kind: Pod metadata: name: pod-gpu spec: nodeSelector: tipo: gpu

Este Pod se programará solo en nodos que tengan la etiqueta tipo=gpu.

En este ejemplo:

  • El Pod se llama pod-gpu.
  • El campo nodeSelector indica que el Pod solo será programado en nodos que tengan la etiqueta tipo=gpu.

3. Node Affinity y Pod Anti-Affinity

Node Affinity es una versión más avanzada de nodeSelector, que permite reglas de selección más complejas y flexibles, mientras que Pod Anti-Affinity se enfoca en evitar que ciertos Pods se programen juntos en el mismo nodo.

Hay 2 parámetros principales que determinan la flexibilidad de las reglas:

  • requiredDuringSchedulingIgnoredDuringExecution El Pod solo se programa en nodos que cumplen estrictamente con los criterios especificados, ya sea en Node Affinity (criterios de nodo) o Pod Anti-Affinity (criterios de distribución de Pods).
  • preferredDuringSchedulingIgnoredDuringExecution: El Pod intenta programarse en nodos que cumplan con los criterios, pero no es obligatorio. Kubernetes priorizará los nodos que cumplen con el criterio, pero permitirá el despliegue en otros nodos si es necesario.
Este uso flexible de los parámetros permite establecer reglas más o menos estrictas, tanto para la afinidad con nodos específicos como para evitar la proximidad a otros Pods con características determinadas.

Ejemplo Node Affinity : Primero, etiqueta el nodo:

kubectl label nodes nodo1 prioridad=alta

Luego, en el manifiesto del Pod:

apiVersion: v1 kind: Pod metadata: name: pod-alta-prioridad spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: prioridad operator: In values: - alta

Este Pod solo se programará en nodos con la etiqueta prioridad=alta.

Pod Anti-Affinity, permite configurar restricciones para evitar que un Pod se programe en el mismo nodo que otros Pods con etiquetas específicas. Esto es útil cuando deseas distribuir Pods entre diferentes nodos, lo cual es común para aplicaciones que requieren alta disponibilidad o para evitar que múltiples instancias de un servicio estén en el mismo nodo.

Ejemplo Pod Anti-Affinity:

En este ejemplo, queremos asegurarnos de que un Pod no se ejecute en el mismo nodo que otros Pods con la etiqueta app=frontend.

Primero, etiqueta los Pods de la aplicación existente:

kubectl label pod <nombre-del-pod> app=frontend

Luego, en el manifiesto del nuevo Pod, configuras podAntiAffinity para evitar que se programe junto a Pods con esa misma etiqueta:


apiVersion: v1 kind: Pod metadata: name: pod-anti-frontend spec: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - frontend topologyKey: "kubernetes.io/hostname"

Descripción del ejemplo:

  • labelSelector: Busca Pods en el mismo nodo que tengan la etiqueta app=frontend.
  • topologyKey: Aquí se usa "kubernetes.io/hostname", lo cual significa que Kubernetes evitará programar el Pod en el mismo nodo (hostname) que ya tenga Pods con la etiqueta app=frontend.

Esto garantiza que el Pod pod-anti-frontend no se programe en el mismo nodo que otros Pods con la etiqueta app=frontend, distribuyéndolos en distintos nodos según la clave de topología seleccionada.

4. Combinación de Node Affinity y Taints/Tolerations

Para un control absoluto sobre la programación de Pods, puedes combinar Node Affinity con Taints y Tolerations.

Ejemplo: Etiqueta el nodo y aplica un Taint:

kubectl label nodes nodo1 uso=exclusivo kubectl taint nodes nodo1 uso=exclusivo:NoSchedule

En el manifiesto del Pod, configuras nodeAffinity y Tolerations:

apiVersion: v1 kind: Pod metadata: name: pod-exclusivo spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: uso operator: In values: - exclusivo tolerations: - key: "uso" operator: "Equal" value: "exclusivo" effect: "NoSchedule"

Este Pod se programará únicamente en nodos etiquetados como uso=exclusivo y que tengan el Taint correspondiente.

Resumen

Para controlar dónde se ejecutan los Pods:

  • NodeSelector: Es la opción más sencilla para usar etiquetas de nodo.
  • Node Affinity/Pod Anti-Affinity: Permite reglas de selección más detalladas.
  • Taints y Tolerations: Evitan que ciertos Pods se ejecuten en nodos específicos, a menos que tengan la tolerancia adecuada.

Al combinar estas herramientas, puedes gestionar la programación de Pods de forma avanzada y asegurar que se ajusten a las necesidades de tu clúster y aplicaciones.

Hay que tener en cuenta que existen otros factores que influyen en la decisión del scheduler a la hora de programar los pods, como iremos viendo durante el resto del curso, como:

 - Disponibilidad de recursos: El scheduler buscará un nodo que tenga suficientes recursos (CPU, memoria, etc.) para ejecutar el pod.

 - Afinidad de nodo: Si el pod tiene afinidad de nodo, el scheduler intentará programarlo en un nodo específico.

 - Anti-afinidad de nodo: Si el pod tiene anti-afinidad de nodo, el scheduler intentará evitar programarlo en ciertos nodos.

 - Otras políticas de programación: El scheduler puede tener otras políticas de programación que influyan en la decisión final.

Comentarios