Kubernetes Kubernetes

Depura un Pod que falla: describe, logs, exec y port-forward

Una lista de comprobación repetible para averiguar por qué un Pod está Pending, se cae o no responde.

12 min Principiante

Tarde o temprano un Pod no arrancará, se reiniciará constantemente o estará en ejecución pero no responderá. La solución casi siempre es visible con un puñado de comandos de kubectl usados en el orden correcto. Esta guía te da ese orden: leer el estado, pedir los eventos, leer los logs, entrar en el contenedor y luego rastrear la ruta de red.

Lee primero el estado

Empieza por la columna de estado:

kubectl get pods -o wide

Los estados más comunes y dónde mirar primero:

  • Pending. El Pod no está asignado a un nodo. Normalmente no hay suficiente CPU o memoria, se agotó una cuota o un volumen no puede enlazarse. describe te dirá cuál.
  • ContainerCreating. El nodo está preparando el contenedor. Si se queda así, busca un volumen ausente o una descarga de imagen lenta.
  • ErrImagePull / ImagePullBackOff. No se puede descargar la imagen, casi siempre por un error tipográfico en el nombre o la etiqueta, o por faltar credenciales de un registro privado.
  • CrashLoopBackOff. El contenedor arranca, termina y se reinicia repetidamente con retrasos crecientes. La aplicación está fallando, así que lee sus logs.
  • CreateContainerConfigError. El Pod referencia un ConfigMap, Secret o clave que no existe.
  • Running pero READY 0/1. El contenedor está levantado pero falla su readiness probe.
  • OOMKilled (se muestra en describe). El contenedor superó su límite de memoria.

Pregúntale a Kubernetes por qué: describe y eventos

kubectl describe pod POD_NAME

Desplázate hasta la sección Events al final. Se lee como una línea de tiempo: Scheduled, Pulling, Failed, BackOff. Mensajes como 0/3 nodes are available: insufficient cpu o Failed to pull image "ngnix:1.25" suelen nombrar el problema directamente. Más arriba, mira el State y el Last State de cada contenedor para ver códigos de salida y motivos. Para una vista de todo lo ocurrido en el namespace:

kubectl get events --sort-by=.metadata.creationTimestamp

Lee la salida del contenedor

kubectl logs POD_NAME
kubectl logs POD_NAME -c CONTAINER_NAME
kubectl logs POD_NAME --all-containers
kubectl logs POD_NAME --previous
kubectl logs POD_NAME -f --since=10m --tail=100

Usa -c cuando un Pod tiene varios contenedores, -f para transmitir en directo, --since y --tail para limitar la salida, y --previous para leer los logs de la última instancia que se cayó, que es lo que necesitas en un crash loop. Para probarlo, crea un Deployment cuyo contenedor falle a propósito:

kubectl create deployment crashy --image=busybox:1.36 -- \
  sh -c 'echo starting; echo "fatal: DB_URL is not set" >&2; exit 1'
kubectl get pods -l app=crashy
kubectl logs deployment/crashy --previous

El estado pasa a CrashLoopBackOff y el log muestra por qué. Limpia con kubectl delete deployment crashy.

Ten en cuenta que los logs pertenecen al Pod. Cuando se elimina o se reemplaza, sus logs se limpian con él, así que para tener historial necesitas un recolector de logs como Fluentd o Fluent Bit que los envíe a un lugar duradero.

Entra: exec, port-forward y cp

kubectl exec -it POD_NAME -- sh
kubectl exec POD_NAME -- env
kubectl port-forward pod/POD_NAME 8080:80
curl http://localhost:8080
kubectl cp POD_NAME:/path/to/file ./file

exec ejecuta un comando en el contenedor, port-forward conecta un puerto local con un puerto del Pod (también funciona con svc/NAME) y cp copia archivos hacia dentro o hacia fuera. Muchas imágenes de producción son mínimas y no tienen shell ni curl. En ese caso, kubectl debug -it POD_NAME --image=busybox:1.36 --target=CONTAINER_NAME adjunta un contenedor de depuración temporal que comparte los namespaces del Pod.

Todo lo que cambies dentro de un contenedor en ejecución, como editar un archivo de configuración, se pierde en cuanto se reemplaza el Pod. Usa exec para investigar, pero haz la corrección en la imagen, en el manifiesto o en un ConfigMap.

Trabaja hacia atrás por la ruta de la petición

Si el Pod parece sano pero la aplicación no es accesible, rastrea la ruta de dentro hacia fuera:

  1. ¿El Pod está en ejecución y READY? Arregla primero el Pod.
  2. ¿El selector del Service coincide con las etiquetas del Pod? kubectl get pods --show-labels y kubectl describe service NAME deberían coincidir.
  3. ¿El Service tiene endpoints? kubectl get endpointslices -l kubernetes.io/service-name=NAME debería listar IP de Pods. Una lista vacía significa un selector que no coincide o ningún Pod listo.
  4. ¿Coinciden los puertos? El targetPort del Service debe ser el puerto en el que el contenedor escucha realmente.
  5. ¿Funciona desde otro Pod? kubectl run tmp --rm -it --image=busybox:1.36 --restart=Never -- wget -qO- http://NAME. Esto prueba el DNS y el Service, descartando todo lo que esté fuera del clúster.
  6. ¿Podría estar bloqueándolo una NetworkPolicy? Lístalas con kubectl get networkpolicy.
  7. Por último, revisa el Ingress o el balanceador de carga externo que hay delante.

Cada paso acota el problema a una capa, lo que es más rápido que adivinar.

Guías populares