Capítulo 6: Logs y Debugging
Logs: tu mejor herramienta
Como desarrollador, los logs son lo primero que vas a revisar cuando algo falla. Kubernetes captura todo lo que tu app escribe en stdout/stderr.
Comandos básicos de logs
# Logs de un pod
kubectl logs <nombre-pod> -n dev
# Últimas 50 líneas
kubectl logs <nombre-pod> -n dev --tail=50
# Logs en tiempo real (como tail -f)
kubectl logs <nombre-pod> -n dev -f
# Logs de la última hora
kubectl logs <nombre-pod> -n dev --since=1h
# Logs de los últimos 30 minutos
kubectl logs <nombre-pod> -n dev --since=30m
Logs de todos los pods del deployment
# Usando el selector de labels
kubectl logs -l app=fastapi-app -n dev
# En tiempo real, todos los pods
kubectl logs -l app=fastapi-app -n dev -f
# Con prefijo del pod (para saber cuál generó cada línea)
kubectl logs -l app=fastapi-app -n dev --prefix
Logs de un container que crasheó
# Logs del container anterior (si reinició)
kubectl logs <nombre-pod> -n dev --previous
# Útil cuando el pod está en CrashLoopBackOff
Logging estructurado en FastAPI
Para que los logs sean más útiles en Kubernetes, usá logging estructurado:
import logging
import json
from fastapi import FastAPI, Request
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s %(levelname)s %(name)s %(message)s'
)
logger = logging.getLogger("fastapi-app")
app = FastAPI()
@app.middleware("http")
async def log_requests(request: Request, call_next):
logger.info(f"→ {request.method} {request.url.path}")
response = await call_next(request)
logger.info(f"← {request.method} {request.url.path} {response.status_code}")
return response
Describe: el primer paso para diagnosticar
describe muestra eventos, condiciones y configuración del recurso:
# Describe de un pod
kubectl describe pod <nombre-pod> -n dev
# Qué buscar en la salida:
# - Events: mensajes del scheduler, kubelet, etc.
# - Conditions: Ready, ContainersReady, PodScheduled
# - State: Running, Waiting (con razón), Terminated (con razón)
# - Last State: si reinició, por qué terminó
Depurar con shell interactivo
# Entrar al pod con bash
kubectl exec -it <nombre-pod> -n dev -- /bin/bash
# Si no tiene bash (imagen slim)
kubectl exec -it <nombre-pod> -n dev -- /bin/sh
# Probar la API desde dentro
curl localhost:8000/health
# Ver procesos
ps aux
# Ver variables de entorno
env | grep DATABASE
Pod de debug temporal
Si necesitás herramientas que tu imagen no tiene:
# Pod con curl, nslookup, etc.
kubectl run debug --rm -it --image=nicolaka/netshoot -n dev -- /bin/bash
# Desde ahí podés:
curl http://fastapi-service/health
nslookup fastapi-service
ping fastapi-service
Eventos del namespace
# Ver todos los eventos recientes
kubectl get events -n dev --sort-by='.lastTimestamp'
# Filtrar por tipo
kubectl get events -n dev --field-selector type=Warning
Diagnóstico rápido: checklist
Cuando algo falla, seguí este orden:
# 1. ¿Los pods están corriendo?
kubectl get pods -n dev
# 2. ¿Qué dicen los logs?
kubectl logs -l app=fastapi-app -n dev --tail=20
# 3. ¿Qué dicen los eventos?
kubectl describe pod <pod-con-error> -n dev
# 4. ¿El service tiene endpoints?
kubectl get endpoints fastapi-service -n dev
# 5. ¿Las env vars están bien?
kubectl exec <pod> -n dev -- env | grep -i database
# 6. ¿La app responde desde dentro del pod?
kubectl exec <pod> -n dev -- curl -s localhost:8000/health
Errores comunes y soluciones
CrashLoopBackOff:
kubectl logs <pod> -n dev --previous— ver qué pasó antes del crash- Generalmente es un error en el código o dependencia faltante
ImagePullBackOff:
- Imagen no existe o nombre/tag incorrecto
kubectl describe pod <pod>para ver el mensaje exacto
OOMKilled:
- El container excedió su límite de memoria
- Aumentar
resources.limits.memoryen el deployment
CreateContainerConfigError:
- ConfigMap o Secret referenciado no existe
kubectl get configmap,secret -n dev
En el siguiente capítulo: flujo de desarrollo iterativo con port-forward.