Capítulo 6: Logs y Debugging

Por: Artiko
kuberneteslogsdebuggingkubectlfastapi

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:

ImagePullBackOff:

OOMKilled:

CreateContainerConfigError:

En el siguiente capítulo: flujo de desarrollo iterativo con port-forward.