Introducción
Normalmente, el planificador elige cualquier nodo que pueda ejecutar el Pod. Las etiquetas y la afinidad permiten expresar preferencias de ubicación para las cargas de trabajo, mientras que los taints hacen que los nodos rechacen Pods que no tengan las tolerations correspondientes.
Aplicarás progresivamente estos controles al nodo preparado y utilizarás los eventos de los Pods para distinguir entre una regla de programación obligatoria y una preferencia flexible.
Usa este modelo mental: las etiquetas describen los nodos; los selectores y la afinidad expresan lo que quiere un Pod; los taints indican lo que un nodo rechaza; y las tolerations permiten que un Pod atraviese una regla de rechazo compatible. Ninguno de estos mecanismos «mueve» directamente un Pod que ya está en ejecución.
Etiquetar el nodo para controlar la ubicación
En este paso, añadirás etiquetas descriptivas y las mostrarás como columnas.
Las etiquetas están pensadas deliberadamente para describir la ubicación de la carga de trabajo, no la identidad de la aplicación. workload-zone=local representa una ubicación, mientras que disk-class=fast representa una capacidad. Kubernetes no valida su significado empresarial; son las reglas de programación las que les dan sentido.
cd /home/labex/project/pod-scheduling
kubectl create namespace practice-scheduling
kubectl label node labex-v135 workload-zone=local disk-class=fast
kubectl get nodes -L workload-zone,disk-class
Las etiquetas son datos que utilizan los selectores y la afinidad; no trasladan automáticamente los Pods existentes, a menos que un controlador cree reemplazos.
La opción -L añade una columna por cada clave de etiqueta. Ver local y fast en la fila del nodo confirma que el planificador puede evaluar las reglas que utilizarás en los pasos siguientes.
NAME STATUS ROLES ... VERSION WORKLOAD-ZONE DISK-CLASS
labex-v135 Ready control-plane ... v1.35.5 local fast
Esto también confirma que el entorno actualizado ejecuta Kubernetes v1.35.5. Las dos columnas personalizadas proceden de las etiquetas que acabas de añadir.
Exigir un nodo con nodeSelector
En este paso, crearás un Pod con la regla de ubicación exacta más sencilla.
nodeSelector es un mapa de coincidencias de igualdad obligatorias. El Pod solo puede ubicarse en un nodo en el que todas las claves tengan el valor solicitado. Es una opción adecuada para una ubicación obligatoria y sencilla.
cd /home/labex/project/pod-scheduling
cat <<'EOF' > selected-pod.yaml
apiVersion: v1
kind: Pod
metadata: {name: selected-pod, namespace: practice-scheduling}
spec:
nodeSelector: {disk-class: fast}
containers:
- {name: worker, image: busybox:1.36, imagePullPolicy: IfNotPresent, command: ["sh", "-c", "while true; do sleep 30; done"]}
EOF
Aplica el Pod, espera a que esté listo y consulta el nodo asignado en la columna NODE:
kubectl apply -f selected-pod.yaml
kubectl wait --for=condition=Ready pod/selected-pod -n practice-scheduling --timeout=120s
kubectl get pod selected-pod -n practice-scheduling -o wide
nodeSelector es obligatorio: si ningún nodo tiene todas las etiquetas solicitadas, el Pod permanece en estado Pending.
El nodo seleccionado debería ser labex-v135. La programación correcta demuestra que la etiqueta disk-class=fast del nodo coincide con el requisito del Pod.
pod/selected-pod condition met
NAME READY STATUS ... NODE
selected-pod 1/1 Running ... labex-v135
La columna NODE muestra el resultado de la programación; el nodeSelector del manifiesto explica por qué ese nodo podía recibir el Pod.
Comparar la afinidad obligatoria y preferida
En este paso, crearás un Pod que exige la zona local y prefiere los discos rápidos.
La afinidad de nodos ofrece más posibilidades que nodeSelector. La sección obligatoria descarta los nodos que no coinciden; la sección preferida asigna una puntuación a los nodos compatibles, pero no convierte esa preferencia en un requisito.
Los nombres largos de estos campos describen cuándo se aplican: la regla se evalúa durante la programación y se ignora durante la ejecución. Si una etiqueta cambia posteriormente, Kubernetes no expulsará el Pod en ejecución únicamente por esta regla de afinidad.
cd /home/labex/project/pod-scheduling
cat <<'EOF' > affinity-pod.yaml
apiVersion: v1
kind: Pod
metadata: {name: affinity-pod, namespace: practice-scheduling}
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions: [{key: workload-zone, operator: In, values: [local]}]
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions: [{key: disk-class, operator: In, values: [fast]}]
containers:
- {name: worker, image: busybox:1.36, imagePullPolicy: IfNotPresent, command: ["sh", "-c", "while true; do sleep 30; done"]}
EOF
Aplica el Pod y espera a que se programe:
kubectl apply -f affinity-pod.yaml
kubectl wait --for=condition=Ready pod/affinity-pod -n practice-scheduling --timeout=120s
kubectl get pod affinity-pod -n practice-scheduling -o wide
Las reglas obligatorias filtran los nodos posibles; las reglas preferidas puntúan los que quedan. IgnoredDuringExecution significa que los cambios posteriores en las etiquetas no expulsan un Pod que ya está en ejecución.
La tabla resultante también debería indicar labex-v135. En este clúster de un solo nodo, la regla obligatoria workload-zone=local determina si el nodo es válido; la regla preferida para discos rápidos muestra la sintaxis de puntuación sin necesidad de crear un segundo nodo únicamente para comparar.
Usar un taint y una toleration
En este paso, aplicarás un taint al nodo, observarás cómo un Pod sin toleration permanece en Pending y, después, crearás un Pod autorizado.
El taint tiene la clave practice-reserved, el valor true y el efecto NoSchedule. Este efecto bloquea los Pods nuevos que no tengan una toleration compatible, pero no expulsa los Pods que ya se estén ejecutando en el nodo.
kubectl taint node NODE key=value:effect añade esa regla a un nodo. El siguiente comando kubectl run crea un Pod independiente sin toleration; al esperar a su condición PodScheduled, darás al planificador diez segundos como máximo para demostrar el fallo esperado.
cd /home/labex/project/pod-scheduling
kubectl taint node labex-v135 practice-reserved=true:NoSchedule
kubectl run blocked-pod -n practice-scheduling --image=busybox:1.36 --image-pull-policy=IfNotPresent --restart=Never -- sh -c 'sleep 3600'
kubectl wait --for=condition=PodScheduled pod/blocked-pod -n practice-scheduling --timeout=10s || true
kubectl events -n practice-scheduling --for pod/blocked-pod
El tiempo de espera agotado es esperado y || true permite continuar con la lección. El evento de programación debería mencionar un taint no tolerado, lo que demuestra por qué el Pod permanece en Pending.
error: timed out waiting for the condition on pods/blocked-pod
LAST SEEN TYPE REASON OBJECT MESSAGE
... Warning FailedScheduling Pod/blocked-pod 0/1 nodes are available: 1 node(s) had untolerated taint(s)...
En este caso, que el Pod permanezca en Pending es el resultado experimental correcto. El planificador evaluó el Pod, pero no encontró ningún nodo que pudiera utilizar.
Crea una toleration compatible. Una toleration permite programar el Pod a pesar del taint; por sí sola no selecciona el nodo.
cat <<'EOF' > tolerated-pod.yaml
apiVersion: v1
kind: Pod
metadata: {name: tolerated-pod, namespace: practice-scheduling}
spec:
nodeSelector: {workload-zone: local}
tolerations:
- {key: practice-reserved, operator: Equal, value: "true", effect: NoSchedule}
containers:
- {name: worker, image: busybox:1.36, imagePullPolicy: IfNotPresent, command: ["sh", "-c", "while true; do sleep 30; done"]}
EOF
kubectl apply -f tolerated-pod.yaml
kubectl wait --for=condition=Ready pod/tolerated-pod -n practice-scheduling --timeout=120s
kubectl delete pod blocked-pod -n practice-scheduling
El Pod con toleration también incluye un nodeSelector: una regla selecciona el nodo y la otra permite utilizarlo. Al eliminar el Pod bloqueado intencionadamente, el espacio de nombres queda limpio y en buen estado.
pod/tolerated-pod condition met confirma que añadir la toleration compatible cambió el resultado. Los Pods anteriores siguen ejecutándose porque NoSchedule afecta a las nuevas ubicaciones, pero no expulsa los Pods que ya están en ejecución.
Resumen
Has utilizado etiquetas, nodeSelector, afinidad obligatoria y preferida, eventos del planificador, taints y tolerations para expresar por separado la intención de ubicación y las condiciones de admisión de los nodos.


