#Diagnosticar es reducir incertidumbre
"No funciona Docker" puede significar que el proceso no arranca, el puerto no está publicado, el proxy no resuelve el servicio, la base no tiene una tabla o la máquina no tiene espacio. Cambiar muchas cosas a la vez destruye la evidencia que permitiría distinguirlos.
Empieza por el síntoma que ve el usuario, cuándo empezó y qué cambió. Conserva la revisión desplegada y un intervalo temporal. Después recorre el camino de la petición. Si el proxy devuelve 502, ya sabes que llegaste al proxy; investigar primero el DNS público desde cero probablemente no es lo más útil.
#Un panel mínimo sin instalar cinco plataformas
Para una aplicación pequeña necesitas tres tipos de información: estado actual, historial de sucesos y tendencia de recursos. Docker ofrece un primer nivel:
docker compose ps -a
docker compose logs --since 15m --tail 200 api proxy
docker stats --no-stream
docker system df
ps -a incluye tareas fallidas o finalizadas. --since acota logs y --tail evita volcar megabytes. stats ofrece una fotografía de CPU, memoria y otras métricas. system df muestra espacio atribuible a recursos de Docker; el espacio libre del sistema de archivos se consulta aparte, por ejemplo con df -h en Linux.
Una fotografía no describe una tendencia: un pico de memoria pudo desaparecer tras matar el proceso. Para operación continuada, captura métricas de forma periódica o usa un sistema de monitorización. Añádelo cuando necesites histórico y alertas; primero define qué decisión tomará cada métrica.
| Señal | Pregunta que responde | Acción útil |
|---|---|---|
| Tasa de HTTP 5xx | ¿Cuántas peticiones fallan? | Separar fallos de proxy, API y dependencia |
| Latencia p95 | ¿Qué tarda una petición lenta habitual? | Buscar saturación, consultas o red |
| Memoria frente al límite | ¿Queda margen antes de OOM? | Ajustar carga, workers o límite con evidencia |
| Espacio libre y crecimiento | ¿Cuándo se llenará el disco? | Rotar logs, revisar datos y capacidad |
| Edad del último backup válido | ¿Estamos cumpliendo recuperación? | Investigar el trabajo de copia y su validación |
| Reinicios inesperados | ¿El proceso se mantiene estable? | Inspeccionar salida, OOM y eventos |
p95 significa que el 95 % de las mediciones quedaron por debajo de ese valor. Es más informativo que un promedio cuando unas pocas peticiones sufren mucho. Define ventana y cantidad de tráfico: con tres peticiones un percentil no es una descripción estable.
#Logs que ayudan sin llenar el disco
La aplicación escribe en stdout/stderr y Docker recoge esas salidas mediante un driver. Evita archivos internos que desaparezcan al reemplazar el contenedor o crezcan sin rotación.
services:
api:
logging:
driver: local
options:
max-size: "10m"
max-file: "3"
local soporta rotación y lectura por docker logs. Las opciones se escriben como cadenas. La retención de tres archivos de hasta ese tamaño es un límite operativo aproximado del flujo de logs, no una política de auditoría empresarial. Si necesitas meses, envía los eventos a un destino apropiado con retención y control de acceso.
Cada evento debería incluir hora, servicio, nivel y un mensaje útil. Un identificador de petición permite seguir el mismo recorrido en proxy y API. No metas cuerpo completo, contraseña o cabecera Authorization por defecto: la observabilidad puede convertirse en una copia de datos sensibles.
Puedes usar JSON para estructurar eventos, por ejemplo {"event":"db_unavailable","request_id":"…"}. El valor está en poder filtrar y correlacionar; JSON sin un esquema estable tampoco explica la causa. Registra tipos de error y duración, y conserva detalles internos solo donde corresponda.
#Investigar un contenedor que se reinicia
Obtén el identificador del servicio y consulta estado:
api_id=$(docker compose ps -q api)
docker inspect "$api_id" --format \
'exit={{.State.ExitCode}} oom={{.State.OOMKilled}} restarts={{.RestartCount}} error={{.State.Error}}'
docker compose logs --tail 100 api
api_id es una variable de shell que contiene el identificador obtenido, no un nombre mágico. Si está vacía, el servicio quizá nunca llegó a crearse: vuelve a compose ps -a y a la salida del comando que lo creó.
| Evidencia | Interpretación plausible | Qué comprobar después |
|---|---|---|
| Código 0, servidor desaparecido | Comando puntual o proceso que salió voluntariamente | CMD, entrypoint y logs de cierre |
| Código 1 con traceback | Error de aplicación o configuración | Primera excepción, variables requeridas y archivos |
137 con OOMKilled=true |
Proceso matado por memoria | Límite, workers y evolución del consumo |
| 137 sin OOM | Puede haber parada forzada | Señales, eventos y presupuesto de cierre |
| Healthy pero peticiones fallan | Prueba de salud insuficiente o camino distinto | Endpoint real, permisos y proxy |
Antes de subir el límite, compara el cambio con la versión anterior: ¿añadiste workers? ¿se carga un modelo grande por proceso? ¿un endpoint acumula todos los resultados en memoria? El contenedor es el lugar donde ves el síntoma, pero la causa puede estar en el código o los datos.
#Investigar un 502 sin instalar herramientas dentro de la API
Primero consulta logs del proxy. Después prueba una petición desde la red que comparte con la API. En el proyecto, puedes ejecutar un cliente temporal usando la configuración de la API:
docker compose run --rm --no-deps api python -c \
'import urllib.request; print(urllib.request.urlopen("http://api:8000/health/ready", timeout=5).status)'
El comando crea una instancia puntual de la imagen de API pero reemplaza su arranque por un cliente HTTP. Se conecta a api, el servicio existente, no a sí mismo. Comparte sus redes sin publicar puertos. Puede leer la misma configuración y secretos porque hereda la definición del servicio: úsalo como operación administrativa, no como acceso para cualquier usuario.
Si responde 200, el camino API+base funciona desde esa red; revisa upstream y configuración del proxy. Si devuelve 503, la API atiende HTTP pero no está lista. Si no resuelve api, revisa redes. Si rechaza conexión, revisa escucha y proceso. Cada respuesta elimina hipótesis.
#Disco lleno: recuperar espacio sin borrar la base
docker system prune -a --volumes no es una receta general para una incidencia. Puede eliminar recursos que planeabas reutilizar y aumenta el riesgo de pérdida de datos. Primero identifica qué creció: logs, capas, caché de construcción, volúmenes o archivos fuera de Docker.
docker system df -v
docker volume ls
df -h
Una caché de construcción reproducible puede ser candidata a limpieza dirigida. Un volumen de base no lo es por el hecho de que ahora no haya ningún contenedor usándolo: tal vez la aplicación está parada durante una reparación. Comprueba propietario funcional, backups y recursos concretos antes de eliminar.
Si el problema son logs sin rotación, configura el driver y recrea los contenedores correspondientes; cambiar la configuración predeterminada del motor no reconfigura automáticamente cada contenedor existente. Si son datos legítimos, necesitas capacidad, retención o una política de archivo, no borrar imágenes al azar cada semana.
#Un ensayo de recuperación que merece llamarse ensayo
En un entorno de prueba, guarda un dato identificable, detén la base, observa respuesta y tiempo de espera, arráncala y comprueba que la API vuelve a atender. Después recrea la API y comprueba que el dato sigue ahí. Por último, restaura una copia en otra instancia y consulta con el rol de aplicación.
Son tres fallos diferentes: dependencia temporalmente caída, cómputo reemplazado y almacenamiento perdido. Si solo pruebas restart api, no has comprobado los otros dos. Anota lo que esperabas, lo que ocurrió y el tiempo que tardaste.
Un procedimiento de incidente útil cabe en pasos concretos: identificar proyecto y versión; capturar estado y logs; reducir el impacto; aplicar la recuperación más pequeña compatible; verificar desde la entrada pública; registrar la causa y la prevención. Evita que la primera acción sea destruir el entorno que contiene la evidencia.
#Elegir alertas que conducen a una acción
Una alerta debería indicar servicio, síntoma, impacto y dónde mirar. "CPU 80 % durante un segundo" suele ser ruido. "Errores 5xx sostenidos por encima del objetivo durante cinco minutos con tráfico suficiente" describe algo que merece investigar. Ajusta umbrales a tu uso real y define quién los recibe.
Monitoriza también desde fuera del host: si la máquina se apaga, su monitor interno puede dejar de avisar. Una comprobación externa del endpoint público detecta fallos de dominio, certificado y conectividad que una petición interna no ve. No necesita ser una plataforma enorme; necesita ser independiente del componente que vigila.
#Referencias para profundizar
- Drivers de logs: retención, configuración y cambio de contenedores.
- Driver
local: rotación y opciones. docker stats: métricas y alcance.docker events: eventos de creación, parada y salud.