El scheduler decide en qué nodo va cada pod. Este módulo son las herramientas para influir en esa decisión: forzar un nodo, atraer, repeler, reservar recursos y distribuir cargas. Acá el multi-node de tu cluster por fin importa.
The scheduler decides which node each pod lands on. This module is the toolbox for influencing that decision: pin a node, attract, repel, reserve resources and spread load. This is where your multi-node cluster finally matters.
Lo scheduler decide su quale nodo va ogni pod. Questo modulo è la cassetta degli attrezzi per influenzare quella decisione: forzare un nodo, attrarre, respingere, riservare risorse e distribuire il carico. Qui il multi-node del tuo cluster finalmente conta.
Duración60 min
Duration60 min
Durata60 min
DominioWorkloads & Scheduling
DomainWorkloads & Scheduling
DominioWorkloads & Scheduling
Cierrael dominio 15%
Closesthe 15% domain
Chiudeil dominio 15%
Requieremulti-node
Requiresmulti-node
Richiedemulti-node
Por defecto el scheduler (lo viste en el Módulo 1) reparte los pods solo, buscando nodos con recursos. A veces querés control: "este pod SOLO en nodos con SSD", "no pongas estos dos juntos", "reservá al menos tanto CPU". Este módulo son los cuatro mecanismos para eso — atraer (nodeSelector/affinity), repeler (taints/tolerations), reservar (requests/limits) y distribuir (pod affinity / topology spread) — y el orden en que el scheduler los aplica.
By default the scheduler (you met it in Module 1) places pods on its own, looking for nodes with room. Sometimes you want control: "this pod ONLY on SSD nodes", "don't put these two together", "reserve at least this much CPU". This module is the four mechanisms for that — attract (nodeSelector/affinity), repel (taints/tolerations), reserve (requests/limits) and spread (pod affinity / topology spread) — and the order the scheduler applies them in.
Di default lo scheduler (l'hai visto nel Modulo 1) piazza i pod da solo, cercando nodi con spazio. A volte però vuoi controllo: "questo pod SOLO su nodi con SSD", "non mettere questi due insieme", "riserva almeno tanta CPU". Questo modulo sono i quattro meccanismi per farlo — attrarre (nodeSelector/affinity), respingere (taints/tolerations), riservare (requests/limits) e distribuire (pod affinity / topology spread) — e l'ordine in cui lo scheduler li applica.
5.1
nodeSelector y Node Affinity
nodeSelector and Node Affinity
nodeSelector e Node Affinity
Atraer pods a ciertos nodos por labels.
Attract pods to certain nodes by label.
Attrarre i pod verso certi nodi tramite label.
5.2
Taints y Tolerations
Taints and Tolerations
Taints e Tolerations
Repeler pods de ciertos nodos, salvo los que toleran.
Repel pods from certain nodes, except those that tolerate.
Respingere i pod da certi nodi, tranne quelli che tollerano.
5.3
Resources: requests y limits
Resources: requests and limits
Resources: requests e limits
Reservar y limitar CPU/memoria. Afecta el scheduling y el QoS.
Reserve and cap CPU/memory. Drives scheduling and QoS.
Riservare e limitare CPU/memoria. Guida scheduling e QoS.
5.4
Pod Affinity y Topology Spread
Pod Affinity and Topology Spread
Pod Affinity e Topology Spread
Juntar/separar pods entre sí, distribuir uniforme.
Co-locate/separate pods, spread evenly.
Unire/separare i pod, distribuire uniformemente.
★
Práctica · 9 ejercicios
Practice · 9 exercises
Pratica · 9 esercizi
E1–E5 + 4 de troubleshooting, con manifest listo para aplicar.
E1–E5 plus 4 troubleshooting drills, each with a ready-to-apply manifest.
E1–E5 più 4 di troubleshooting, ognuno con un manifest pronto da applicare.
5.1
Atraer · 15 min
Attract · 15 min
Attrarre · 15 min
nodeSelector y Node Affinity
nodeSelector and Node Affinity
nodeSelector e Node Affinity
"Quiero que este pod corra en nodos con tal característica."
"I want this pod to run on nodes with such-and-such trait."
"Voglio che questo pod giri su nodi con questa caratteristica."
Práctica · Etiquetar y atraer
Practice · Label and attract
Pratica · Etichettare e attrarre
Primero: etiquetar un nodo
First: label a node
Primo: etichettare un nodo
# Poner un label a un nodo# Put a label on a node# Mettere una label su un nodok label node w1 disktype=ssd
# Ver labels de los nodos# See node labels# Vedere le label dei nodik get nodes --show-labels
k get nodes -l disktype=ssd
# Quitar un label (guion al final del nombre)# Remove a label (trailing dash after the name)# Rimuovere una label (trattino finale dopo il nome)k label node w1 disktype-
nodeSelector — la forma simple
nodeSelector — the simple form
nodeSelector — la forma semplice
apiVersion: v1
kind: Pod
metadata:
name: web-ssd
spec:
nodeSelector: # nivel POD (hermano de containers)# POD level (sibling of containers)# livello POD (fratello di containers)disktype: ssd # ↔ label del nodo, valor exacto# ↔ node label, exact value# ↔ label del nodo, valore esattocontainers:
- name: app
image: nginx
El pod SOLO irá a nodos con el label disktype=ssd. Si ninguno lo tiene, queda Pending para siempre. En un Deployment esto va en spec.template.spec.nodeSelector.
The pod will go ONLY to nodes carrying the disktype=ssd label. If none does, it stays Pending forever. In a Deployment this lives under spec.template.spec.nodeSelector.
Il pod andrà SOLO su nodi con la label disktype=ssd. Se nessuno ce l'ha, resta Pending per sempre. In un Deployment questo va sotto spec.template.spec.nodeSelector.
Node Affinity — la forma flexible
Node Affinity — the flexible form
Node Affinity — la forma flessibile
spec:
affinity: # nivel POD# POD level# livello PODnodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # DURA# HARD# DURAnodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values: ["ssd", "nvme"] # OR de valores# OR of values# OR di valoripreferredDuringSchedulingIgnoredDuringExecution: # BLANDA# SOFT# MORBIDA
- weight: 50 # 1..100# 1..100# 1..100preference:
matchExpressions:
- key: zone
operator: In
values: ["a"]
containers:
- name: app
image: nginx
★
Nadie se aprende requiredDuringSchedulingIgnoredDuringExecution de memoria: se lee. "Requerido AL schedular, ignorado DESPUÉS". Y si te trabás en el examen, k explain --recursive pod.spec.affinity te escupe el árbol de campos entero.
Nobody memorises requiredDuringSchedulingIgnoredDuringExecution: you read it. "Required WHEN scheduling, ignored AFTERWARDS". And if you blank in the exam, k explain --recursive pod.spec.affinity prints the whole field tree.
Nessuno impara a memoria requiredDuringSchedulingIgnoredDuringExecution: si legge. "Richiesto QUANDO si schedula, ignorato DOPO". E se ti blocchi all'esame, k explain --recursive pod.spec.affinity stampa l'intero albero dei campi.
Teoría · nodeSelector vs affinity
Theory · nodeSelector vs affinity
Teoria · nodeSelector vs affinity
La diferencia
The difference
La differenza
nodeSelector
node Affinity
Simple: label = valor
Expresiones (In, NotIn, Exists…)
Simple: label = value
Expressions (In, NotIn, Exists…)
Semplice: label = valore
Espressioni (In, NotIn, Exists…)
Solo AND exacto
OR, rangos, negación
Exact AND only
OR, ranges, negation
Solo AND esatto
OR, intervalli, negazione
Solo "requerido"
requerido O preferido
"Required" only
required OR preferred
Solo "richiesto"
richiesto O preferito
required vs preferred
required vs preferred
required vs preferred
required: regla dura. Si no se cumple, el pod queda Pending.
preferred: regla blanda, con weight. El scheduler la intenta; si no puede, coloca el pod igual en otro lado.
operator: In, NotIn, Exists, DoesNotExist, Gt, Lt.
required: hard rule. If unmet, the pod stays Pending.
preferred: soft rule, with a weight. The scheduler tries; if it can't, it places the pod elsewhere anyway.
operator: In, NotIn, Exists, DoesNotExist, Gt, Lt.
required: regola dura. Se non è soddisfatta, il pod resta Pending.
preferred: regola morbida, con weight. Lo scheduler ci prova; se non riesce, piazza comunque il pod altrove.
operator: In, NotIn, Exists, DoesNotExist, Gt, Lt.
★
Regla práctica: si te alcanza con "label = valor", usá nodeSelector (más corto, menos error). Si necesitás lógica (varios valores, preferencias, negación), pasás a nodeAffinity.
Rule of thumb: if "label = value" is enough, use nodeSelector (shorter, fewer typos). If you need logic (several values, preferences, negation), move to nodeAffinity.
Regola pratica: se ti basta "label = valore", usa nodeSelector (più corto, meno errori). Se ti serve logica (più valori, preferenze, negazione), passa a nodeAffinity.
⚠
IgnoredDuringExecution significa que si el label del nodo cambia después, el pod ya colocado no se expulsa. No existe todavía un "RequiredDuringExecution".
IgnoredDuringExecution means that if the node label changes later, an already-placed pod is not evicted. There is still no "RequiredDuringExecution".
IgnoredDuringExecution vuol dire che se la label del nodo cambia dopo, il pod già piazzato non viene espulso. Non esiste ancora un "RequiredDuringExecution".
5.2
Repeler · 15 min
Repel · 15 min
Respingere · 15 min
Taints y Tolerations
Taints and Tolerations
Taints e Tolerations
Lo inverso a affinity: el nodo rechaza pods, salvo los que "toleran" el taint.
The inverse of affinity: the node rejects pods, except those that "tolerate" the taint.
L'inverso dell'affinity: il nodo rifiuta i pod, tranne quelli che "tollerano" il taint.
Práctica · Manchar y tolerar
Practice · Taint and tolerate
Pratica · Taintare e tollerare
Poner un taint a un nodo
Taint a node
Mettere un taint su un nodo
# Formato: key=value:efecto# Format: key=value:effect# Formato: key=value:effettok taint node w1 gpu=true:NoSchedule
# Quitar el taint (mismo comando + guion al final)# Remove the taint (same command + trailing dash)# Togliere il taint (stesso comando + trattino finale)k taint node w1 gpu=true:NoSchedule-
# Ver los taints de todos los nodos de un vistazo# See every node's taints at a glance# Vedere i taint di tutti i nodi a colpo d'occhiok get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
Ahora ningún pod se schedulea en w1… salvo los que toleren ese taint.
Now no pod schedules onto w1… except those that tolerate that taint.
Adesso nessun pod viene schedulato su w1… tranne quelli che tollerano quel taint.
Toleration en el pod
Toleration on the pod
Toleration nel pod
spec:
tolerations: # nivel POD# POD level# livello POD
- key: "gpu"operator: "Equal"# Equal usa value; Exists lo ignora# Equal uses value; Exists ignores it# Equal usa value; Exists lo ignoravalue: "true"effect: "NoSchedule"# si lo omitís, tolera TODOS los efectos de esa key# omit it and you tolerate ALL effects of that key# se lo ometti, tolleri TUTTI gli effetti di quella keycontainers:
- name: app
image: nginx
★
Ya viste taints en acción: el control plane trae node-role.kubernetes.io/control-plane:NoSchedule para que no le caigan cargas de usuario. Los DaemonSets de sistema (kube-proxy, CNI) llevan operator: Exists sin key: eso tolera cualquier taint, y por eso sobreviven a todo.
You have already seen taints at work: the control plane ships with node-role.kubernetes.io/control-plane:NoSchedule so user workloads stay off it. System DaemonSets (kube-proxy, CNI) carry operator: Exists with no key: that tolerates any taint, which is why they survive everything.
Hai già visto i taint all'opera: il control plane arriva con node-role.kubernetes.io/control-plane:NoSchedule perché i carichi utente stiano alla larga. I DaemonSet di sistema (kube-proxy, CNI) hanno operator: Exists senza key: questo tollera qualsiasi taint, ed è il motivo per cui sopravvivono a tutto.
Teoría · Los 3 efectos
Theory · The 3 effects
Teoria · I 3 effetti
Los efectos de un taint
A taint's effects
Gli effetti di un taint
Efecto
Qué hace
Effect
What it does
Effetto
Cosa fa
NoSchedule
No entran pods nuevos sin toleration
NoSchedule
No new pods without a toleration
NoSchedule
Niente pod nuovi senza toleration
PreferNoSchedule
Evita, pero no prohíbe
PreferNoSchedule
Avoids, but does not forbid
PreferNoSchedule
Evita, ma non vieta
NoExecute
Además EXPULSA los pods ya corriendo
NoExecute
Also EVICTS pods already running
NoExecute
Inoltre ESPELLE i pod già in esecuzione
Affinity vs Taints — la diferencia clave
Affinity vs Taints — the key difference
Affinity vs Taints — la differenza chiave
Affinity es el pod diciendo "quiero ir a tal nodo" (atracción, desde el pod). Taint es el nodo diciendo "no quiero pods, salvo excepciones" (repulsión, desde el nodo).
Affinity is the pod saying "I want that node" (attraction, from the pod). Taint is the node saying "no pods here, bar exceptions" (repulsion, from the node).
Affinity è il pod che dice "voglio quel nodo" (attrazione, dal pod). Taint è il nodo che dice "niente pod, salvo eccezioni" (repulsione, dal nodo).
⚠
Un toleration es permiso, no dirección: NO garantiza que el pod vaya a ese nodo, solo le deja. Para forzarlo, combinás toleration (permiso) + nodeAffinity/nodeSelector (dirección).
A toleration is permission, not direction: it does NOT guarantee the pod lands there, it only allows it. To force it, combine toleration (permission) + nodeAffinity/nodeSelector (direction).
Una toleration è permesso, non direzione: NON garantisce che il pod finisca lì, gli consente solo di farlo. Per forzarlo, combini toleration (permesso) + nodeAffinity/nodeSelector (direzione).
★
Un toleration matchea un efecto puntual: tolerar NoSchedule no te salva de un NoExecute sobre la misma key. Si querés cubrir ambos, escribís dos entradas (o omitís effect). Con NoExecute podés agregar tolerationSeconds: 30 = "aguantame 30 s antes de echarme".
A toleration matches one specific effect: tolerating NoSchedule does not save you from a NoExecute on the same key. To cover both, write two entries (or omit effect). With NoExecute you can add tolerationSeconds: 30 = "give me 30 s before evicting me".
Una toleration corrisponde a un effetto preciso: tollerare NoSchedule non ti salva da un NoExecute sulla stessa key. Per coprirli entrambi scrivi due voci (o ometti effect). Con NoExecute puoi aggiungere tolerationSeconds: 30 = "dammi 30 s prima di espellermi".
5.3
Recursos · 15 min
Resources · 15 min
Risorse · 15 min
Requests, Limits y QoS
Requests, Limits and QoS
Requests, Limits e QoS
Reservar recursos decide dónde cabe el pod. Limitarlos decide qué puede consumir.
Reserving decides where the pod fits. Capping decides what it may consume.
Riservare decide dove entra il pod. Limitare decide quanto può consumare.
Práctica · Definir recursos
Practice · Define resources
Pratica · Definire le risorse
requests y limits en un pod
requests and limits on a pod
requests e limits in un pod
spec:
containers:
- name: app
image: nginx
resources: # nivel CONTAINER, no pod# CONTAINER level, not pod# livello CONTAINER, non podrequests: # lo que RESERVA → lo mira el scheduler# what it RESERVES → the scheduler reads this# quello che RISERVA → lo legge lo schedulercpu: "250m"memory: "128Mi"limits: # el TECHO → lo hace cumplir el kubelet# the CEILING → enforced by the kubelet# il TETTO → lo fa rispettare il kubeletcpu: "500m"memory: "256Mi"
Atajo imperativo y lectura del nodo
Imperative shortcut and reading the node
Scorciatoia imperativa e lettura del nodo
# Setear recursos sin editar YAML# Set resources without editing YAML# Impostare le risorse senza editare YAMLk set resources deploy web \
--requests=cpu=100m,memory=64Mi --limits=cpu=250m,memory=128Mi
# LA tabla del troubleshooting de Pending por recursos# THE table for troubleshooting resource-Pending# LA tabella per il troubleshooting di Pending da risorsek describe node w1 | grep -A8 "Allocated resources"# Topes por namespace# Namespace-wide caps# Limiti a livello di namespacek get limitrange # defaults por pod# per-pod defaults# default per podk get resourcequota # tope total del ns# total cap of the ns# tetto totale del ns
★
Las unidades importan: CPU en m (millicores, 1000m = 1 core), memoria en Mi/Gi (binario) o M/G (decimal). El HPA del Módulo 4 calcula el porcentaje sobre requests.cpu: sin requests, el HPA muestra <unknown>.
Units matter: CPU in m (millicores, 1000m = 1 core), memory in Mi/Gi (binary) or M/G (decimal). The HPA from Module 4 computes its percentage against requests.cpu: with no requests, the HPA shows <unknown>.
Le unità contano: CPU in m (millicore, 1000m = 1 core), memoria in Mi/Gi (binario) o M/G (decimale). L'HPA del Modulo 4 calcola la percentuale su requests.cpu: senza requests, l'HPA mostra <unknown>.
Teoría · Request vs Limit
Theory · Request vs Limit
Teoria · Request vs Limit
La distinción crucial
The crucial distinction
La distinzione cruciale
Request
Limit
Lo que reserva
El techo máximo
What it reserves
The hard ceiling
Quello che riserva
Il tetto massimo
Lo usa el scheduler para ver dónde cabe
Lo hace cumplir el kubelet en runtime
Used by the scheduler to see where it fits
Enforced by the kubelet at runtime
Usato dallo scheduler per vedere dove entra
Fatto rispettare dal kubelet a runtime
No puede superar el allocatable del nodo
Puede sobrecomprometerse (>100%)
Cannot exceed the node's allocatable
May be overcommitted (>100%)
Non può superare l'allocatable del nodo
Può essere sovracommesso (>100%)
Al superar el limit
Going over the limit
Superare il limit
CPU: se throttlea (se frena, no se mata).
Memoria: se mata el container → OOMKilled.
CPU: it gets throttled (slowed, not killed).
Memory: the container is killed → OOMKilled.
CPU: viene throttlata (rallentata, non uccisa).
Memoria: il container viene ucciso → OOMKilled.
Las 3 clases de QoS
The 3 QoS classes
Le 3 classi di QoS
Clase
Cuándo
Al desalojar
Class
When
On eviction
Classe
Quando
All'eviction
Guaranteed
requests == limits (todo)
último
Guaranteed
requests == limits (all)
last
Guaranteed
requests == limits (tutto)
ultimo
Burstable
tiene requests, pero ≠ limits
segundo
Burstable
has requests, but ≠ limits
second
Burstable
ha requests, ma ≠ limits
secondo
BestEffort
sin requests ni limits
primero
BestEffort
no requests, no limits
first
BestEffort
né requests né limits
per primo
Se lee con k get pod X -o jsonpath='{.status.qosClass}'. Cuando el nodo se queda sin memoria, el kubelet desaloja de abajo hacia arriba de esa tabla.
Read it with k get pod X -o jsonpath='{.status.qosClass}'. When the node runs out of memory, the kubelet evicts from the bottom of that table upwards.
Si legge con k get pod X -o jsonpath='{.status.qosClass}'. Quando il nodo esaurisce la memoria, il kubelet espelle dal fondo di quella tabella verso l'alto.
★
"Pending por recursos" es un clásico: el scheduler suma los requests ya reservados del nodo, no el uso real. Un nodo con 90% de CPU ociosa te rechaza igual si ya tiene todo reservado. Radar: k describe node → Allocated resources.
"Pending on resources" is a classic: the scheduler adds up the node's already-reserved requests, not actual usage. A node sitting 90% idle will still reject you if everything is reserved. Radar: k describe node → Allocated resources.
"Pending per risorse" è un classico: lo scheduler somma le requests già riservate del nodo, non l'uso reale. Un nodo al 90% inattivo ti rifiuta lo stesso se ha già riservato tutto. Radar: k describe node → Allocated resources.
VisorExplorerVisore · ¿en qué nodos puede entrar este pod?which nodes can take this pod?su quali nodi può entrare questo pod?
Los tres filtros del scheduler, en orden. Un nodo tiene que sobrevivir a todos para ser candidato.
The scheduler's three filters, in order. A node must survive all of them to be a candidate.
I tre filtri dello scheduler, in ordine. Un nodo deve sopravvivere a tutti per essere candidato.
5.4
Relación entre pods · 15 min
Pod-to-pod relations · 15 min
Relazioni fra pod · 15 min
Pod Affinity y Topology Spread
Pod Affinity and Topology Spread
Pod Affinity e Topology Spread
Juntar o separar pods entre sí, y distribuirlos de forma pareja.
Co-locate or separate pods, and spread them evenly.
Unire o separare i pod, e distribuirli in modo uniforme.
Práctica · Relaciones
Practice · Relations
Pratica · Relazioni
Pod Anti-Affinity — separar réplicas
Pod Anti-Affinity — split replicas
Pod Anti-Affinity — separare le repliche
# "No pongas dos pods de esta app en el mismo nodo"# "Don't put two pods of this app on the same node"# "Non mettere due pod di questa app sullo stesso nodo"spec: # en un Deployment: spec.template.spec# in a Deployment: spec.template.spec# in un Deployment: spec.template.specaffinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: web # ↔ labels del template, no del Deployment# ↔ template labels, not the Deployment's# ↔ label del template, non del DeploymenttopologyKey: kubernetes.io/hostname
Topology Spread — distribución pareja
Topology Spread — even distribution
Topology Spread — distribuzione uniforme
spec:
topologySpreadConstraints:
- maxSkew: 1 # diferencia máxima entre topologías# max difference across topologies# differenza massima fra topologietopologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule # dura · o ScheduleAnyway (blanda)# hard · or ScheduleAnyway (soft)# dura · o ScheduleAnyway (morbida)labelSelector:
matchLabels:
app: web
Reparte los pods de app=web lo más parejo posible entre nodos (diferencia máxima de 1).
Spreads app=web pods as evenly as possible across nodes (max difference of 1).
Distribuisce i pod app=web nel modo più uniforme possibile fra i nodi (differenza massima di 1).
⚠
Con required + 3 réplicas + 2 nodos, la tercera queda Pending: no hay dónde ponerla sin violar la regla. Con preferred / ScheduleAnyway, entra igual. Es exactamente el trade-off HA vs disponibilidad.
With required + 3 replicas + 2 nodes, the third stays Pending: nowhere to put it without breaking the rule. With preferred / ScheduleAnyway it schedules anyway. That is exactly the HA-vs-availability trade-off.
Con required + 3 repliche + 2 nodi, la terza resta Pending: non c'è dove metterla senza violare la regola. Con preferred / ScheduleAnyway entra comunque. È esattamente il compromesso HA contro disponibilità.
Teoría · Los conceptos
Theory · The concepts
Teoria · I concetti
Pod affinity vs node affinity
Pod affinity vs node affinity
Pod affinity vs node affinity
Node affinity (5.1) mira labels del NODO. Pod affinity/anti-affinity mira otros PODS ya ubicados: "quiero (o no) estar cerca de pods con tal label".
Node affinity (5.1) looks at NODE labels. Pod affinity/anti-affinity looks at other PODS already placed: "I want (or don't want) to sit near pods with this label".
Node affinity (5.1) guarda le label del NODO. Pod affinity/anti-affinity guarda altri POD già piazzati: "voglio (o non voglio) stare vicino a pod con questa label".
El topologyKey
The topologyKey
Il topologyKey
Define qué significa "estar cerca". kubernetes.io/hostname = mismo nodo; topology.kubernetes.io/zone = misma zona. Es la unidad de agrupación.
Defines what "near" means. kubernetes.io/hostname = same node; topology.kubernetes.io/zone = same zone. It is the grouping unit.
Definisce cosa significa "vicino". kubernetes.io/hostname = stesso nodo; topology.kubernetes.io/zone = stessa zona. È l'unità di raggruppamento.
Casos de uso típicos
Typical use cases
Casi d'uso tipici
Anti-affinity: repartir réplicas en nodos distintos (HA).
Affinity: poner el cache cerca de la app que lo usa.
Topology spread: distribución uniforme, la forma moderna y declarativa.
Anti-affinity: split replicas across nodes (HA).
Affinity: keep the cache next to the app using it.
Topology spread: even distribution, the modern declarative form.
Anti-affinity: distribuire le repliche su nodi diversi (HA).
Affinity: tenere la cache accanto all'app che la usa.
Topology spread: distribuzione uniforme, la forma moderna e dichiarativa.
★
Anti-affinity con topologyKey: hostname es el patrón clásico de HA: si un nodo cae, no te llevás todas las réplicas juntas.
Anti-affinity with topologyKey: hostname is the classic HA pattern: if a node dies, you don't lose every replica at once.
Anti-affinity con topologyKey: hostname è il classico pattern di HA: se un nodo cade, non perdi tutte le repliche insieme.
★
Cierre del módulo
Module wrap-up
Chiusura del modulo
Lo que te llevás del Módulo 5
What you take from Module 5
Cosa porti via dal Modulo 5
Resumen · Comandos clave
Summary · Key commands
Riassunto · Comandi chiave
Para tu chuleta
For your cheat sheet
Per il tuo bigliettino
# Labels de nodos# Node labels# Label dei nodik label node NODE key=value
k label node NODE key-
k get nodes --show-labels
# Taints# Taints# Taintsk taint node NODE key=val:NoSchedule
k taint node NODE key=val:NoSchedule-
# Recursos y diagnóstico de Pending# Resources and Pending diagnosis# Risorse e diagnosi di Pendingk set resources deploy D --requests=cpu=100m --limits=cpu=250m
k describe node NODE # → Allocated resources# → Allocated resources# → Allocated resourcesk describe pod POD # → Events → FailedScheduling# → Events → FailedScheduling# → Events → FailedScheduling# Campos que van en el pod spec:# Fields that live in the pod spec:# Campi che stanno nel pod spec:# nodeSelector / affinity.nodeAffinity# tolerations# resources.requests / limits (container)# affinity.podAntiAffinity# topologySpreadConstraints
Teoría · Lo esencial
Theory · The essentials
Teoria · L'essenziale
Las 4 ideas que no se negocian
The 4 non-negotiable ideas
Le 4 idee non negoziabili
nodeSelector/affinity = el pod elige nodo por label (atracción).
Taints/tolerations = el nodo repele; la toleration da permiso (repulsión).
requests deciden dónde cabe (scheduling); limits cortan el uso (runtime) y definen el QoS.
pod affinity = relación entre pods; topology spread = distribución pareja.
nodeSelector/affinity = the pod picks a node by label (attraction).
Taints/tolerations = the node repels; the toleration grants permission (repulsion).
requests decide where it fits (scheduling); limits cap usage (runtime) and set QoS.
pod affinity = pod-to-pod relation; topology spread = even distribution.
nodeSelector/affinity = il pod sceglie il nodo tramite label (attrazione).
Taints/tolerations = il nodo respinge; la toleration dà il permesso (repulsione).
requests decidono dove entra (scheduling); limits tagliano l'uso (runtime) e fissano il QoS.
pod affinity = relazione fra pod; topology spread = distribuzione uniforme.
★
El orden de los filtros: taints → afinidad/selector → recursos. Un pod se descarta de un nodo en ese orden y el evento reporta solo el primer motivo que falló. Por eso un nodo puede "caer por taint" aunque tampoco cumpla el selector.
Filter order: taints → affinity/selector → resources. A pod is ruled out of a node in that order and the event reports only the first reason that failed. That is why a node can "fail on the taint" even when it also misses the selector.
Ordine dei filtri: taint → affinity/selector → risorse. Un pod viene scartato da un nodo in quell'ordine e l'evento riporta solo il primo motivo fallito. Ecco perché un nodo può "cadere per il taint" anche se non soddisfa nemmeno il selector.
Checkpoint Modulo 5: attrazione (affinity), repulsione (taint), risorse (requests/limits/QoS) e relazioni fra pod. Chiude Workloads & Scheduling (15%). Prossimo: Modulo 6 — Services & Networking.
★
Fase 2 · resolver en vivo
Phase 2 · solve it live
Fase 2 · risolvere dal vivo
Práctica — 9 ejercicios
Practice — 9 exercises
Pratica — 9 esercizi
E1–E5 construyen; T1–T4 diagnostican. ~5 min cada uno.
E1–E5 build; T1–T4 diagnose. ~5 min each.
E1–E5 costruiscono; T1–T4 diagnosticano. ~5 min ciascuno.
0 / 9
⚙
Setup. Cluster de 2 nodos (cp con taint control-plane:NoSchedule, w1 worker limpio). Namespace de trabajo: k create ns m5, y -n m5 en todo. Ojo: labels y taints de nodo NO son namespaced — sobreviven al delete ns, hay que limpiarlos aparte (última tarjeta).
Setup. Two-node cluster (cp tainted control-plane:NoSchedule, w1 a clean worker). Working namespace: k create ns m5, and -n m5 on everything. Careful: node labels and taints are NOT namespaced — they survive delete ns and must be cleaned separately (last card).
Setup. Cluster a 2 nodi (cp con taint control-plane:NoSchedule, w1 worker pulito). Namespace di lavoro: k create ns m5, e -n m5 su tutto. Attenzione: label e taint dei nodi NON sono namespaced — sopravvivono al delete ns e vanno puliti a parte (ultima scheda).
◆
Manifests publicados. Cada ejercicio tiene su YAML servido por este mismo sitio, así que no hace falta copiar nada a mano: k apply -f <url>/manifests/m5e1.yaml. Los E son la solución de referencia (mirá el YAML cuando quieras ver cómo se escribe); los Tplantan el problema — aplicalos y diagnosticá sin leerlos. Todos crean el namespace m5, y k delete -f <misma-url> los limpia. Cada tarjeta trae el comando exacto, con botón de copiar.
Published manifests. Every exercise has its YAML served by this very site, so nothing needs copying by hand: k apply -f <url>/manifests/m5e1.yaml. The E ones are the reference solution (open the YAML whenever you want to see how it is written); the T ones plant the problem — apply them and diagnose without reading them. All of them create namespace m5, and k delete -f <same-url> cleans up. Each card carries the exact command, with a copy button.
Manifest pubblicati. Ogni esercizio ha il suo YAML servito da questo stesso sito, così non serve copiare nulla a mano: k apply -f <url>/manifests/m5e1.yaml. Gli E sono la soluzione di riferimento (apri lo YAML quando vuoi vedere come si scrive); i Tpiantano il problema — applicali e diagnostica senza leggerli. Tutti creano il namespace m5, e k delete -f <stessa-url> pulisce. Ogni scheda porta il comando esatto, con pulsante copia.
🧠
Modelo mental. El scheduler decide; vos lo influenciás con 4 palancas: atraer (nodeSelector/nodeAffinity), repeler (taint + toleration), reservar (requests/limits) y distribuir (pod anti-affinity / topology spread). Filtros en orden: taints → afinidad/selector → recursos. Resolvé antes de abrir la solución.
Mental model. The scheduler decides; you steer it with 4 levers: attract (nodeSelector/nodeAffinity), repel (taint + toleration), reserve (requests/limits) and spread (pod anti-affinity / topology spread). Filters in order: taints → affinity/selector → resources. Solve it before opening the solution.
Modello mentale. Lo scheduler decide; tu lo guidi con 4 leve: attrarre (nodeSelector/nodeAffinity), respingere (taint + toleration), riservare (requests/limits) e distribuire (pod anti-affinity / topology spread). Filtri in ordine: taint → affinity/selector → risorse. Risolvi prima di aprire la soluzione.
E1
nodeSelector — forzar un pod a un nodo
nodeSelector — pin a pod to a node
nodeSelector — forzare un pod su un nodo
~5 min
Etiquetá el nodo w1 con disktype=ssd.
Creá un Pod web-ssd (nginx) que solo pueda correr en nodos con esa label.
Verificá que cayó en w1.
Label node w1 with disktype=ssd.
Create a Pod web-ssd (nginx) that can run only on nodes with that label.
Verify it landed on w1.
Etichetta il nodo w1 con disktype=ssd.
Crea un Pod web-ssd (nginx) che possa girare solo su nodi con quella label.
k label node w1 disktype=ssd
k get nodes -l disktype=ssd
# generar el esqueleto y agregarle el nodeSelector# generate the skeleton, then add the nodeSelector# genera lo scheletro, poi aggiungi il nodeSelectork run web-ssd --image=nginx -n m5 --dry-run=client -o yaml > web-ssd.yaml
# bajo spec: nodeSelector: { disktype: ssd }# under spec: nodeSelector: { disktype: ssd }# sotto spec: nodeSelector: { disktype: ssd }k apply -f web-ssd.yaml
k get pod web-ssd -n m5 -o wide # NODE = w1# NODE = w1# NODE = w1
Claves
Key points
Punti chiave
nodeSelector es un AND exacto: el nodo debe tener todas las labels con el valor exacto. Si ninguno matchea → Pending (ese es el T1).
Va en spec.nodeSelector; en un Deployment, spec.template.spec.nodeSelector.
nodeSelector is an exact AND: the node must carry every label with the exact value. If none matches → Pending (that's T1).
It lives at spec.nodeSelector; in a Deployment, spec.template.spec.nodeSelector.
nodeSelector è un AND esatto: il nodo deve avere tutte le label con il valore esatto. Se nessuno corrisponde → Pending (è il T1).
Sta in spec.nodeSelector; in un Deployment, spec.template.spec.nodeSelector.
E2
Node Affinity — required + preferred
Node Affinity — required + preferred
Node Affinity — required + preferred
~5 min
Creá un Pod web-aff (nginx) que obligatoriamente corra en un nodo cuyo disktype esté en {ssd, nvme}, y preferentemente (peso 50) en uno con zone=a. Verificá dónde cayó y por qué.
Create a Pod web-aff (nginx) that must run on a node whose disktype is in {ssd, nvme}, and preferably (weight 50) on one with zone=a. Verify where it landed and why.
Crea un Pod web-aff (nginx) che debba girare su un nodo il cui disktype sia in {ssd, nvme}, e preferibilmente (peso 50) su uno con zone=a. Verifica dove è finito e perché.
k taint node w1 gpu=true:NoSchedule
# 2 · sin toleration: Pending (w1 por gpu, cp por control-plane)# 2 · no toleration: Pending (w1 by gpu, cp by control-plane)# 2 · senza toleration: Pending (w1 per gpu, cp per control-plane)k run no-tol --image=nginx -n m5
k describe pod no-tol -n m5 | grep -A6 Events
# 3+4 · toleration que cubre AMBOS efectos de la misma key# 3+4 · a toleration covering BOTH effects of the same key# 3+4 · una toleration che copre ENTRAMBI gli effetti della stessa keyspec:
tolerations:
- key: gpu
operator: Equal
value: "true"effect: NoSchedule
- key: gpu
operator: Equal
value: "true"effect: NoExecute # sin esta línea, el NoExecute lo expulsa# without this one, NoExecute evicts it# senza questa, il NoExecute lo espellek taint node w1 gpu=true:NoSchedule-
k taint node w1 gpu=true:NoExecute
k get pods -n m5 -o wide
Claves
Key points
Punti chiave
Un toleration matchea un efecto puntual. Omitir effect cubre todos los de esa key; operator: Exists sin key tolera cualquier taint.
NoExecute vacía el nodo: los Pods pelados sin toleration se borran y no vuelven; los que tiene un Deployment se recrean (y quedan Pending). Lección: controller vs pod suelto.
Quitar un taint = mismo comando con - al final, y el efecto tiene que coincidir.
A toleration matches one specific effect. Omitting effect covers all of them for that key; operator: Exists with no key tolerates any taint.
NoExecute drains the node: bare Pods without a toleration are deleted and never come back; Deployment-owned ones are recreated (and go Pending). Lesson: controller vs bare pod.
Removing a taint = same command with a trailing -, and the effect must match.
Una toleration corrisponde a un effetto preciso. Omettere effect li copre tutti per quella key; operator: Exists senza key tollera qualsiasi taint.
NoExecute svuota il nodo: i Pod nudi senza toleration vengono cancellati e non tornano; quelli di un Deployment vengono ricreati (e restano Pending). Lezione: controller contro pod nudo.
Togliere un taint = stesso comando con - finale, e l'effetto deve coincidere.
E4
Requests, limits y "Allocated resources"
Requests, limits and "Allocated resources"
Requests, limits e "Allocated resources"
~5 min
Creá un Deployment resweb (nginx, 2 réplicas) con requests cpu=100m/mem=64Mi y limits cpu=250m/mem=128Mi.
Verificá los recursos en el pod.
Leé cuánto hay reservado y libre en w1, y decí qué QoS class le tocó al pod.
Create a Deployment resweb (nginx, 2 replicas) with requests cpu=100m/mem=64Mi and limits cpu=250m/mem=128Mi.
Verify the resources on the pod.
Read how much is reserved and free on w1, and say which QoS class the pod got.
Crea un Deployment resweb (nginx, 2 repliche) con requests cpu=100m/mem=64Mi e limits cpu=250m/mem=128Mi.
Verifica le risorse nel pod.
Leggi quanto è riservato e libero su w1, e dì quale QoS class ha ottenuto il pod.
El labelSelector del anti-affinity apunta a las labels del template, no del Deployment.
Con required, una réplica de más queda Pending: la regla gana sobre la disponibilidad. maxSkew + ScheduleAnyway es la versión tolerante.
Acordate de volver a poner el taint del control plane al terminar.
The anti-affinity labelSelector targets the template labels, not the Deployment's.
With required, one replica too many stays Pending: the rule beats availability. maxSkew + ScheduleAnyway is the forgiving version.
Remember to put the control-plane taint back when you are done.
Il labelSelector dell'anti-affinity punta alle label del template, non a quelle del Deployment.
Con required, una replica di troppo resta Pending: la regola batte la disponibilità. maxSkew + ScheduleAnyway è la versione tollerante.
Ricordati di rimettere il taint del control plane alla fine.
T1
Troubleshooting — Pending por constraint imposible
Troubleshooting — Pending on an impossible constraint
Troubleshooting — Pending per un vincolo impossibile
~5 min
Hay un Pod stuck-1 en m5 que quedó Pending y no arranca nunca. Diagnosticá por qué no se puede schedular y hacelo correr con el mínimo cambio.
There is a Pod stuck-1 in m5 stuck in Pending that never starts. Diagnose why it cannot be scheduled and get it running with the smallest change.
C'è un Pod stuck-1 in m5 bloccato in Pending che non parte mai. Diagnostica perché non può essere schedulato e fallo partire con la modifica minima.
manifest · plantar el problemamanifest · plant the problemmanifest · piantare il problema
Ver soluciónShow solutionMostra soluzione
Equivalente imperativo:k run stuck-1 --image=nginx -n m5 --overrides='{"spec":{"nodeSelector":{"disktype":"nvme"}}}'
Imperative equivalent:k run stuck-1 --image=nginx -n m5 --overrides='{"spec":{"nodeSelector":{"disktype":"nvme"}}}'
Equivalente imperativo:k run stuck-1 --image=nginx -n m5 --overrides='{"spec":{"nodeSelector":{"disktype":"nvme"}}}'
k get pod stuck-1 -n m5 -o wide # Pending, NODE <none># Pending, NODE <none># Pending, NODE <none>k describe pod stuck-1 -n m5 | grep -A6 Events
# 0/2 nodes are available: ... didn't match Pod's node affinity/selector# 0/2 nodes are available: ... didn't match Pod's node affinity/selector# 0/2 nodes are available: ... didn't match Pod's node affinity/selectork get pod stuck-1 -n m5 -o jsonpath='{.spec.nodeSelector}'; echo
# {"disktype":"nvme"} → ningún nodo tiene esa label# {"disktype":"nvme"} → no node carries that label# {"disktype":"nvme"} → nessun nodo ha quella label# Arreglo A: satisfacer el selector etiquetando un nodo# Fix A: satisfy the selector by labelling a node# Fix A: soddisfare il selector etichettando un nodok label node w1 disktype=nvme --overwrite
# Arreglo B: corregir el selector — pero en un Pod es inmutable: borrar y recrear# Fix B: correct the selector — but it is immutable on a Pod: delete and recreate# Fix B: correggere il selector — ma su un Pod è immutabile: cancella e ricrea
Claves
Key points
Punti chiave
Pending = el scheduler no encontró nodo. El porqué SIEMPRE está en Events: 0/N nodes are available: … desglosa la causa por nodo.
Red herring: el bloque Tolerations: con not-ready/unreachable aparece en todos los pods — lo inyecta el admission controller DefaultTolerationSeconds. No es tu problema. Mirá Events.
Un Pod suelto no se re-schedulea editándole el selector (campos inmutables); un Deployment sí, con un rollout.
Pending = the scheduler found no node. The why is ALWAYS in Events: 0/N nodes are available: … breaks the cause down per node.
Red herring: the Tolerations: block with not-ready/unreachable shows up on every pod — injected by the DefaultTolerationSeconds admission controller. Not your problem. Read Events.
A bare Pod is not rescheduled by editing its selector (immutable fields); a Deployment is, via a rollout.
Pending = lo scheduler non ha trovato un nodo. Il perché è SEMPRE in Events: 0/N nodes are available: … scompone la causa per nodo.
Falsa pista: il blocco Tolerations: con not-ready/unreachable compare su tutti i pod — lo inietta l'admission controller DefaultTolerationSeconds. Non è il tuo problema. Leggi Events.
Un Pod nudo non viene rischedulato modificandone il selector (campi immutabili); un Deployment sì, con un rollout.
T2
Troubleshooting — Pending por recursos
Troubleshooting — Pending on resources
Troubleshooting — Pending per risorse
~5 min
Un Deployment stuck-2 también está Pending, pero el cluster está casi vacío de carga real. Averiguá por qué no entra y hacelo correr.
A Deployment stuck-2 is also Pending, yet the cluster carries almost no real load. Work out why it does not fit and get it running.
Anche un Deployment stuck-2 è Pending, ma il cluster non ha quasi carico reale. Scopri perché non entra e fallo partire.
manifest · plantar el problemamanifest · plant the problemmanifest · piantare il problema
Ver soluciónShow solutionMostra soluzione
Equivalente imperativo:k create deploy stuck-2 --image=nginx -n m5 && k set resources deploy stuck-2 -n m5 --requests=cpu=6
Imperative equivalent:k create deploy stuck-2 --image=nginx -n m5 && k set resources deploy stuck-2 -n m5 --requests=cpu=6
Equivalente imperativo:k create deploy stuck-2 --image=nginx -n m5 && k set resources deploy stuck-2 -n m5 --requests=cpu=6
k describe pod -n m5 -l app=stuck-2 | grep -A6 Events
# 0/2 nodes are available: 2 Insufficient cpu# 0/2 nodes are available: 2 Insufficient cpu# 0/2 nodes are available: 2 Insufficient cpuk get pod -n m5 -l app=stuck-2 -o jsonpath='{.items[0].spec.containers[0].resources.requests}'; echo
# {"cpu":"6"} → pide 6 cores; ningún nodo tiene tanto LIBRE# {"cpu":"6"} → asks for 6 cores; no node has that much FREE# {"cpu":"6"} → chiede 6 core; nessun nodo ne ha così tanti LIBERIk describe node w1 | grep -A8 "Allocated resources"k set resources deploy stuck-2 -n m5 --requests=cpu=100m,memory=64Mi --limits=cpu=200m,memory=128Mi
k get pods -n m5 -o wide
Claves
Key points
Punti chiave
Insufficient cpu/memory = ningún nodo tiene suficiente request libre (allocatable − ya reservado). Se mira lo reservado, no el uso real.
Mismo síntoma que T1 (Pending), distinta causa. El reflejo es siempre describe pod → Events, que discrimina entre selector/taint y recursos.
Herramienta distinta para confirmar: T1 → get nodes --show-labels; T2 → describe node (Allocated resources) y k top nodes para el uso real.
Insufficient cpu/memory = no node has enough free request (allocatable − already reserved). It looks at reservations, not actual usage.
Same symptom as T1 (Pending), different cause. The reflex is always describe pod → Events, which discriminates between selector/taint and resources.
Different tool to confirm: T1 → get nodes --show-labels; T2 → describe node (Allocated resources) plus k top nodes for real usage.
Insufficient cpu/memory = nessun nodo ha abbastanza request libera (allocatable − già riservato). Guarda le prenotazioni, non l'uso reale.
Stesso sintomo del T1 (Pending), causa diversa. Il riflesso è sempre describe pod → Events, che distingue fra selector/taint e risorse.
Strumento diverso per confermare: T1 → get nodes --show-labels; T2 → describe node (Allocated resources) più k top nodes per l'uso reale.
T3
Troubleshooting — el DaemonSet al que le falta un nodo
Troubleshooting — the DaemonSet missing a node
Troubleshooting — il DaemonSet a cui manca un nodo
~5 min
Un DaemonSet node-agent debería correr un pod por nodo. Aplicalo, contá los pods y explicá por qué falta uno. Después hacé que corra en todos, sin tocar los nodos.
Ojo con el síntoma: acá no hay ningún pod Pending. Los que existen están perfectos.
A DaemonSet node-agent should run one pod per node. Apply it, count the pods and explain why one is missing. Then make it run everywhere, without touching the nodes.
Mind the symptom: there is no Pending pod at all here. The ones that exist are perfectly fine.
Un DaemonSet node-agent dovrebbe eseguire un pod per nodo. Applicalo, conta i pod e spiega perché ne manca uno. Poi fallo girare ovunque, senza toccare i nodi.
Attenzione al sintomo: qui non c'è nessun pod Pending. Quelli che esistono stanno benissimo.
manifest · plantar el problemamanifest · plant the problemmanifest · piantare il problema
Ver soluciónShow solutionMostra soluzione
k get ds -n m5
# DESIRED 1, CURRENT 1, READY 1 ... pero hay 2 nodos# DESIRED 1, CURRENT 1, READY 1 ... but there are 2 nodes# DESIRED 1, CURRENT 1, READY 1 ... ma i nodi sono 2k get nodes
k get pods -n m5 -o wide # solo en w1; en cp no hay nada# only on w1; nothing on cp# solo su w1; su cp niente# el nodo que falta está tainteado, y el DS no lo tolera# the missing node is tainted, and the DS does not tolerate it# il nodo mancante è taintato, e il DS non lo tollerak describe node cp | grep -A2 Taints
# node-role.kubernetes.io/control-plane:NoSchedule# arreglo: agregarle la toleration al template del DS# fix: add the toleration to the DS template# fix: aggiungere la toleration al template del DSk edit ds node-agent -n m5
spec:
template:
spec:
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
k get ds -n m5 # DESIRED 2# DESIRED 2# DESIRED 2
Claves
Key points
Punti chiave
El síntoma no es Pending, es un número. El controller del DaemonSet excluye del DESIRED a los nodos que el pod no podría tolerar: no crea un pod para que quede Pending, directamente no lo cuenta. Por eso get pods se ve sano y el problema solo aparece en get ds.
Es el mismo taint del control plane que en E3, del otro lado del mostrador: en E3 lo ponés vos, acá ya estaba.
operator: Exists sin value tolera cualquier valor de esa key. Es exactamente lo que usan kube-proxy y los CNI para llegar a todos los nodos: mirá k get ds -n kube-system kube-proxy -o yaml.
Regla general: si un DaemonSet tiene que estar en el control plane, necesita la toleration sí o sí. Es la causa nº 1 de "instalé el agente y no está en el master".
The symptom is not Pending, it is a number. The DaemonSet controller excludes from DESIRED the nodes the pod could not tolerate: it does not create a pod and leave it Pending, it simply does not count the node. That is why get pods looks healthy and the problem only shows in get ds.
It is the same control-plane taint as in E3, from the other side of the counter: in E3 you set it, here it was already there.
operator: Exists with no value tolerates any value of that key. It is exactly what kube-proxy and the CNIs use to reach every node: look at k get ds -n kube-system kube-proxy -o yaml.
General rule: if a DaemonSet has to be on the control plane, it needs the toleration, no exceptions. It is the number-one cause of "I installed the agent and it is not on the master".
Il sintomo non è Pending, è un numero. Il controller del DaemonSet esclude dal DESIRED i nodi che il pod non potrebbe tollerare: non crea un pod lasciandolo Pending, semplicemente non conta il nodo. Per questo get pods sembra sano e il problema compare solo in get ds.
È lo stesso taint del control plane di E3, dall'altro lato del bancone: in E3 lo metti tu, qui c'era già.
operator: Exists senza value tollera qualsiasi valore di quella key. È esattamente ciò che usano kube-proxy e i CNI per raggiungere tutti i nodi: guarda k get ds -n kube-system kube-proxy -o yaml.
Regola generale: se un DaemonSet deve stare sul control plane, la toleration serve per forza. È la causa numero uno di "ho installato l'agent e sul master non c'è".
T4
Troubleshooting — las réplicas que no entran
Troubleshooting — the replicas that will not fit
Troubleshooting — le repliche che non entrano
~6 min
El Deployment spread-web pide 4 réplicas y levanta menos; el resto queda Pending. Hay CPU de sobra y ningún nodo tiene labels raras. Diagnosticá qué filtro las rechaza y decidí cómo arreglarlo: ¿aflojás la regla o agregás nodos?
Deployment spread-web asks for 4 replicas and brings up fewer; the rest stay Pending. There is CPU to spare and no node carries odd labels. Diagnose which filter rejects them and decide how to fix it: loosen the rule or add nodes?
Il Deployment spread-web chiede 4 repliche e ne alza meno; il resto resta Pending. C'è CPU in abbondanza e nessun nodo ha label strane. Diagnostica quale filtro le respinge e decidi come sistemarlo: allentare la regola o aggiungere nodi?
manifest · plantar el problemamanifest · plant the problemmanifest · piantare il problema
Ver soluciónShow solutionMostra soluzione
k get pods -n m5 -o wide -l app=spread-web
k describe pod -n m5 -l app=spread-web | grep -A6 Events
# 0/2 nodes are available: 1 node(s) didn't match pod anti-affinity rules,# 1 node(s) had untolerated taint(s). ... preemption: 0/2 nodes are available:# 2 Preemption is not helpful for scheduling.# la cuenta: anti-affinity por hostname = máximo 1 réplica por nodo# the arithmetic: anti-affinity by hostname = at most 1 replica per node# il conto: anti-affinity per hostname = al massimo 1 replica per nodok get nodes # contá los SCHEDULABLES (sin taint) → ese es el techo# count the SCHEDULABLE ones (untainted) → that is the ceiling# conta i SCHEDULABILI (senza taint) → quello è il tetto# Arreglo A — aflojar: preferred en vez de required# Fix A — loosen it: preferred instead of required# Fix A — allentare: preferred invece di requiredpodAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels: { app: spread-web }
topologyKey: kubernetes.io/hostname
# Arreglo B — con topologySpread, que sí sabe repartir de a varias# Fix B — with topologySpread, which does know how to pack several# Fix B — con topologySpread, che sa distribuirne più d'unatopologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels: { app: spread-web }
# Arreglo C — dar más nodos: destaintear cp# Fix C — give it more nodes: untaint cp# Fix C — dargli più nodi: togliere il taint a cpk taint node cp node-role.kubernetes.io/control-plane:NoSchedule-
# … el techo sube en 1, no llega a 4 salvo que haya 4 nodos# … the ceiling goes up by 1; it reaches 4 only with 4 nodes# … il tetto sale di 1; arriva a 4 solo con 4 nodi
Claves
Key points
Punti chiave
Anti-affinity required por hostname pone un techo duro: una réplica por nodo. Pedir más réplicas que nodos schedulables deja el resto Pending para siempre, y ninguna cantidad de CPU lo arregla.
El Events lista las causas por nodo y acá hay dos distintas en la misma línea: un nodo rechaza por la regla, el otro por el taint. Leer las dos partes es lo que te da el techo real.
La diferencia de fondo con topologySpread: la anti-affinity es binaria (hay o no hay un pod igual), el spread cuenta y admite un desbalance de maxSkew. Por eso el spread reparte 4 réplicas en 2 nodos y la anti-affinity no.
Antes de tocar el YAML, hacé la cuenta: réplicas ≤ nodos schedulables. Si no cierra, no es un bug, es aritmética.
El no-arreglo: borrar el bloque affinity. El Pending desaparece y parece resuelto — pero las réplicas se apilan todas en un nodo, que es justo lo que la regla evitaba. Perdiste el requisito, no lo cumpliste. Si el enunciado pide alta disponibilidad, esto puntúa cero: aflojá la regla (A) o repartí de verdad (B), no la elimines.
required anti-affinity by hostname sets a hard ceiling: one replica per node. Asking for more replicas than schedulable nodes leaves the rest Pending forever, and no amount of CPU fixes it.
Events lists the causes per node and here there are two different ones on the same line: one node refuses on the rule, the other on the taint. Reading both parts is what gives you the real ceiling.
The underlying difference from topologySpread: anti-affinity is binary (there either is or is not a matching pod), spread counts and tolerates an imbalance of maxSkew. That is why spread fits 4 replicas on 2 nodes and anti-affinity does not.
Before touching the YAML, do the arithmetic: replicas ≤ schedulable nodes. If it does not add up, it is not a bug, it is counting.
The non-fix: deleting the affinity block. The Pending goes away and it looks solved — but the replicas all pile onto one node, which is exactly what the rule prevented. You lost the requirement, you did not meet it. If the task asks for high availability, this scores zero: loosen the rule (A) or genuinely spread (B), do not delete it.
L'anti-affinity required per hostname impone un tetto rigido: una replica per nodo. Chiedere più repliche dei nodi schedulabili lascia il resto Pending per sempre, e nessuna quantità di CPU lo risolve.
Events elenca le cause per nodo e qui ce ne sono due diverse sulla stessa riga: un nodo rifiuta per la regola, l'altro per il taint. Leggere entrambe le parti è ciò che ti dà il tetto reale.
La differenza di fondo con topologySpread: l'anti-affinity è binaria (c'è o non c'è un pod uguale), lo spread conta e ammette uno sbilanciamento di maxSkew. Per questo lo spread distribuisce 4 repliche su 2 nodi e l'anti-affinity no.
Prima di toccare lo YAML, fai il conto: repliche ≤ nodi schedulabili. Se non torna, non è un bug, è aritmetica.
Il non-fix: cancellare il blocco affinity. Il Pending sparisce e sembra risolto — ma le repliche si accatastano tutte su un nodo, che è proprio ciò che la regola evitava. Hai perso il requisito, non l'hai soddisfatto. Se la consegna chiede alta disponibilità, questo vale zero: allenta la regola (A) o distribuisci davvero (B), non eliminarla.
✕
Limpieza — labels y taints no son namespaced
Cleanup — labels and taints are not namespaced
Pulizia — label e taint non sono namespaced
~1 min
Borrar el namespace no revierte lo que tocaste en los nodos. Dejalos como estaban:
Deleting the namespace does not undo what you changed on the nodes. Put them back:
Cancellare il namespace non annulla quello che hai toccato sui nodi. Rimettili a posto:
k label node w1 disktype- zone- 2>/dev/null
k taint node w1 gpu=true:NoSchedule- 2>/dev/null
k taint node w1 gpu=true:NoExecute- 2>/dev/null
k taint node cp node-role.kubernetes.io/control-plane:NoSchedule # restaurar# restore# ripristinak delete ns m5
Autoevaluación — antes de marcar M5 como ★ dominado
Self-check — before marking M5 as ★ mastered
Autovalutazione — prima di segnare M5 come ★ padroneggiato
Forzar un pod a un nodo con nodeSelector, sin mirar.
Escribir un nodeAffinity required + preferred con operator: In.
Taintear, tolerar, y explicar NoSchedule vs NoExecute (y qué le pasa a un pod pelado).
Setear requests/limits y leer Allocated resources; decir la QoS class de un pod.
Repartir réplicas con podAntiAffinity o topologySpread por hostname.
Ante un Pending, ir a Events y distinguir constraint vs recursos.
Pin a pod to a node with nodeSelector, from memory.
Write a nodeAffinity required + preferred with operator: In.
Taint, tolerate, and explain NoSchedule vs NoExecute (and what happens to a bare pod).
Set requests/limits and read Allocated resources; state a pod's QoS class.
Spread replicas with podAntiAffinity or topologySpread by hostname.
Given a Pending pod, go to Events and tell constraint from resources.
Forzare un pod su un nodo con nodeSelector, a memoria.
Scrivere un nodeAffinity required + preferred con operator: In.
Taintare, tollerare e spiegare NoSchedule vs NoExecute (e cosa succede a un pod nudo).
Impostare requests/limits e leggere Allocated resources; dire la QoS class di un pod.
Distribuire le repliche con podAntiAffinity o topologySpread per hostname.
Davanti a un Pending, andare su Events e distinguere vincolo da risorse.