Programa los Pods pendientes
El clúster de este playground tiene un control plane y dos nodos worker, y dos Pods llevan toda la mañana en Pending sin que nadie entienda por qué. Los manifiestos están en /home/laborant/manifests. Echa un primer vistazo desde la pestaña dev-machine:
kubectl get pods -o wide
kubectl get nodes
Dos reglas de juego antes de empezar. Primera: el taint del nodo node-01 lo puso el equipo de infraestructura a propósito para reservar ese nodo, y debe permanecer. Segunda: el Pod db monta un volumen con pedidos dentro, y esos datos tienen que sobrevivir. Las soluciones que consisten en borrar la restricción o en tirar el disco y empezar de cero no cuentan.
El Pod monitor
Pista 1
Un taint con efecto NoSchedule es un repelente: el nodo rechaza cualquier Pod que no declare explícitamente que lo tolera. El mecanismo complementario se llama toleration y se declara en el spec del Pod, indicando la clave, el valor y el efecto del taint que se acepta. A diferencia del resto del spec, las tolerations sí admiten que les añadas entradas a un Pod vivo, pero corrige el manifiesto y recrea el Pod: es donde el arreglo sobrevive al próximo despliegue.
Pista 2
Fíjate en que taints y tolerations solo abren la puerta, no eligen el destino. Lo que garantiza que monitor acabe precisamente en node-01 es su nodeSelector por hostname; la toleration solo hace que ese nodo deje de rechazarlo. La combinación de ambos mecanismos es el patrón estándar para dedicar nodos a cargas concretas.
El Pod db
Este es distinto, y el describe lo dice con todas las letras: los dos nodos quedan descartados, cada uno por un motivo diferente.
Pista 3
Lee el evento del scheduler entero, sin quedarte en la primera línea:
0/3 nodes are available: 1 node(s) didn't match PersistentVolume's node affinity,
1 node(s) didn't match Pod's node affinity/selector, 1 node(s) had untolerated taint(s).
Ese primer motivo es nuevo en el curso. No habla del Pod: habla del volumen.
Pista 4
La StorageClass local-path aprovisiona directorios en el disco de un nodo concreto, y por eso el PersistentVolume nace con una nodeAffinity que lo ata a ese nodo para siempre. Míralo:
kubectl get pv -o jsonpath='{.items[*].spec.nodeAffinity}'
El disco no viaja. Un Pod que monte ese volumen solo puede programarse donde esté el volumen, diga lo que diga su nodeSelector.
Pista 5
Tienes dos restricciones que se contradicen: el nodeSelector del Pod dice un nodo y la nodeAffinity del volumen dice el otro. Una de las dos tiene que ceder, y no es negociable cuál: los datos no se mueven de nodo, así que lo que sobra es la preferencia del Pod. Corrige /home/laborant/manifests/db.yaml y recrea el Pod.
Por qué importa
Etiquetas, taints, tolerations y afinidades son el vocabulario con el que se gobierna dónde corre cada cosa en un clúster. Pero el db enseña la regla que está por encima de todas ellas: el almacenamiento local gana siempre. Puedes tolerar un taint, cambiar una etiqueta o reescribir una afinidad; lo que no puedes es mover un disco de máquina con un kubectl.
Por eso el volume node affinity conflict es de los Pending más caros de producción: aparece cuando ya hay datos dentro, normalmente después de un mantenimiento en el que alguien fijó un Pod a un nodo distinto del que guarda su volumen. La salida no es pelear con el scheduler: es entender que el Pod va donde están sus datos, o que hay que replicarlos antes de moverlo.