Práctica · Medir
Practice · Measuring
Pratica · Misurare
Consumo real (necesita metrics-server)
Real usage (needs metrics-server)
Consumo reale (richiede metrics-server)
k top nodes
k top pods -A --sort-by=memory
k top pod app --containers
# Si dice "Metrics API not available": falta el addon# If it says "Metrics API not available": the addon is missing# Se dice "Metrics API not available": manca l'addon
k get deploy -n kube-system metrics-server
Lo que el nodo tiene comprometido
What the node has committed
Ciò che il nodo ha impegnato
k describe node worker1 | grep -A8 'Allocated resources'
# Ojo: son REQUESTS reservadas, no uso real# Careful: these are reserved REQUESTS, not real usage# Attenzione: sono REQUESTS riservate, non uso reale
k describe node worker1 | grep -A6 Conditions
# MemoryPressure / DiskPressure / PIDPressure = True → evictions
Cuotas del namespace
Namespace quotas
Quote del namespace
k get resourcequota,limitrange -n prod
k describe quota -n prod
# "exceeded quota" al crear → es esto, no el scheduler# "exceeded quota" on create → this, not the scheduler# "exceeded quota" alla creazione → è questo, non lo scheduler
Pods evictados
Evicted pods
Pod evicted
k get pods -A | grep Evicted
k describe pod app | grep -i evict
# The node was low on resource: memory
# Limpiar los cadáveres# Clean up the corpses# Ripulire i cadaveri
k delete pods -A --field-selector status.phase=Failed
La clase de QoS de un pod
A pod's QoS class
La classe QoS di un pod
# Kubernetes la calcula solo: es un campo de STATUS, no de spec# Kubernetes computes it itself: it is a STATUS field, not a spec one# Kubernetes la calcola da solo: è un campo di STATUS, non di spec
k get pod app -o jsonpath='{.status.qosClass}'
k describe pod app | grep -i 'QoS Class'
# Todo el cluster de un vistazo: quién cae primero# The whole cluster at a glance: who falls first# L'intero cluster a colpo d'occhio: chi cade per primo
k get pods -A -o custom-columns=\
NS:.metadata.namespace,NAME:.metadata.name,QOS:.status.qosClass
# Solo los más expuestos a un eviction# Only those most exposed to an eviction# Solo i più esposti a un eviction
k get pods -A -o json | \
jq -r '.items[]|select(.status.qosClass=="BestEffort")|.metadata.name'
Escribir cada clase
Writing each class
Scrivere ogni classe
# Guaranteed: requests == limits en TODOS los containers# Guaranteed: requests == limits in ALL containers# Guaranteed: requests == limits in TUTTI i container
resources:
requests: {cpu: "500m", memory: "256Mi"}
limits: {cpu: "500m", memory: "256Mi"}
# Burstable: hay algo, pero no son iguales# Burstable: something is set, but they are not equal# Burstable: c'è qualcosa, ma non sono uguali
resources:
requests: {memory: "128Mi"}
limits: {memory: "512Mi"}
# BestEffort: bloque resources AUSENTE por completo# BestEffort: the resources block is ENTIRELY ABSENT# BestEffort: il blocco resources è del tutto ASSENTE
# Promover un deploy a Guaranteed, rápido# Promote a deploy to Guaranteed, fast# Promuovere un deploy a Guaranteed, in fretta
k set resources deploy/web \
--requests=cpu=500m,memory=256Mi --limits=cpu=500m,memory=256Mi
⚠
La clase no se declara: se deduce. No existe ningún campo qosClass en el spec que puedas escribir — si un ejercicio pide "que el pod sea Guaranteed", lo que tenés que hacer es igualar requests y limits (CPU y memoria) en todos sus containers, incluidos los initContainers.
The class is not declared: it is deduced. There is no qosClass field in the spec you can write — if an exercise asks for "the pod to be Guaranteed", what you must do is equalise requests and limits (CPU and memory) in all its containers, initContainers included.
La classe non si dichiara: si deduce. Non esiste alcun campo qosClass nella spec che tu possa scrivere — se un esercizio chiede "che il pod sia Guaranteed", quel che devi fare è pareggiare requests e limits (CPU e memoria) in tutti i suoi container, initContainer inclusi.
Teoría · Quién mata
Theory · Who kills
Teoria · Chi uccide
Dos muertes distintas
Two different deaths
Due morti diverse
| OOMKilled | Evicted |
| Quien mata | El kernel | El kubelet |
| Who kills | The kernel | The kubelet |
| Chi uccide | Il kernel | Il kubelet |
| Motivo | Superó su limit | El nodo se quedó sin recursos |
| Reason | It exceeded its limit | The node ran out of resources |
| Motivo | Ha superato il suo limit | Il nodo è rimasto senza risorse |
| Alcance | Un container | Pods enteros, por prioridad |
| Scope | One container | Whole pods, by priority |
| Portata | Un container | Pod interi, per priorità |
| Se ve en | Last State del container | Status del pod + eventos |
| Seen in | The container's Last State | The pod's Status + events |
| Si vede in | Il Last State del container | Lo Status del pod + eventi |
QoS: la clase que decide quién muere
QoS: the class that decides who dies
QoS: la classe che decide chi muore
Cuando un nodo se queda sin recursos, el kubelet tiene que matar pods para salvarse. No elige al azar ni por antigüedad: elige por clase de QoS, una etiqueta que Kubernetes le calcula a cada pod a partir de sus requests y limits (M5). Vos nunca la escribís; la deduce el sistema y la publica en status.qosClass.
When a node runs out of resources, the kubelet must kill pods to save itself. It does not choose at random or by age: it chooses by QoS class, a label Kubernetes computes for each pod from its requests and limits (M5). You never write it; the system deduces it and publishes it in status.qosClass.
Quando un nodo resta senza risorse, il kubelet deve uccidere pod per salvarsi. Non sceglie a caso né per anzianità: sceglie per classe QoS, un'etichetta che Kubernetes calcola per ogni pod a partire dalle sue requests e limits (M5). Tu non la scrivi mai; il sistema la deduce e la pubblica in status.qosClass.
Cómo se asigna, en tres reglas
How it is assigned, in three rules
Come si assegna, in tre regole
| Clase | Condición | Se desaloja |
| Class | Condition | Evicted |
| Classe | Condizione | Espulso |
| Guaranteed | requests = limits, CPU y memoria, en todos los containers | último |
| Guaranteed | requests = limits, CPU and memory, in all containers | last |
| Guaranteed | requests = limits, CPU e memoria, in tutti i container | ultimo |
| Burstable | Tiene algún request o limit, pero no cumple lo anterior | segundo |
| Burstable | It has some request or limit, but does not meet the above | second |
| Burstable | Ha qualche request o limit, ma non soddisfa quanto sopra | secondo |
| BestEffort | Ningún container declara requests ni limits | primero |
| BestEffort | No container declares requests or limits | first |
| BestEffort | Nessun container dichiara requests o limits | primo |
Se evalúa a nivel pod, no de container: un solo container sin resources arrastra a todo el pod a Burstable, y un solo campo desigual lo baja de Guaranteed a Burstable.
It is evaluated at the pod level, not the container level: a single container without resources drags the whole pod to Burstable, and a single unequal field drops it from Guaranteed to Burstable.
Si valuta a livello di pod, non di container: un solo container senza resources trascina l'intero pod a Burstable, e un solo campo diseguale lo fa scendere da Guaranteed a Burstable.
El orden del sacrificio
The order of sacrifice
L'ordine del sacrificio
Bajo presión el kubelet desaloja BestEffort → Burstable → Guaranteed. Dentro de Burstable hay un desempate que vale la pena saber: caen antes los pods que están consumiendo más por encima de su request. O sea que el request no es solo una reserva para el scheduler: también es la línea que define cuánto te "merecés" quedarte.
Under pressure the kubelet evicts BestEffort → Burstable → Guaranteed. Within Burstable there is a tiebreaker worth knowing: pods consuming furthest above their request fall first. So the request is not just a reservation for the scheduler: it is also the line defining how much you "deserve" to stay.
Sotto pressione il kubelet espelle BestEffort → Burstable → Guaranteed. Dentro Burstable c'è uno spareggio che vale la pena sapere: cadono prima i pod che consumano più sopra la loro request. Quindi la request non è solo una prenotazione per lo scheduler: è anche la linea che definisce quanto "meriti" di restare.
Por eso poner requests no es burocracia. Un pod BestEffort corre igual de bien que cualquier otro… hasta el primer apretón de memoria, y ahí es el primero en desaparecer.
That is why setting requests is not bureaucracy. A BestEffort pod runs just as well as any other… until the first memory squeeze, and then it is the first to vanish.
Per questo mettere le requests non è burocrazia. Un pod BestEffort gira bene come qualsiasi altro… fino alla prima stretta di memoria, e lì è il primo a sparire.
QoS y OOMKilled no son lo mismo
QoS and OOMKilled are not the same
QoS e OOMKilled non sono la stessa cosa
El eviction por QoS lo decide el kubelet, mirando el nodo entero, y se lleva pods completos. El OOMKilled lo decide el kernel cuando un container pasa su propio limit de memoria, sin importar qué clase tenga: un pod Guaranteed que se excede muere igual. Distinta causa, distinto ejecutor, distinto síntoma.
A QoS eviction is decided by the kubelet, looking at the whole node, and it takes entire pods. OOMKilled is decided by the kernel when a container passes its own memory limit, regardless of class: a Guaranteed pod that overshoots dies just the same. Different cause, different executioner, different symptom.
L'eviction per QoS la decide il kubelet, guardando l'intero nodo, e si porta via pod interi. L'OOMKilled lo decide il kernel quando un container supera il proprio limit di memoria, indipendentemente dalla classe: un pod Guaranteed che eccede muore lo stesso. Causa diversa, esecutore diverso, sintomo diverso.
metrics-server no viene incluido
metrics-server is not included
metrics-server non è incluso
kubectl top consulta la Metrics API, que sirve un addon aparte. Si no está instalado, el comando falla — y eso no significa que el cluster esté roto. Sin él te quedan los describe, que muestran requests/limits pero no consumo real.
kubectl top queries the Metrics API, served by a separate addon. If it is not installed, the command fails — and that does not mean the cluster is broken. Without it you still have describe, which shows requests/limits but not real usage.
kubectl top interroga la Metrics API, servita da un addon a parte. Se non è installato, il comando fallisce — e non significa che il cluster sia rotto. Senza di esso ti restano i describe, che mostrano requests/limits ma non il consumo reale.
★
La confusión más cara del dominio: «Allocated resources» del nodo son requests reservadas, no uso. Un nodo puede estar al 90% "asignado" y al 10% de uso real, y aun así rechazar pods nuevos. Si un pod queda Pending por CPU pero top nodes muestra el nodo ocioso, ya sabés por qué.
The costliest confusion in this domain: a node's "Allocated resources" are reserved requests, not usage. A node can be 90% "allocated" and at 10% real usage, and still refuse new pods. If a pod stays Pending on CPU while top nodes shows the node idle, now you know why.
La confusione più costosa del dominio: le "Allocated resources" di un nodo sono requests riservate, non uso. Un nodo può essere al 90% "allocato" e al 10% di uso reale, e rifiutare comunque pod nuovi. Se un pod resta Pending per la CPU mentre top nodes mostra il nodo ozioso, ora sai perché.