CKA · Módulo 9CKA · Module 9CKA · Modulo 9 K8s v1.34 Troubleshooting 30%

Troubleshooting de apps: leer el cluster en vez de adivinar

App troubleshooting: reading the cluster instead of guessing

Troubleshooting delle app: leggere il cluster invece di indovinare

El dominio más pesado del examen no se estudia de memoria: se estudia como método. Un pod roto siempre te dice qué le pasa — en su status, en sus eventos o en sus logs. Este módulo es el orden en que se mira.

The heaviest domain in the exam is not learnt by heart: it is learnt as a method. A broken pod always tells you what is wrong — in its status, its events or its logs. This module is the order in which you look.

Il dominio più pesante dell'esame non si studia a memoria: si studia come metodo. Un pod rotto ti dice sempre cosa ha — nel suo status, nei suoi eventi o nei suoi log. Questo modulo è l'ordine in cui si guarda.

Duración90 min
Duration90 min
Durata90 min
DominioTroubleshooting
DomainTroubleshooting
DominioTroubleshooting
Peso30% (1 de 3)
Weight30% (1 of 3)
Peso30% (1 di 3)
RequiereM4 · M5 · M6
RequiresM4 · M5 · M6
RichiedeM4 · M5 · M6
Treinta por ciento del examen — el bloque más grande, y el único donde no alcanza con saber escribir YAML. Acá te van a dar algo ya roto y dos minutos para arreglarlo. La diferencia entre aprobar y no aprobar no es conocer más causas: es tener una secuencia fija que ejecutás sin pensar, que va acorralando el problema hasta que solo queda una explicación. Este módulo cubre la mitad de aplicaciones (pods, containers, probes); el M10 hace lo mismo con el control plane y el M11 con la red.
Thirty per cent of the exam — the biggest block, and the only one where writing YAML is not enough. Here you are handed something already broken and two minutes to fix it. The difference between passing and failing is not knowing more causes: it is having a fixed sequence you run without thinking, that corners the problem until only one explanation is left. This module covers the application half (pods, containers, probes); M10 does the same for the control plane and M11 for the network.
Trenta per cento dell'esame — il blocco più grande, e l'unico dove saper scrivere YAML non basta. Qui ti danno qualcosa già rotto e due minuti per sistemarlo. La differenza fra passare e non passare non è conoscere più cause: è avere una sequenza fissa che esegui senza pensare, che accerchia il problema finché non resta una sola spiegazione. Questo modulo copre la metà applicativa (pod, container, probe); il M10 fa lo stesso col control plane e il M11 con la rete.
Modelo mental del módulo
Module mental model
Modello mentale del modulo
¿Arrancó? → EVENTS  ·  ¿Arrancó y murió? → LOGS
Did it start? → EVENTS  ·  Did it start and die? → LOGS
È partito? → EVENTS  ·  È partito ed è morto? → LOGS
Toda la disciplina cabe en esa bifurcación. Si el pod nunca llegó a correr (Pending, ImagePullBackOff, ContainerCreating), el problema es de Kubernetes y está en los eventos del describe. Si el container arrancó y después se cayó (CrashLoopBackOff, Error, OOMKilled), el problema es de la app y está en los logs — casi siempre en --previous.
The whole discipline fits in that fork. If the pod never ran (Pending, ImagePullBackOff, ContainerCreating), the problem is Kubernetes' and it is in the describe events. If the container started and then fell over (CrashLoopBackOff, Error, OOMKilled), the problem is the app's and it is in the logs — almost always in --previous.
Tutta la disciplina sta in quel bivio. Se il pod non è mai partito (Pending, ImagePullBackOff, ContainerCreating), il problema è di Kubernetes ed è negli eventi del describe. Se il container è partito e poi è caduto (CrashLoopBackOff, Error, OOMKilled), il problema è dell'app ed è nei log — quasi sempre in --previous.
9.1
El método
The method
Il metodo
describe, logs, events: qué mirar y en qué orden.
describe, logs, events: what to look at and in what order.
describe, logs, events: cosa guardare e in che ordine.
9.2
Estados de pod
Pod states
Stati dei pod
Pending, ImagePullBackOff, CrashLoopBackOff, OOMKilled.
9.3
Probes mal configuradas
Misconfigured probes
Probe mal configurate
liveness que mata, readiness que aisla, startup que salva.
liveness that kills, readiness that isolates, startup that saves.
liveness che uccide, readiness che isola, startup che salva.
9.4
kubectl debug
Ephemeral containers, copias del pod, debug de nodo.
Ephemeral containers, pod copies, node debugging.
Ephemeral container, copie del pod, debug del nodo.
9.5
Recursos del cluster
Cluster resources
Risorse del cluster
top, limits, quotas, clases de QoS — y quién mató a quién.
top, limits, quotas, QoS classes — and who killed whom.
top, limit, quota, classi QoS — e chi ha ucciso chi.
★
Práctica · 8 ejercicios
Practice · 8 exercises
Pratica · 8 esercizi
Ocho pods rotos, uno por síntoma. Cronómetro: 3 min cada uno.
Eight broken pods, one per symptom. Clock: 3 min each.
Otto pod rotti, uno per sintomo. Cronometro: 3 min ciascuno.
9.1
La secuencia · 20 min
The sequence · 20 min
La sequenza · 20 min

describe, logs, events

Cuatro comandos, siempre en el mismo orden.
Four commands, always in the same order.
Quattro comandi, sempre nello stesso ordine.
Práctica · La rutina
Practice · The routine
Pratica · La routine

Paso 0 — el panorama

Step 0 — the overview

Passo 0 — il quadro generale

k get pods -A -o wide
# Mirá TRES columnas: STATUS, READY (2/3!) y RESTARTS# Look at THREE columns: STATUS, READY (2/3!) and RESTARTS# Guarda TRE colonne: STATUS, READY (2/3!) e RESTARTS

# Solo lo que no está sano# Only what is not healthy# Solo ciò che non è sano
k get pods -A --field-selector status.phase!=Running

Paso 1 — describe (el 60% de los casos muere acá)

Step 1 — describe (60% of cases end here)

Passo 1 — describe (il 60% dei casi finisce qui)

k describe pod app

# Leé de abajo hacia arriba:# Read from the bottom up:# Leggi dal basso verso l'alto:
#   Events         ← EMPEZÁ ACÁ, siempre#   Events         ← START HERE, always#   Events         ← INIZIA QUI, sempre
#   State / Last State + Reason + Exit Code
#   Ready, Restart Count
#   Mounts, Node, Conditions

Paso 2 — logs

Step 2 — logs

Passo 2 — logs

k logs app
k logs app --previous          # EL comando del CrashLoop# THE CrashLoop command# IL comando del CrashLoop
k logs app -c sidecar          # pod multi-container# multi-container pod# pod multi-container
k logs app --all-containers --prefix
k logs app -f --tail=50
k logs app --since=10m
k logs -l app=web --tail=20    # por label, varios pods# by label, several pods# per label, più pod

Paso 3 — events del namespace

Step 3 — namespace events

Passo 3 — eventi del namespace

k get events --sort-by=.metadata.creationTimestamp
k get events -w                       # en vivo# live# dal vivo
k get events --field-selector type=Warning
k events --for pod/app --watch             # v1.34

Paso 4 — entrar, si sigue vivo

Step 4 — go in, if it is still alive

Passo 4 — entrare, se è ancora vivo

k exec -it app -- sh
k exec app -- env | sort
k exec app -- cat /etc/config/app.conf
k get pod app -o yaml | less     # la verdad completa# the complete truth# la verità completa
★
Los eventos expiran a la hora por defecto. En un pod que lleva rato roto el describe puede mostrar <none> en Events, y eso no significa que esté sano: significa que la evidencia se venció. Ahí te apoyás en Last State y en logs --previous.
Events expire after an hour by default. On a pod that has been broken a while, describe may show <none> under Events, and that does not mean it is healthy: it means the evidence expired. There you fall back on Last State and logs --previous.
Gli eventi scadono dopo un'ora per default. Su un pod rotto da un po', describe può mostrare <none> in Events, e non significa che sia sano: significa che la prova è scaduta. Lì ti appoggi su Last State e logs --previous.
Teoría · Cómo leer
Theory · How to read
Teoria · Come leggere

La bifurcación de entrada

The entry fork

Il bivio d'ingresso

Si el pod…Mirá
If the pod…Look at
Se il pod…Guarda
nunca arrancódescribe → Events
never starteddescribe → Events
non è mai partitodescribe → Events
arrancó y muriólogs --previous
started and diedlogs --previous
è partito ed è mortologs --previous
corre pero no respondelogs + probes + Service
runs but does not answerlogs + probes + Service
gira ma non rispondelogs + probe + Service
no tiene shell ni logskubectl debug (9.4)
has no shell and no logskubectl debug (9.4)
non ha shell né logkubectl debug (9.4)

READY no es STATUS

READY is not STATUS

READY non è STATUS

Un pod puede estar Running y 1/2: está corriendo, pero un container no pasa su readinessProbe. Para el Service, ese pod no existe (M6: no entra al EndpointSlice). Es el caso que más se confunde con "problema de red" sin serlo.

A pod can be Running and 1/2: it is running, but one container fails its readinessProbe. To the Service, that pod does not exist (M6: it never enters the EndpointSlice). It is the case most often mistaken for a "network problem" without being one.

Un pod può essere Running e 1/2: gira, ma un container non supera la sua readinessProbe. Per il Service, quel pod non esiste (M6: non entra nell'EndpointSlice). È il caso più spesso scambiato per un "problema di rete" senza esserlo.

Exit codes que hay que reconocer

Exit codes you must recognise

Exit code da riconoscere

CódigoSignifica
CodeMeans
CodiceSignifica
0Terminó bien (¿debería?)
0Finished cleanly (should it?)
0Terminato bene (doveva?)
1Error genérico de la app → logs
1Generic app error → logs
1Errore generico dell'app → log
125 / 126 / 127Problema con el comando o la imagen
125 / 126 / 127Problem with the command or the image
125 / 126 / 127Problema col comando o l'immagine
137SIGKILL → OOMKilled o liveness
137SIGKILL → OOMKilled or liveness
137SIGKILL → OOMKilled o liveness
143SIGTERM → terminación ordenada
143SIGTERM → graceful termination
143SIGTERM → terminazione ordinata

De dónde salen los logs

Where logs come from

Da dove vengono i log

kubectl logs devuelve el stdout/stderr del proceso principal del container, tal como lo capturó el runtime. Si tu app escribe a un archivo dentro del container, ahí no aparece nada — y esa ausencia de logs es en sí misma un dato.

kubectl logs returns the container's main process stdout/stderr, exactly as the runtime captured it. If your app writes to a file inside the container, nothing shows up there — and that absence of logs is itself a data point.

kubectl logs restituisce lo stdout/stderr del processo principale del container, così come l'ha catturato il runtime. Se la tua app scrive su un file dentro il container, lì non compare nulla — e quell'assenza di log è essa stessa un dato.

★
Bajo presión se salta pasos y se empieza a probar cosas al azar. La disciplina que cambia el resultado es esta: describe siempre primero, y leé los Events de abajo hacia arriba. En el examen no hay tiempo para adivinar dos veces.
Under pressure you skip steps and start trying things at random. The discipline that changes the outcome is this: describe first, always, and read the Events bottom-up. In the exam there is no time to guess twice.
Sotto pressione si saltano passi e si iniziano a provare cose a caso. La disciplina che cambia il risultato è questa: describe sempre per primo, e leggi gli Events dal basso verso l'alto. All'esame non c'è tempo per indovinare due volte.
9.2
Catálogo · 20 min
Catalogue · 20 min
Catalogo · 20 min

Los estados de pod y qué significan

Pod states and what they mean

Gli stati dei pod e cosa significano

Cada nombre feo del STATUS apunta a un lugar concreto.
Every ugly STATUS name points at one concrete place.
Ogni brutto nome nello STATUS punta a un posto preciso.
Práctica · Caso por caso
Practice · Case by case
Pratica · Caso per caso

Pending — nadie lo agarró

Pending — nobody took it

Pending — nessuno l'ha preso

k describe pod app | tail -15
# 0/3 nodes are available: 3 Insufficient cpu    → requests (M5)
# ... had untolerated taint {k=v}                → taints (M5)
# ... didn't match node selector                 → nodeSelector (M5)
# unbound immediate PersistentVolumeClaims       → storage (M8)

k get nodes
k describe node worker1 | grep -A6 'Allocated resources'

ImagePullBackOff / ErrImagePull

k describe pod app | grep -A3 Events
# Failed to pull image "ngnix:latest": not found  → TYPO# Failed to pull image "ngnix:latest": not found  → TYPO# Failed to pull image "ngnix:latest": not found  → REFUSO
# pull access denied / unauthorized               → falta imagePullSecret# pull access denied / unauthorized               → missing imagePullSecret# pull access denied / unauthorized               → manca imagePullSecret

# Ver la imagen tal como quedó escrita# See the image exactly as it was written# Vedere l'immagine così com'è stata scritta
k get pod app -o jsonpath='{.spec.containers[*].image}'

# Arreglar sin editar el YAML entero# Fix without editing the whole YAML# Sistemare senza modificare l'intero YAML
k set image deploy/web web=nginx:1.27

CrashLoopBackOff — arranca y se muere

CrashLoopBackOff — it starts and dies

CrashLoopBackOff — parte e muore

# LO PRIMERO, siempre:# FIRST THING, always:# PRIMA COSA, sempre:
k logs app --previous

k describe pod app | grep -A6 'Last State'
# Reason: Error       Exit Code: 1     → la app falló# Reason: Error       Exit Code: 1     → the app failed# Reason: Error       Exit Code: 1     → l'app è fallita
# Reason: OOMKilled   Exit Code: 137   → memoria# Reason: OOMKilled   Exit Code: 137   → memory# Reason: OOMKilled   Exit Code: 137   → memoria
# Reason: Completed   Exit Code: 0     → el comando terminó# Reason: Completed   Exit Code: 0     → the command finished# Reason: Completed   Exit Code: 0     → il comando è finito

# Truco: mantenerlo vivo para poder entrar# Trick: keep it alive so you can get in# Trucco: tenerlo vivo per poter entrare
k run tmp --image=mi-img --command -- sleep 3600

OOMKilled — se pasó del limit

OOMKilled — it went over its limit

OOMKilled — ha superato il limit

k describe pod app | grep -B2 -A6 'Last State'
k top pod app --containers

# Subir el limit (M5)# Raise the limit (M5)# Alzare il limit (M5)
k set resources deploy/web --limits=memory=512Mi

CreateContainerConfigError

k describe pod app | grep -A3 Events
# configmap "app-config" not found
# secret "db-pass" not found
# → El objeto referenciado NO EXISTE. Verificá nombre Y namespace.# → The referenced object DOES NOT EXIST. Check the name AND the namespace.# → L'oggetto referenziato NON ESISTE. Controlla nome E namespace.
k get cm,secret
⚠
CrashLoopBackOff no es una causa, es una consecuencia: es el kubelet esperando (10s, 20s, 40s… hasta 5 min) antes de reintentar. La causa real está en logs --previous y en el Last State. Nunca reportes "el pod está en CrashLoop" como diagnóstico — es el síntoma.
CrashLoopBackOff is not a cause, it is a consequence: it is the kubelet waiting (10s, 20s, 40s… up to 5 min) before retrying. The real cause is in logs --previous and in Last State. Never report "the pod is in CrashLoop" as a diagnosis — it is the symptom.
CrashLoopBackOff non è una causa, è una conseguenza: è il kubelet che aspetta (10s, 20s, 40s… fino a 5 min) prima di riprovare. La causa vera è in logs --previous e nel Last State. Non riportare mai "il pod è in CrashLoop" come diagnosi — è il sintomo.
Teoría · El mapa
Theory · The map
Teoria · La mappa

Tabla de decisión

Decision table

Tabella decisionale

STATUSEtapaMirá
STATUSStageLook at
STATUSFaseGuarda
PendingSchedulingEvents del pod + nodos
PendingSchedulingPod events + nodes
PendingSchedulingEventi del pod + nodi
ContainerCreatingPreparaciónVolumes, secrets, CNI
ContainerCreatingPreparationVolumes, secrets, CNI
ContainerCreatingPreparazioneVolume, secret, CNI
ImagePullBackOffImagenNombre, tag, registry, secret
ImagePullBackOffImageName, tag, registry, secret
ImagePullBackOffImmagineNome, tag, registry, secret
CrashLoopBackOffEjecuciónlogs --previous
CrashLoopBackOffExecutionlogs --previous
CrashLoopBackOffEsecuzionelogs --previous
ErrorEjecuciónExit code + logs
ErrorExecutionExit code + logs
ErrorEsecuzioneExit code + log
OOMKilledEjecuciónLimits de memoria
OOMKilledExecutionMemory limits
OOMKilledEsecuzioneLimit di memoria
Terminating (colgado)BorradoFinalizers, nodo caído
Terminating (stuck)DeletionFinalizers, dead node
Terminating (bloccato)CancellazioneFinalizer, nodo caduto
Running pero 0/1ReadinessLa probe (9.3)
Running but 0/1ReadinessThe probe (9.3)
Running ma 0/1ReadinessLa probe (9.3)

Las fases oficiales

The official phases

Le fasi ufficiali

Solo hay cinco: Pending, Running, Succeeded, Failed, Unknown. Todo lo demás que ves en la columna STATUS — CrashLoopBackOff, ImagePullBackOff, Init:0/2 — sale del estado de los containers, no de la fase. Saberlo evita buscar "la fase CrashLoopBackOff" en la doc.

There are only five: Pending, Running, Succeeded, Failed, Unknown. Everything else you see in the STATUS column — CrashLoopBackOff, ImagePullBackOff, Init:0/2 — comes from the containers' state, not the phase. Knowing that saves you searching the docs for "the CrashLoopBackOff phase".

Ce ne sono solo cinque: Pending, Running, Succeeded, Failed, Unknown. Tutto il resto che vedi nella colonna STATUS — CrashLoopBackOff, ImagePullBackOff, Init:0/2 — viene dallo stato dei container, non dalla fase. Saperlo evita di cercare "la fase CrashLoopBackOff" nella documentazione.

Init containers

Init:0/2 o Init:Error significa que el pod ni empezó: los init containers corren en orden y hasta completarse antes que los principales. Se depuran igual, apuntando al container: k logs app -c init-db.

Init:0/2 or Init:Error means the pod has not even begun: init containers run in order and to completion before the main ones. You debug them the same way, pointing at the container: k logs app -c init-db.

Init:0/2 o Init:Error significa che il pod non è nemmeno iniziato: gli init container girano in ordine e fino al completamento prima di quelli principali. Si debuggano allo stesso modo, puntando al container: k logs app -c init-db.

Terminating eterno

Eternal Terminating

Terminating eterno

Dos causas típicas: un finalizer que nadie resuelve, o el nodo desapareció y el kubelet no puede confirmar el borrado. El --force --grace-period=0 existe, pero borra el objeto de la API sin garantizar que el proceso murió: último recurso.

Two typical causes: a finalizer nobody resolves, or the node vanished and the kubelet cannot confirm the deletion. --force --grace-period=0 exists, but it removes the object from the API without guaranteeing the process died: last resort.

Due cause tipiche: un finalizer che nessuno risolve, o il nodo è sparito e il kubelet non può confermare la cancellazione. --force --grace-period=0 esiste, ma toglie l'oggetto dall'API senza garantire che il processo sia morto: ultima risorsa.

★
Regla de oro para ubicarte rápido: Pending = problema del cluster (no hay dónde ponerlo). CrashLoop = problema del container (hay dónde, pero se muere). Esa sola distinción te dice si mirar nodos o mirar logs.
Golden rule to orient fast: Pending = a cluster problem (nowhere to put it). CrashLoop = a container problem (there is somewhere, but it dies). That single distinction tells you whether to look at nodes or at logs.
Regola d'oro per orientarti in fretta: Pending = problema del cluster (non c'è dove metterlo). CrashLoop = problema del container (c'è dove, ma muore). Quella sola distinzione ti dice se guardare i nodi o i log.
9.3
Salud · 15 min
Health · 15 min
Salute · 15 min

Probes mal configuradas

Misconfigured probes

Probe mal configurate

La causa más elegante de un pod que "no anda" estando sano.
The most elegant cause of a pod that "does not work" while being healthy.
La causa più elegante di un pod che "non va" pur essendo sano.
Práctica · Leer y corregir
Practice · Read and fix
Pratica · Leggere e correggere

Las tres probes

The three probes

Le tre probe

containers:
- name: web
  image: nginx
  livenessProbe:            # ¿sigue vivo? si no → REINICIA# still alive? if not → RESTART# ancora vivo? se no → RIAVVIA
    httpGet: {path: /healthz, port: 8080}
    initialDelaySeconds: 15
    periodSeconds: 10
    failureThreshold: 3
  readinessProbe:           # ¿puede recibir tráfico? si no → SACA del Service# can it take traffic? if not → REMOVE from the Service# può ricevere traffico? se no → TOGLIE dal Service
    httpGet: {path: /ready, port: 8080}
    periodSeconds: 5
  startupProbe:             # protege el arranque lento# protects a slow start-up# protegge l'avvio lento
    httpGet: {path: /healthz, port: 8080}
    failureThreshold: 30
    periodSeconds: 10       # hasta 300s para arrancar# up to 300s to start# fino a 300s per partire

Los otros dos tipos de chequeo

The other two check types

Gli altri due tipi di controllo

  livenessProbe:
    exec:
      command: ["cat", "/tmp/healthy"]      # exit 0 = OK

  readinessProbe:
    tcpSocket: {port: 3306}            # ¿abre el puerto?# is the port open?# la porta è aperta?

Diagnosticar

Diagnosing

Diagnosticare

k describe pod app | grep -A3 -i probe
# Liveness probe failed: HTTP probe failed with statuscode: 404
# Readiness probe failed: dial tcp 10.244.1.5:8080: connect: refused

# RESTARTS que sube sin logs de error → liveness matando al pod# RESTARTS climbing with no error logs → liveness killing the pod# RESTARTS che sale senza log di errore → la liveness uccide il pod
k get pod app -o jsonpath='{.spec.containers[0].livenessProbe}'

# Probar el endpoint a mano DESDE el pod# Test the endpoint by hand FROM inside the pod# Provare l'endpoint a mano DA dentro il pod
k exec app -- curl -sv localhost:8080/healthz

Quitar una probe rota, rápido

Removing a broken probe, fast

Togliere una probe rotta, in fretta

k edit deploy web      # borrar el bloque livenessProbe# delete the livenessProbe block# cancella il blocco livenessProbe
# o corregir el puerto/path y dejar que ruede el rollout# or fix the port/path and let the rollout run# o correggi porta/path e lascia scorrere il rollout
k rollout status deploy/web
⚠
Síntoma inconfundible: RESTARTS crece pero los logs no muestran ningún error. La app está perfecta; es la livenessProbe la que la está matando. Casi siempre por un initialDelaySeconds demasiado corto para el arranque real.
Unmistakable symptom: RESTARTS grows but the logs show no error at all. The app is fine; it is the livenessProbe killing it. Nearly always because of an initialDelaySeconds too short for the real start-up.
Sintomo inconfondibile: RESTARTS cresce ma i log non mostrano alcun errore. L'app è perfetta; è la livenessProbe che la uccide. Quasi sempre per un initialDelaySeconds troppo corto per l'avvio reale.
Teoría · Quién hace qué
Theory · Who does what
Teoria · Chi fa cosa

Consecuencias distintas

Different consequences

Conseguenze diverse

ProbeSi fallaSíntoma
ProbeOn failureSymptom
ProbeSe fallisceSintomo
livenessReinicia el containerRESTARTS sube
livenessRestarts the containerRESTARTS climbs
livenessRiavvia il containerRESTARTS sale
readinessLo saca del ServiceREADY 0/1
readinessRemoves it from the ServiceREADY 0/1
readinessLo toglie dal ServiceREADY 0/1
startupReinicia el containerNunca llega a Ready
startupRestarts the containerNever reaches Ready
startupRiavvia il containerNon arriva mai a Ready

Los parámetros que importan

The parameters that matter

I parametri che contano

  • initialDelaySeconds — cuánto espera antes del primer chequeo (default 0).
  • periodSeconds — cada cuánto reintenta (default 10).
  • failureThreshold — cuántos fallos seguidos hacen falta (default 3).
  • timeoutSeconds — cuánto espera cada chequeo (default 1, cortito).
  • initialDelaySeconds — how long it waits before the first check (default 0).
  • periodSeconds — how often it retries (default 10).
  • failureThreshold — how many consecutive failures are needed (default 3).
  • timeoutSeconds — how long each check waits (default 1, very short).
  • initialDelaySeconds — quanto aspetta prima del primo controllo (default 0).
  • periodSeconds — ogni quanto riprova (default 10).
  • failureThreshold — quanti fallimenti consecutivi servono (default 3).
  • timeoutSeconds — quanto aspetta ogni controllo (default 1, cortissimo).

El primer chequeo sale a los initialDelaySeconds y de ahí en más cada periodSeconds. Como hace falta que fallen failureThreshold chequeos seguidos, el container se reinicia en el chequeo número failureThreshold, o sea alrededor de initialDelay + (failureThreshold − 1) × periodSeconds. Con esa cuenta explicás casi cualquier reinicio raro — y un solo éxito resetea el contador de fallos.

The first check fires at initialDelaySeconds and every periodSeconds thereafter. Since failureThreshold consecutive checks must fail, the container restarts on check number failureThreshold, i.e. around initialDelay + (failureThreshold − 1) × periodSeconds. That arithmetic explains almost any odd restart — and a single success resets the failure counter.

Il primo controllo parte a initialDelaySeconds e poi ogni periodSeconds. Poiché devono fallire failureThreshold controlli consecutivi, il container si riavvia al controllo numero failureThreshold, cioè intorno a initialDelay + (failureThreshold − 1) × periodSeconds. Con quel conto spieghi quasi ogni riavvio strano — e un solo successo azzera il contatore dei fallimenti.

startupProbe: para qué se inventó

startupProbe: why it was invented

startupProbe: perché è stata inventata

Apps que tardan mucho en levantar forzaban un initialDelaySeconds enorme en liveness — y entonces, ya en régimen, un cuelgue tardaba muchísimo en detectarse. La startupProbe separa los dos momentos: mientras corre, liveness y readiness quedan suspendidas; cuando pasa una vez, no vuelve a ejecutarse nunca y las otras dos entran con sus tiempos agresivos.

Apps that take long to come up forced a huge initialDelaySeconds on liveness — and then, in steady state, a hang took forever to be detected. The startupProbe separates the two moments: while it runs, liveness and readiness are suspended; once it passes a single time it never runs again and the other two kick in with their aggressive timings intact.

Le app lente ad avviarsi imponevano un initialDelaySeconds enorme sulla liveness — e poi, a regime, un blocco ci metteva un'eternità a essere rilevato. La startupProbe separa i due momenti: mentre gira, liveness e readiness restano sospese; quando passa una volta sola non viene più eseguita e le altre due entrano coi loro tempi aggressivi intatti.

Los cuatro errores clásicos

The four classic mistakes

I quattro errori classici

  • Puerto de la probe ≠ puerto en el que escucha la app.
  • Path que devuelve 404 (cualquier código fuera de 2xx/3xx es fallo).
  • initialDelaySeconds corto → loop de reinicios en el arranque.
  • Liveness que consulta una dependencia externa: si se cae la base, te reinicia toda la flota sin motivo.
  • Probe port ≠ the port the app listens on.
  • A path returning 404 (any code outside 2xx/3xx is a failure).
  • A short initialDelaySeconds → a restart loop at start-up.
  • A liveness probe that checks an external dependency: if the database goes down, it restarts your whole fleet for nothing.
  • Porta della probe ≠ porta su cui ascolta l'app.
  • Un path che restituisce 404 (qualsiasi codice fuori da 2xx/3xx è un fallimento).
  • initialDelaySeconds corto → loop di riavvii all'avvio.
  • Una liveness che interroga una dipendenza esterna: se cade il database, ti riavvia l'intera flotta per nulla.
★
Conexión con el M6 que aparece todo el tiempo: readiness rota = Service sin endpoints. Si un Service "no responde" y sus pods están Running 0/1, no toques la red: el problema es la probe.
The link back to M6 that keeps showing up: a broken readiness = a Service with no endpoints. If a Service "does not respond" and its pods are Running 0/1, do not touch the network: the problem is the probe.
Il collegamento al M6 che ricorre di continuo: readiness rotta = Service senza endpoint. Se un Service "non risponde" e i suoi pod sono Running 0/1, non toccare la rete: il problema è la probe.
VisorExplorerVisore · ¿la liveness te mata la app antes de que arranque?does liveness kill your app before it starts?la liveness uccide l'app prima che parta?
9.4
Herramienta · 20 min
Tooling · 20 min
Strumento · 20 min

kubectl debug y ephemeral containersand ephemeral containerse gli ephemeral container

Meter herramientas donde no hay ni shell.
Getting tools into a place with not even a shell.
Portare strumenti dove non c'è nemmeno una shell.
Práctica · Los tres modos
Practice · The three modes
Pratica · Le tre modalità

1. Inyectar un container en un pod vivo

1. Inject a container into a live pod

1. Iniettare un container in un pod vivo

# Se suma al pod EXISTENTE, comparte su red y sus namespaces# It joins the EXISTING pod, sharing its network and namespaces# Si aggiunge al pod ESISTENTE, condividendone rete e namespace
k debug -it app --image=busybox --target=web

# Adentro: la red del pod es la tuya# Inside: the pod's network is yours# Dentro: la rete del pod è la tua
wget -qO- localhost:8080/healthz
nslookup mi-service
netstat -tlnp

# --target=CONTAINER: ver además sus procesos# --target=CONTAINER: also see its processes# --target=CONTAINER: vedere anche i suoi processi
ps aux

2. Copia del pod (para el que crashea)

2. A copy of the pod (for the crashing one)

2. Copia del pod (per quello che crasha)

# Copia con un container extra, sin tocar el original# A copy with an extra container, leaving the original alone# Una copia con un container extra, senza toccare l'originale
k debug app -it --image=busybox --copy-to=app-dbg

# Copia CAMBIANDO el comando: evita el CrashLoop# A copy CHANGING the command: dodges the CrashLoop# Una copia CAMBIANDO il comando: evita il CrashLoop
k debug app -it --copy-to=app-dbg --container=web -- sh

# Copia compartiendo el espacio de procesos# A copy sharing the process namespace# Una copia che condivide lo spazio dei processi
k debug app -it --copy-to=app-dbg --share-processes --image=busybox

k delete pod app-dbg        # limpiar SIEMPRE al terminar# ALWAYS clean up when done# pulire SEMPRE alla fine

3. Debug de un nodo

3. Debugging a node

3. Debug di un nodo

# Pod privilegiado en el nodo, con su FS en /host# A privileged pod on the node, with its FS at /host# Un pod privilegiato sul nodo, col suo FS in /host
k debug node/worker1 -it --image=busybox

chroot /host
journalctl -u kubelet -n 50
crictl ps -a
ls /etc/kubernetes/manifests/

Ver los ephemeral containers

Seeing the ephemeral containers

Vedere gli ephemeral container

k get pod app -o jsonpath='{.spec.ephemeralContainers[*].name}'
k describe pod app | grep -A5 'Ephemeral Containers'
★
Imágenes que conviene tener en la cabeza: busybox (chiquita, tiene wget/nslookup/netstat), nicolaka/netshoot (la navaja suiza de red) y alpine. En el examen no hay internet libre: si el enunciado te sugiere una imagen, usá esa.
Images worth keeping in your head: busybox (tiny, has wget/nslookup/netstat), nicolaka/netshoot (the network Swiss army knife) and alpine. In the exam there is no open internet: if the wording suggests an image, use that one.
Immagini da tenere a mente: busybox (piccolissima, ha wget/nslookup/netstat), nicolaka/netshoot (il coltellino svizzero della rete) e alpine. All'esame non c'è internet libero: se il testo ti suggerisce un'immagine, usa quella.
Teoría · Cómo funciona
Theory · How it works
Teoria · Come funziona

El problema que resuelve

The problem it solves

Il problema che risolve

Las imágenes modernas son distroless: no traen shell, ni curl, ni ps. Con eso, kubectl exec deja de servir. Y los pods son inmutables: no podés agregarle un container a uno que ya corre… salvo por esta vía.

Modern images are distroless: no shell, no curl, no ps. With that, kubectl exec stops being useful. And pods are immutable: you cannot add a container to one that is already running… except this way.

Le immagini moderne sono distroless: niente shell, niente curl, niente ps. Con questo, kubectl exec smette di servire. E i pod sono immutabili: non puoi aggiungere un container a uno già in esecuzione… tranne per questa via.

Ephemeral containers

Un subrecurso especial del pod (ephemeralcontainers) que sí admite agregar containers en caliente. Comparten el network namespace del pod — misma IP, mismo localhost — y, con --target, también el espacio de procesos del container elegido. No admiten probes ni puertos, no se reinician, y desaparecen con el pod.

A special pod subresource (ephemeralcontainers) that does allow adding containers live. They share the pod's network namespace — same IP, same localhost — and, with --target, the process namespace of the chosen container too. They allow no probes or ports, never restart, and disappear with the pod.

Una sottorisorsa speciale del pod (ephemeralcontainers) che permette di aggiungere container a caldo. Condividono il network namespace del pod — stesso IP, stesso localhost — e, con --target, anche lo spazio dei processi del container scelto. Non ammettono probe né porte, non si riavviano, e spariscono col pod.

Los tres modos, en una tabla

The three modes, in one table

Le tre modalità, in una tabella

ModoCuándo
ModeWhen
ModalitàQuando
ephemeral (default)El pod corre y querés herramientas
ephemeral (default)The pod runs and you want tools
ephemeral (default)Il pod gira e vuoi degli strumenti
--copy-toEl pod crashea o hay que cambiarle el comando
--copy-toThe pod crashes or its command must change
--copy-toIl pod crasha o va cambiato il suo comando
node/<nodo>Diagnosticar el nodo mismo (M10)
node/<node>Diagnosing the node itself (M10)
node/<nodo>Diagnosticare il nodo stesso (M10)

Por qué la copia

Why the copy

Perché la copia

Un container efímero no te salva de un CrashLoop: el pod original sigue reiniciándose. La copia te deja reemplazar el comando por un sh o un sleep, con las mismas imágenes, volúmenes y variables — y ahí sí entrás a mirar por qué el arranque falla. Ojo: es un pod suelto, no lo gestiona ningún controller, y lo tenés que borrar vos.

An ephemeral container does not save you from a CrashLoop: the original pod keeps restarting. The copy lets you replace the command with an sh or a sleep, keeping the same images, volumes and variables — and there you can finally look at why start-up fails. Careful: it is a loose pod, no controller manages it, and you must delete it yourself.

Un container effimero non ti salva da un CrashLoop: il pod originale continua a riavviarsi. La copia ti lascia sostituire il comando con un sh o uno sleep, con le stesse immagini, volumi e variabili — e lì puoi finalmente vedere perché l'avvio fallisce. Attenzione: è un pod sciolto, nessun controller lo gestisce, e devi cancellarlo tu.

★
Escalá en este orden y no al revés: describe → logs → logs --previous → exec → debug. Cada escalón cuesta más tiempo que el anterior, y la mayoría de los problemas del examen se resuelven en los dos primeros.
Escalate in this order and not the reverse: describe → logs → logs --previous → exec → debug. Each rung costs more time than the last, and most exam problems are solved on the first two.
Scala in quest'ordine e non al contrario: describe → logs → logs --previous → exec → debug. Ogni gradino costa più tempo del precedente, e la maggior parte dei problemi d'esame si risolve sui primi due.
9.5
Contexto · 15 min
Context · 15 min
Contesto · 15 min

Recursos y QoS: quién mató a quién

Resources and QoS: who killed whom

Risorse e QoS: chi ha ucciso chi

Cuando el problema no es el pod sino lo que lo rodea.
When the problem is not the pod but its surroundings.
Quando il problema non è il pod ma ciò che lo circonda.
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

OOMKilledEvicted
Quien mataEl kernelEl kubelet
Who killsThe kernelThe kubelet
Chi uccideIl kernelIl kubelet
MotivoSuperó su limitEl nodo se quedó sin recursos
ReasonIt exceeded its limitThe node ran out of resources
MotivoHa superato il suo limitIl nodo è rimasto senza risorse
AlcanceUn containerPods enteros, por prioridad
ScopeOne containerWhole pods, by priority
PortataUn containerPod interi, per priorità
Se ve enLast State del containerStatus del pod + eventos
Seen inThe container's Last StateThe pod's Status + events
Si vede inIl Last State del containerLo 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

ClaseCondiciónSe desaloja
ClassConditionEvicted
ClasseCondizioneEspulso
Guaranteedrequests = limits, CPU y memoria, en todos los containersúltimo
Guaranteedrequests = limits, CPU and memory, in all containerslast
Guaranteedrequests = limits, CPU e memoria, in tutti i containerultimo
BurstableTiene algún request o limit, pero no cumple lo anteriorsegundo
BurstableIt has some request or limit, but does not meet the abovesecond
BurstableHa qualche request o limit, ma non soddisfa quanto soprasecondo
BestEffortNingún container declara requests ni limitsprimero
BestEffortNo container declares requests or limitsfirst
BestEffortNessun container dichiara requests o limitsprimo

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é.
★
Cierre del módulo
Module wrap-up
Chiusura del modulo

Lo que te llevás del Módulo 9

What you take from Module 9

Cosa porti via dal Modulo 9

Resumen · Comandos clave
Summary · Key commands
Riassunto · Comandi chiave
# El panorama# The overview# Il quadro generale
k get pods -A -o wide
k get pods -A --field-selector status.phase!=Running

# El orden sagrado# The sacred order# L'ordine sacro
k describe pod app              # Events, de abajo hacia arriba# Events, bottom-up# Events, dal basso verso l'alto
k logs app
k logs app --previous             # CrashLoopBackOff
k logs app -c sidecar --tail=50
k get events --sort-by=.metadata.creationTimestamp

# Entrar / inyectar# Enter / inject# Entrare / iniettare
k exec -it app -- sh
k debug -it app --image=busybox --target=web
k debug app -it --copy-to=dbg --container=web -- sh
k debug node/worker1 -it --image=busybox

# Arreglos rápidos# Quick fixes# Fix rapidi
k set image deploy/web web=nginx:1.27
k set resources deploy/web --limits=memory=512Mi
k edit deploy web
k rollout status deploy/web

# Recursos# Resources# Risorse
k top nodes ; k top pods -A --sort-by=memory
k describe node worker1 | grep -A8 'Allocated resources'
k get pod app -o jsonpath='{.status.qosClass}'
Teoría · Lo esencial
Theory · The essentials
Teoria · L'essenziale

Las 7 ideas que no se negocian

The 7 non-negotiable ideas

Le 7 idee non negoziabili

  • ¿Arrancó o no arrancó? No arrancó → Events. Arrancó y murió → logs --previous. Todo el método cuelga de esa pregunta.
  • describe siempre primero, y los Events se leen de abajo hacia arriba. Expiran en una hora.
  • CrashLoopBackOff es el síntoma, no la causa: la causa está en Last State + Exit Code + logs previos.
  • READY 0/1 con STATUS Running = readinessProbe, y por lo tanto Service sin endpoints. No es la red.
  • RESTARTS que sube sin errores en los logs = livenessProbe matándolo.
  • kubectl debug para imágenes sin shell: efímero si el pod vive, --copy-to si crashea.
  • La clase de QoS no se declara, se deduce de requests y limits — y define el orden de eviction: BestEffort cae primero.
  • Did it start or not? It did not → Events. It started and died → logs --previous. The whole method hangs off that question.
  • describe first, always, and Events are read bottom-up. They expire within an hour.
  • CrashLoopBackOff is the symptom, not the cause: the cause is in Last State + Exit Code + previous logs.
  • READY 0/1 with STATUS Running = readinessProbe, and therefore a Service with no endpoints. It is not the network.
  • RESTARTS climbing with no errors in the logs = a livenessProbe killing it.
  • kubectl debug for shell-less images: ephemeral if the pod lives, --copy-to if it crashes.
  • The QoS class is not declared, it is deduced from requests and limits — and it sets the eviction order: BestEffort falls first.
  • È partito o no? Non è partito → Events. È partito ed è morto → logs --previous. Tutto il metodo pende da quella domanda.
  • describe sempre per primo, e gli Events si leggono dal basso verso l'alto. Scadono entro un'ora.
  • CrashLoopBackOff è il sintomo, non la causa: la causa è in Last State + Exit Code + log precedenti.
  • READY 0/1 con STATUS Running = readinessProbe, e quindi Service senza endpoint. Non è la rete.
  • RESTARTS che sale senza errori nei log = una livenessProbe che lo uccide.
  • kubectl debug per immagini senza shell: effimero se il pod vive, --copy-to se crasha.
  • La classe QoS non si dichiara, si deduce da requests e limits — e stabilisce l'ordine di eviction: BestEffort cade per primo.
★
El hilo del módulo: troubleshooting no es saber más causas, es descartar más rápido. Cada comando de la secuencia parte el espacio de posibilidades en dos. Si te encontrás probando cosas sin haber leído los eventos, pará y volvé al paso 1 — ese reflejo vale más puntos que cualquier truco.
The thread of the module: troubleshooting is not knowing more causes, it is ruling out faster. Each command in the sequence halves the space of possibilities. If you catch yourself trying things without having read the events, stop and go back to step 1 — that reflex is worth more points than any trick.
Il filo del modulo: il troubleshooting non è conoscere più cause, è escludere più in fretta. Ogni comando della sequenza dimezza lo spazio delle possibilità. Se ti accorgi di provare cose senza aver letto gli eventi, fermati e torna al passo 1 — quel riflesso vale più punti di qualsiasi trucco.
Checkpoint Módulo 9: el método describe→logs→events, el catálogo de estados de pod con su causa, las tres probes y sus síntomas, kubectl debug en sus tres modos, y recursos: OOMKilled vs Evicted y las clases de QoS. Primer tercio del dominio Troubleshooting (30%). Siguiente: Módulo 10 — troubleshooting de cluster y control plane.
Module 9 checkpoint: the describe→logs→events method, the catalogue of pod states with their causes, the three probes and their symptoms, kubectl debug in its three modes, and resources: OOMKilled vs Evicted and the QoS classes. First third of the Troubleshooting domain (30%). Next: Module 10 — cluster and control plane troubleshooting.
Checkpoint Modulo 9: il metodo describe→logs→events, il catalogo degli stati dei pod con le loro cause, le tre probe e i loro sintomi, kubectl debug nelle sue tre modalità, e le risorse: OOMKilled contro Evicted e le classi QoS. Primo terzo del dominio Troubleshooting (30%). Prossimo: Modulo 10 — troubleshooting di cluster e control plane.
★
Fase 2 · resolver en vivo
Phase 2 · solve it live
Fase 2 · risolvere dal vivo

Práctica — 8 ejercicios

Practice — 8 exercises

Pratica — 8 esercizi

Ocho pods rotos, uno por síntoma. Cronometrate: 3 min cada uno.
Eight broken pods, one per symptom. Time yourself: 3 min each.
Otto pod rotti, uno per sintomo. Cronometrati: 3 min ciascuno.
0 / 8
🧠
Modelo mental. ¿Arrancó? → Events. ¿Arrancó y murió? → logs --previous. Todos estos ejercicios se resuelven con la misma secuencia, sin excepción: get pods → describe → logs. Si en algún momento te encontrás editando YAML antes de haber leído un evento, parás y volvés al paso 1.
Mental model. Did it start? → Events. Did it start and die? → logs --previous. All these exercises are solved by the same sequence, without exception: get pods → describe → logs. If at any point you catch yourself editing YAML before reading an event, stop and go back to step 1.
Modello mentale. È partito? → Events. È partito ed è morto? → logs --previous. Tutti questi esercizi si risolvono con la stessa sequenza, senza eccezioni: get pods → describe → logs. Se a un certo punto ti accorgi di modificare YAML prima di aver letto un evento, fermati e torna al passo 1.
◆
Preparación. Estos ejercicios se plantan solos y no rompen el cluster. Trabajá en un namespace descartable: k create ns ts && k config set-context --current -n ts. Al terminar, k delete ns ts se lleva todo. La regla del módulo: plantá el problema, olvidate de cómo lo plantaste, y diagnosticá desde cero.
Setup. These exercises plant themselves and do not break the cluster. Work in a throwaway namespace: k create ns ts && k config set-context --current -n ts. When done, k delete ns ts takes everything with it. The module's rule: plant the problem, forget how you planted it, and diagnose from scratch.
Preparazione. Questi esercizi si preparano da soli e non rompono il cluster. Lavora in un namespace usa e getta: k create ns ts && k config set-context --current -n ts. Alla fine, k delete ns ts si porta via tutto. La regola del modulo: pianta il problema, dimentica come l'hai piantato, e diagnostica da zero.
E1

La rutina completa sobre un pod que no arranca

The full routine on a pod that will not start

La routine completa su un pod che non parte

~4 min

Ejecutá la secuencia de los cuatro pasos sobre este pod y llegá a la causa sin editar nada. Después arreglalo con un solo comando.

Para plantarlo: k create deploy web --image=ngnix:1.27 (sí, con el typo).

Run the four-step sequence on this pod and reach the cause without editing anything. Then fix it with a single command.

To plant it: k create deploy web --image=ngnix:1.27 (yes, with the typo).

Esegui la sequenza dei quattro passi su questo pod e arriva alla causa senza modificare nulla. Poi sistemalo con un solo comando.

Per prepararlo: k create deploy web --image=ngnix:1.27 (sì, col refuso).

Ver soluciónShow solutionMostra soluzione
k get pods                          # ImagePullBackOff
k describe pod web-xxx | tail -10
#   Failed to pull image "ngnix:1.27": ... not found
k logs web-xxx                    # vacío: el container nunca existió# empty: the container never existed# vuoto: il container non è mai esistito

k get pod web-xxx -o jsonpath='{.spec.containers[*].image}'
k set image deploy/web ngnix=nginx:1.27
k rollout status deploy/web
Claves
Key points
Punti chiave
  • Los logs vacíos son un dato, no un callejón: confirman que el container nunca llegó a arrancar. Ahí el método te manda a los Events, no a insistir con logs.
  • set image usa el nombre del container, no el de la imagen vieja. Con create deploy el container se llama como la imagen, de ahí ngnix=.
  • Cuatro comandos, cero YAML editado. Ese es el ritmo del examen.
  • Empty logs are data, not a dead end: they confirm the container never started. There the method sends you to the Events, not to insisting with logs.
  • set image takes the container name, not the old image's. With create deploy the container is named after the image, hence ngnix=.
  • Four commands, zero YAML edited. That is exam pace.
  • I log vuoti sono un dato, non un vicolo cieco: confermano che il container non è mai partito. Lì il metodo ti manda agli Events, non a insistere con logs.
  • set image usa il nome del container, non quello della vecchia immagine. Con create deploy il container prende il nome dell'immagine, da cui ngnix=.
  • Quattro comandi, zero YAML modificato. È il ritmo dell'esame.
E2

Pending: distinguir las cuatro causas

Pending: telling the four causes apart

Pending: distinguere le quattro cause

~6 min

Plantá cuatro pods Pending, uno por cada causa, y aprendé a distinguirlas solo por el mensaje del evento: (a) pide 100 CPUs; (b) tiene un nodeSelector que no matchea ningún nodo; (c) no tolera el taint del control plane y no hay workers; (d) monta una PVC que no bindea. Anotá el texto exacto de cada uno.

Plant four Pending pods, one per cause, and learn to tell them apart from the event message alone: (a) it requests 100 CPUs; (b) it has a nodeSelector matching no node; (c) it does not tolerate the control-plane taint and there are no workers; (d) it mounts a PVC that never binds. Note the exact wording of each.

Pianta quattro pod Pending, uno per causa, e impara a distinguerli dal solo messaggio dell'evento: (a) chiede 100 CPU; (b) ha un nodeSelector che non corrisponde a nessun nodo; (c) non tollera il taint del control plane e non ci sono worker; (d) monta una PVC che non lega. Annota il testo esatto di ciascuno.

Ver soluciónShow solutionMostra soluzione
k run a --image=nginx $do | ... # requests.cpu: "100"# requests.cpu: "100"# requests.cpu: "100"
k run b --image=nginx --overrides='{"spec":{"nodeSelector":{"disk":"ssd"}}}'
# c y d: ver M5 y M8# c and d: see M5 and M8# c e d: vedi M5 e M8

k describe pod a | tail -4
#   0/3 nodes are available: 3 Insufficient cpu
k describe pod b | tail -4
#   ... didn't match Pod's node affinity/selector
k describe pod c | tail -4
#   ... had untolerated taint {node-role.kubernetes.io/control-plane: }
k describe pod d | tail -4
#   pod has unbound immediate PersistentVolumeClaims
Claves
Key points
Punti chiave
  • Los cuatro mensajes son distintos y cada uno te manda a un módulo distinto: recursos y taints/selectors al M5, PVC al M8. Reconocer el texto es medio ejercicio resuelto.
  • El formato 0/3 nodes are available: … te dice cuántos nodos rechazaron y por qué. Si hay varias razones, las lista todas.
  • La causa (d) es distinta en naturaleza: no es el scheduler, es que la PVC no bindea. El pod es la víctima, no el problema.
  • All four messages differ and each sends you to a different module: resources and taints/selectors to M5, PVC to M8. Recognising the text is half the exercise solved.
  • The 0/3 nodes are available: … format tells you how many nodes refused and why. With several reasons, it lists them all.
  • Cause (d) is different in nature: it is not the scheduler, it is the PVC failing to bind. The pod is the victim, not the problem.
  • I quattro messaggi sono diversi e ciascuno ti manda a un modulo diverso: risorse e taint/selector al M5, PVC al M8. Riconoscere il testo è mezzo esercizio risolto.
  • Il formato 0/3 nodes are available: … ti dice quanti nodi hanno rifiutato e perché. Con più motivi, li elenca tutti.
  • La causa (d) è diversa per natura: non è lo scheduler, è la PVC che non lega. Il pod è la vittima, non il problema.
E3

CrashLoopBackOff: el --previous

CrashLoopBackOff: the --previous

CrashLoopBackOff: il --previous

~5 min

Un pod está en CrashLoopBackOff. Sacá la causa exacta y el exit code. Después conseguí entrar al container a pesar de que se muere, para inspeccionarlo.

Para plantarlo: k run cl --image=busybox --command -- sh -c 'echo "config no encontrada"; exit 1'

A pod is in CrashLoopBackOff. Get the exact cause and the exit code. Then manage to get inside the container despite it dying, to inspect it.

To plant it: k run cl --image=busybox --command -- sh -c 'echo "config not found"; exit 1'

Un pod è in CrashLoopBackOff. Ricava la causa esatta e l'exit code. Poi riesci a entrare nel container nonostante muoia, per ispezionarlo.

Per prepararlo: k run cl --image=busybox --command -- sh -c 'echo "config non trovata"; exit 1'

Ver soluciónShow solutionMostra soluzione
k logs cl --previous              # la salida de la instancia MUERTA# the DEAD instance's output# l'output dell'istanza MORTA
k describe pod cl | grep -A6 'Last State'
#   Reason: Error   Exit Code: 1

# entrar igual: copia del pod con el comando cambiado# get in anyway: a copy of the pod with the command replaced# entrare comunque: una copia del pod col comando sostituito
k debug cl -it --copy-to=cl-dbg --container=cl -- sh
#   adentro: mismas variables, mismos volúmenes, sin el comando que mata#   inside: same variables, same volumes, without the killing command#   dentro: stesse variabili, stessi volumi, senza il comando che uccide
k delete pod cl-dbg
Claves
Key points
Punti chiave
  • k logs cl a secas puede darte "container is not running" o la salida de un arranque a medio hacer. El --previous te da la instancia que efectivamente murió: es EL comando del CrashLoop.
  • Last State + Exit Code completan el cuadro: 1 es la app, 137 es OOM o liveness, 0 es "el comando terminó y no debía".
  • Un container efímero no sirve acá — el pod original sigue muriendo. Por eso --copy-to con el comando reemplazado.
  • Plain k logs cl may give you "container is not running" or the output of a half-done start. --previous gives you the instance that actually died: it is THE CrashLoop command.
  • Last State + Exit Code complete the picture: 1 is the app, 137 is OOM or liveness, 0 is "the command finished and should not have".
  • An ephemeral container is no use here — the original pod keeps dying. Hence --copy-to with the command replaced.
  • Un k logs cl secco può darti "container is not running" o l'output di un avvio a metà. --previous ti dà l'istanza che è effettivamente morta: è IL comando del CrashLoop.
  • Last State + Exit Code completano il quadro: 1 è l'app, 137 è OOM o liveness, 0 è "il comando è finito e non doveva".
  • Un container effimero non serve qui — il pod originale continua a morire. Da cui --copy-to col comando sostituito.
E4

CreateContainerConfigError

CreateContainerConfigError

CreateContainerConfigError

~4 min

Un pod queda en CreateContainerConfigError. Encontrá qué le falta y creá lo mínimo para que arranque. Explicá por qué este estado no es un CrashLoop.

Para plantarlo: un pod con envFrom: [{configMapRef: {name: app-config}}] y sin crear ese ConfigMap.

A pod sits in CreateContainerConfigError. Find what is missing and create the minimum for it to start. Explain why this state is not a CrashLoop.

To plant it: a pod with envFrom: [{configMapRef: {name: app-config}}] and that ConfigMap never created.

Un pod resta in CreateContainerConfigError. Trova cosa manca e crea il minimo perché parta. Spiega perché questo stato non è un CrashLoop.

Per prepararlo: un pod con envFrom: [{configMapRef: {name: app-config}}] e quella ConfigMap mai creata.

Ver soluciónShow solutionMostra soluzione
k describe pod app | tail -6
#   Error: configmap "app-config" not found

k get cm                       # no está# not there# non c'è
k create configmap app-config --from-literal=MODE=prod

k get pods -w                   # arranca solo, sin recrearlo# it starts on its own, no recreation needed# parte da solo, senza ricrearlo
Claves
Key points
Punti chiave
  • Es un estadio anterior al CrashLoop: el container nunca llegó a existir, porque el kubelet no pudo armar su configuración. Por eso no hay logs de ningún tipo.
  • El kubelet reintenta solo: apenas creás el ConfigMap, el pod arranca sin que lo toques. No hace falta borrarlo ni recrearlo.
  • Verificá siempre nombre Y namespace: un ConfigMap en default no lo ve un pod en ts.
  • It is a stage before the CrashLoop: the container never came into existence, because the kubelet could not assemble its configuration. Hence no logs of any kind.
  • The kubelet retries on its own: the moment you create the ConfigMap, the pod starts without you touching it. No need to delete or recreate.
  • Always check name AND namespace: a ConfigMap in default is invisible to a pod in ts.
  • È uno stadio precedente al CrashLoop: il container non è mai esistito, perché il kubelet non è riuscito a comporne la configurazione. Da cui l'assenza totale di log.
  • Il kubelet riprova da solo: appena crei la ConfigMap, il pod parte senza che tu lo tocchi. Non serve cancellarlo né ricrearlo.
  • Verifica sempre nome E namespace: una ConfigMap in default è invisibile a un pod in ts.
E5

La liveness que mata una app sana

The liveness that kills a healthy app

La liveness che uccide un'app sana

~7 min

Un Deployment tiene RESTARTS subiendo y los logs no muestran ningún error. Diagnosticá qué lo mata, calculá con la fórmula a qué segundo se reinicia, y arreglalo de dos formas distintas: (a) ajustando la liveness; (b) agregando una startupProbe. Explicá cuál es mejor.

Para plantarlo: un deployment nginx con livenessProbe httpGet /healthz:80, initialDelaySeconds: 1.

A Deployment has RESTARTS climbing and the logs show no error at all. Diagnose what kills it, use the formula to compute at which second it restarts, and fix it two different ways: (a) by adjusting the liveness; (b) by adding a startupProbe. Explain which is better.

To plant it: an nginx deployment with livenessProbe httpGet /healthz:80, initialDelaySeconds: 1.

Un Deployment ha RESTARTS che sale e i log non mostrano alcun errore. Diagnostica cosa lo uccide, calcola con la formula a che secondo si riavvia, e sistemalo in due modi diversi: (a) regolando la liveness; (b) aggiungendo una startupProbe. Spiega quale è migliore.

Per prepararlo: un deployment nginx con livenessProbe httpGet /healthz:80, initialDelaySeconds: 1.

Ver soluciónShow solutionMostra soluzione
k get pods                          # RESTARTS 5, 6, 7...
k logs web-xxx                    # nginx arrancando, sin errores# nginx starting up, no errors# nginx che parte, nessun errore
k describe pod web-xxx | grep -i -A2 unhealthy
#   Liveness probe failed: HTTP probe failed with statuscode: 404

# la causa real: nginx no sirve /healthz. Dos arreglos:# the real cause: nginx does not serve /healthz. Two fixes:# la causa vera: nginx non serve /healthz. Due fix:
k edit deploy web       # (a) path: /   e initialDelaySeconds: 10# (a) path: /   and initialDelaySeconds: 10# (a) path: /   e initialDelaySeconds: 10
k edit deploy web       # (b) + startupProbe con failureThreshold: 30# (b) + a startupProbe with failureThreshold: 30# (b) + una startupProbe con failureThreshold: 30
k rollout status deploy/web
Claves
Key points
Punti chiave
  • RESTARTS subiendo + logs limpios = liveness. Es el patrón más característico del módulo y el que más se diagnostica mal.
  • La fórmula: se reinicia a los initialDelay + (failureThreshold − 1) × periodSeconds. Con 1 + 2×10 = 21s. Sabé hacer esa cuenta de cabeza.
  • La startupProbe es mejor cuando el problema es un arranque lento: le da un presupuesto generoso al inicio sin aflojar la detección de cuelgues en régimen. Subir el initialDelaySeconds de la liveness resuelve el arranque pero deja el cluster ciego durante ese tiempo, para siempre.
  • Acá el problema de fondo era el path, no el tiempo. Leé el evento antes de decidir qué ajustar.
  • RESTARTS climbing + clean logs = liveness. It is the module's most characteristic pattern and the most misdiagnosed one.
  • The formula: it restarts at initialDelay + (failureThreshold − 1) × periodSeconds. Here 1 + 2×10 = 21s. Know how to do that in your head.
  • The startupProbe is better when the problem is a slow start: it grants a generous budget up front without loosening hang detection in steady state. Raising liveness's initialDelaySeconds fixes the start-up but leaves the cluster blind for that long, forever.
  • Here the underlying problem was the path, not the timing. Read the event before deciding what to adjust.
  • RESTARTS che sale + log puliti = liveness. È il pattern più caratteristico del modulo e il più diagnosticato male.
  • La formula: si riavvia a initialDelay + (failureThreshold − 1) × periodSeconds. Qui 1 + 2×10 = 21s. Sappi farlo a mente.
  • La startupProbe è migliore quando il problema è un avvio lento: concede un budget generoso all'inizio senza allentare il rilevamento dei blocchi a regime. Alzare l'initialDelaySeconds della liveness risolve l'avvio ma lascia il cluster cieco per quel tempo, per sempre.
  • Qui il problema di fondo era il path, non il tempo. Leggi l'evento prima di decidere cosa regolare.
E6

Debug de una imagen sin shell

Debugging a shell-less image

Debug di un'immagine senza shell

~6 min

Un pod corre con una imagen distroless: k exec -it app -- sh falla con executable file not found. Desde dentro de la red de ese pod, comprobá que su app responde en localhost:8080 y que resuelve el DNS de otro Service. No modifiques el pod.

A pod runs a distroless image: k exec -it app -- sh fails with executable file not found. From inside that pod's network, verify its app answers on localhost:8080 and that it resolves another Service's DNS. Do not modify the pod.

Un pod gira con un'immagine distroless: k exec -it app -- sh fallisce con executable file not found. Da dentro la rete di quel pod, verifica che la sua app risponda su localhost:8080 e che risolva il DNS di un altro Service. Non modificare il pod.

Ver soluciónShow solutionMostra soluzione
k debug -it app --image=busybox --target=app

# adentro: misma IP, mismo localhost que el container real# inside: same IP, same localhost as the real container# dentro: stesso IP, stesso localhost del container reale
wget -qO- localhost:8080
nslookup otro-service
netstat -tlnp
ps aux                          # gracias a --target# thanks to --target# grazie a --target

k get pod app -o jsonpath='{.spec.ephemeralContainers[*].name}'
Claves
Key points
Punti chiave
  • El container efímero comparte el network namespace del pod: por eso localhost adentro es el container real. Es lo que lo hace útil para diagnosticar red.
  • --target agrega el espacio de procesos: sin él ves la red pero no los procesos del otro container.
  • No modificás el pod: el ephemeral container es un subrecurso aparte, no toca el spec de los containers. Desaparece cuando el pod muere.
  • The ephemeral container shares the pod's network namespace: that is why localhost inside is the real container. It is what makes it useful for network diagnosis.
  • --target adds the process namespace: without it you see the network but not the other container's processes.
  • You do not modify the pod: the ephemeral container is a separate subresource, it does not touch the containers' spec. It disappears when the pod dies.
  • Il container effimero condivide il network namespace del pod: per questo localhost dentro è il container reale. È ciò che lo rende utile per diagnosticare la rete.
  • --target aggiunge lo spazio dei processi: senza, vedi la rete ma non i processi dell'altro container.
  • Non modifichi il pod: il container effimero è una sottorisorsa a parte, non tocca lo spec dei container. Sparisce quando il pod muore.
E7

QoS: clasificar y promover

QoS: classify and promote

QoS: classificare e promuovere

~5 min

Creá tres pods, uno de cada clase de QoS, y verificá la clasificación con un solo comando. Después tomá el Burstable y promovelo a Guaranteed sin recrearlo. Extra: creá un pod de dos containers donde uno tenga resources completos y el otro ninguno — ¿qué clase le toca al pod?

Create three pods, one per QoS class, and verify the classification with a single command. Then take the Burstable one and promote it to Guaranteed without recreating it. Extra: create a two-container pod where one has full resources and the other none — which class does the pod get?

Crea tre pod, uno per classe QoS, e verifica la classificazione con un solo comando. Poi prendi il Burstable e promuovilo a Guaranteed senza ricrearlo. Extra: crea un pod di due container dove uno ha resources completi e l'altro nessuno — quale classe tocca al pod?

Ver soluciónShow solutionMostra soluzione
k get pods -o custom-columns=NAME:.metadata.name,QOS:.status.qosClass
#   g    Guaranteed     ·   b    Burstable     ·   be   BestEffort

# promover: igualar requests y limits, CPU Y memoria# promote: equalise requests and limits, CPU AND memory# promuovere: pareggiare requests e limits, CPU E memoria
k set resources deploy/b \
  --requests=cpu=200m,memory=128Mi --limits=cpu=200m,memory=128Mi
k get pods -o custom-columns=NAME:.metadata.name,QOS:.status.qosClass

# el pod mixto → Burstable (se evalúa a nivel POD)# the mixed pod → Burstable (it is evaluated per POD)# il pod misto → Burstable (si valuta a livello POD)
Claves
Key points
Punti chiave
  • La clase no se escribe, se deduce. Si el enunciado pide "hacelo Guaranteed", lo que hacés es igualar requests y limits — no buscar un campo qosClass, que no existe en el spec.
  • Se evalúa a nivel pod: un solo container sin resources arrastra a todo el pod a Burstable. Los initContainers cuentan.
  • Guaranteed exige igualdad en CPU y memoria. Igualar solo memoria te deja en Burstable — es el error más frecuente.
  • El custom-columns con .status.qosClass te da el mapa de quién cae primero en todo el cluster.
  • The class is not written, it is deduced. If the wording asks to "make it Guaranteed", what you do is equalise requests and limits — not hunt for a qosClass field, which does not exist in the spec.
  • It is evaluated per pod: a single container without resources drags the whole pod to Burstable. initContainers count.
  • Guaranteed requires equality on CPU and memory. Equalising only memory leaves you Burstable — the most frequent mistake.
  • custom-columns with .status.qosClass gives you the map of who falls first across the cluster.
  • La classe non si scrive, si deduce. Se il testo chiede di "renderlo Guaranteed", ciò che fai è pareggiare requests e limits — non cercare un campo qosClass, che nella spec non esiste.
  • Si valuta per pod: un solo container senza resources trascina l'intero pod a Burstable. Gli initContainer contano.
  • Guaranteed richiede uguaglianza su CPU e memoria. Pareggiare solo la memoria ti lascia Burstable — l'errore più frequente.
  • Il custom-columns con .status.qosClass ti dà la mappa di chi cade per primo in tutto il cluster.
T1

Troubleshooting — OOMKilled o Evicted

Troubleshooting — OOMKilled or Evicted

Troubleshooting — OOMKilled o Evicted

~7 min

Un pod murió por memoria. Determiná quién lo mató — el kernel o el kubelet — y arreglalo en consecuencia. Explicá en qué campo se ve cada caso.

Para plantar el OOMKilled: un pod con limits.memory: 20Mi corriendo stress --vm 1 --vm-bytes 200M, o más simple: k run oom --image=polinux/stress --limits=memory=20Mi -- stress --vm 1 --vm-bytes 150M --vm-hang 1.

A pod died on memory. Determine who killed it — the kernel or the kubelet — and fix accordingly. Explain which field shows each case.

To plant the OOMKilled: a pod with limits.memory: 20Mi running stress --vm 1 --vm-bytes 200M, or more simply: k run oom --image=polinux/stress --limits=memory=20Mi -- stress --vm 1 --vm-bytes 150M --vm-hang 1.

Un pod è morto per memoria. Determina chi l'ha ucciso — il kernel o il kubelet — e sistemalo di conseguenza. Spiega in quale campo si vede ciascun caso.

Per piantare l'OOMKilled: un pod con limits.memory: 20Mi che esegue stress --vm 1 --vm-bytes 200M, o più semplice: k run oom --image=polinux/stress --limits=memory=20Mi -- stress --vm 1 --vm-bytes 150M --vm-hang 1.

Ver soluciónShow solutionMostra soluzione
# OOMKilled: se ve en el Last State del CONTAINER# OOMKilled: it shows in the CONTAINER's Last State# OOMKilled: si vede nel Last State del CONTAINER
k describe pod oom | grep -A6 'Last State'
#   Reason: OOMKilled   Exit Code: 137
k get pod oom                    # sigue existiendo, reiniciándose# it still exists, restarting# esiste ancora, si riavvia
k set resources deploy/oom --limits=memory=256Mi

# Evicted: se ve en el STATUS del POD, y el pod queda muerto# Evicted: it shows in the POD's STATUS, and the pod stays dead# Evicted: si vede nello STATUS del POD, e il pod resta morto
k get pods -A | grep Evicted
k describe pod x | grep -i -A3 evict
#   The node was low on resource: memory
k describe node worker1 | grep -A6 Conditions      # MemoryPressure
k delete pods -A --field-selector status.phase=Failed
Claves
Key points
Punti chiave
  • El campo donde aparece delata al ejecutor: Last State del container = kernel (OOMKilled, exit 137). Status del pod = kubelet (Evicted). Un OOMKilled reinicia; un Evicted deja un cadáver.
  • Los arreglos son opuestos: el OOMKilled se arregla subiendo el limit del pod (o bajando el consumo). El Evicted se arregla mirando el nodo: liberar memoria, mover cargas, y sobre todo poner requests para no ser BestEffort.
  • Un pod Guaranteed también puede ser OOMKilled: la clase de QoS protege del eviction, no del propio limit.
  • Los pods Evicted se acumulan y ensucian el get pods. Limpialos con el --field-selector status.phase=Failed.
  • The field it appears in reveals the executioner: the container's Last State = kernel (OOMKilled, exit 137). The pod's Status = kubelet (Evicted). An OOMKilled restarts; an Evicted leaves a corpse.
  • The fixes are opposite: OOMKilled is fixed by raising the pod's limit (or lowering consumption). Evicted is fixed by looking at the node: free memory, move workloads, and above all set requests so you are not BestEffort.
  • A Guaranteed pod can be OOMKilled too: the QoS class protects against eviction, not against your own limit.
  • Evicted pods pile up and clutter get pods. Clean them with --field-selector status.phase=Failed.
  • Il campo in cui compare svela l'esecutore: il Last State del container = kernel (OOMKilled, exit 137). Lo Status del pod = kubelet (Evicted). Un OOMKilled si riavvia; un Evicted lascia un cadavere.
  • I fix sono opposti: l'OOMKilled si sistema alzando il limit del pod (o abbassando il consumo). L'Evicted si sistema guardando il nodo: liberare memoria, spostare carichi, e soprattutto mettere le requests per non essere BestEffort.
  • Anche un pod Guaranteed può essere OOMKilled: la classe QoS protegge dall'eviction, non dal proprio limit.
  • I pod Evicted si accumulano e sporcano il get pods. Puliscili con --field-selector status.phase=Failed.

Autoevaluación — antes de marcar M9 como ★ dominado

Self-check — before marking M9 as ★ mastered

Autovalutazione — prima di segnare M9 come ★ padroneggiato

  • Ejecutar la secuencia describe→logs→events sin pensarla.
  • Decir, viendo solo el STATUS, si el problema es del cluster o del container.
  • Distinguir las cuatro causas de Pending por el texto del evento.
  • Llegar a la causa de un CrashLoop con --previous y el Exit Code.
  • Reconocer "RESTARTS sube, logs limpios" como liveness en menos de 30 s.
  • Calcular a qué segundo reinicia una liveness con la fórmula.
  • Usar kubectl debug en sus tres modos y saber cuál toca.
  • Distinguir OOMKilled de Evicted por el campo donde aparece.
  • Run the describe→logs→events sequence without thinking.
  • Say, from the STATUS alone, whether the problem is the cluster's or the container's.
  • Tell the four Pending causes apart by the event text.
  • Reach a CrashLoop's cause with --previous and the Exit Code.
  • Recognise "RESTARTS climbing, clean logs" as liveness in under 30 s.
  • Compute at which second a liveness restarts, using the formula.
  • Use kubectl debug in its three modes and know which one applies.
  • Tell OOMKilled from Evicted by the field it appears in.
  • Eseguire la sequenza describe→logs→events senza pensarci.
  • Dire, dal solo STATUS, se il problema è del cluster o del container.
  • Distinguere le quattro cause di Pending dal testo dell'evento.
  • Arrivare alla causa di un CrashLoop con --previous e l'Exit Code.
  • Riconoscere "RESTARTS sale, log puliti" come liveness in meno di 30 s.
  • Calcolare a che secondo si riavvia una liveness, con la formula.
  • Usare kubectl debug nelle sue tre modalità e sapere quale serve.
  • Distinguere OOMKilled da Evicted dal campo in cui compare.
Módulo 9 completado · Troubleshooting, parte 1 de 3 · Siguiente: nodos NotReady, static pods, crictl y journalctl
Module 9 complete · Troubleshooting, part 1 of 3 · Next: NotReady nodes, static pods, crictl and journalctl
Modulo 9 completato · Troubleshooting, parte 1 di 3 · Prossimo: nodi NotReady, static pod, crictl e journalctl