Logging y Monitoreo de Contenedores

Lectura
25 min~6 min lectura

Concepto clave

El logging y monitoreo de contenedores es fundamental para mantener aplicaciones en producción. Imagina que tus contenedores son como cajas negras: necesitas saber qué sucede dentro sin abrirlas. Los logs son los mensajes que cada contenedor escribe (como un diario de a bordo), y el monitoreo te permite medir métricas como CPU, memoria y red. Sin estas herramientas, estás volando a ciegas: no sabes si un contenedor falló, por qué, o si está a punto de colapsar por falta de recursos.

En Docker, por defecto, los logs se manejan con el controlador json-file, que guarda la salida estándar (stdout) y de error (stderr) en archivos JSON. Sin embargo, en producción necesitas centralizar logs y métricas para poder analizarlos históricamente y alertar en tiempo real. Herramientas como Prometheus (para métricas) y ELK Stack (Elasticsearch, Logstash, Kibana) o Loki + Grafana son los estándares de la industria.

La analogía del mundo real: Piensa en un edificio inteligente. Los logs son los reportes de cada sensor (temperatura, humedad), y el monitoreo es el panel de control que te muestra todo en tiempo real. Sin ellos, no sabrías si un piso está en llamas hasta que sea demasiado tarde.

Cómo funciona en la práctica

Para implementar logging y monitoreo en producción, seguiremos estos pasos:

  1. Configurar el controlador de logs de Docker para que envíe logs a un sistema centralizado (por ejemplo, usando syslog o gelf).
  2. Exponer métricas desde la aplicación (por ejemplo, con una librería como prometheus_client en Python) y configurar Docker para que Prometheus pueda recolectarlas.
  3. Usar docker-compose para levantar una pila de monitoreo con Prometheus, Grafana y un recolector de logs (como Loki o Filebeat).

Ejemplo paso a paso: Supongamos que tienes una aplicación web en un contenedor. Primero, modificamos el docker-compose.yml para agregar un servicio de Loki y Grafana. Luego, instalamos el driver de logs loki (o usamos json-file con un recolector). Finalmente, configuramos Grafana para visualizar tanto logs como métricas.

Código en acción

A continuación, un ejemplo funcional de docker-compose.yml que levanta una aplicación Python, Prometheus, Grafana y Loki:

version: '3.8'

services:
  app:
    build: .
    ports:
      - "5000:5000"
    logging:
      driver: loki
      options:
        loki-url: "http://loki:3100/loki/api/v1/push"
        loki-external-labels: "job=app"

  prometheus:
    image: prom/prometheus
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"

  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin

  loki:
    image: grafana/loki
    ports:
      - "3100:3100"

Además, necesitas un archivo prometheus.yml para configurar el scraping de métricas desde la aplicación:

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'app'
    static_configs:
      - targets: ['app:5000']

Para que la aplicación exponga métricas, agrega el siguiente código Python (suponiendo Flask):

from flask import Flask
from prometheus_flask_exporter import PrometheusMetrics

app = Flask(__name__)
metrics = PrometheusMetrics(app)

@app.route('/')
def hello():
    return "Hello, World!"

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

Con esto, al ejecutar docker-compose up, tendrás logs en Loki y métricas en Prometheus, visibles en Grafana.

Errores comunes

  • No configurar el driver de logs correctamente: Usar el driver por defecto json-file en producción sin rotación puede llenar el disco. Solución: Configurar max-size y max-file en el daemon de Docker o usar un driver externo.
  • No exponer métricas desde la aplicación: Muchos asumen que Docker expone métricas automáticamente, pero solo expone las del contenedor (CPU, memoria). Las métricas de negocio (peticiones, latencia) deben ser expuestas por la app.
  • Olvidar etiquetar logs y métricas: Sin etiquetas (labels) como service, version, environment, es imposible filtrar. Solución: Usar loki-external-labels o etiquetas en Prometheus.
  • No probar la integración localmente: Configurar todo en producción sin verificar que los logs lleguen a Loki o que Grafana pueda consultar Prometheus. Solución: Probar con docker-compose en local.
  • Ignorar la rotación de logs: Incluso con drivers externos, si la app genera muchos logs, el buffer puede saturarse. Solución: Configurar límites de tamaño y políticas de retención en Loki o Elasticsearch.

Checklist de dominio

  • [ ] Configuré el driver de logs de Docker para enviar logs a un sistema centralizado (Loki, ELK, etc.).
  • [ ] Mi aplicación expone métricas en un endpoint /metrics (por ejemplo, con Prometheus client).
  • [ ] Agregué un servicio de Prometheus y Grafana en mi stack de docker-compose.
  • [ ] Configuré Grafana para conectar a Prometheus y Loki como fuentes de datos.
  • [ ] Creé un dashboard en Grafana que muestra al menos 3 métricas (CPU, memoria, peticiones por segundo) y los logs filtrados por servicio.
  • [ ] Verifiqué que los logs contengan etiquetas útiles (servicio, entorno) y que las métricas tengan labels consistentes.
  • [ ] Probé la rotación de logs y que no se llena el disco local.

Configurar una pila de monitoreo para una aplicación contenedorizada

En este ejercicio, configurarás una pila completa de logging y monitoreo para una aplicación web simple. Deberás entregar un archivo docker-compose.yml funcional que incluya la aplicación, Prometheus, Grafana y Loki. Además, deberás crear un dashboard en Grafana que muestre al menos 3 métricas (CPU, memoria, peticiones por segundo) y los logs de la aplicación.

Pasos:

  1. Crea un directorio de trabajo: mkdir monitoreo-ejercicio && cd monitoreo-ejercicio
  2. Dentro, crea una aplicación Python simple (por ejemplo, Flask) que exponga un endpoint /metrics con métricas de Prometheus. Usa la librería prometheus_flask_exporter.
  3. Crea un Dockerfile para la aplicación.
  4. Crea un archivo docker-compose.yml con los servicios: app, prometheus, grafana, loki. Configura el driver de logs de la app para que envíe logs a Loki.
  5. Crea un archivo prometheus.yml para configurar el scraping de métricas desde la app.
  6. Ejecuta docker-compose up y verifica que todo funcione.
  7. Accede a Grafana en http://localhost:3000 (usuario: admin, contraseña: admin). Agrega Prometheus y Loki como fuentes de datos.
  8. Crea un dashboard con al menos 3 paneles: uno para CPU del contenedor, uno para memoria, y uno para peticiones por segundo (puedes usar métricas de Prometheus como container_cpu_usage_seconds_total y flask_http_request_total).
  9. Agrega un panel de logs que muestre los logs de la aplicación (usando la fuente de Loki).
  10. Exporta el dashboard como JSON (opcional) y toma una captura de pantalla.

Entregable:

Sube a la plataforma un archivo ZIP que contenga:

  • docker-compose.yml
  • Dockerfile
  • app.py (código de la aplicación)
  • prometheus.yml
  • requirements.txt (si aplica)
  • Captura de pantalla del dashboard de Grafana mostrando los paneles.

Rúbrica de evaluación:

  • (20%) La aplicación expone métricas correctamente en /metrics.
  • (20%) El archivo docker-compose.yml está bien estructurado y todos los servicios se levantan sin errores.
  • (20%) Los logs de la aplicación llegan a Loki y se pueden consultar desde Grafana.
  • (20%) El dashboard muestra al menos 3 métricas relevantes y los logs.
  • (20%) La solución es reproducible: se puede ejecutar docker-compose up y todo funciona.
Pistas
  • Para exponer métricas de Flask, usa la librería prometheus_flask_exporter. Instálala con pip y agrega la línea 'metrics = PrometheusMetrics(app)' después de crear la app.
  • En el docker-compose, para el driver de logs de Loki, asegúrate de que el servicio de Loki se llame exactamente 'loki' y que el puerto 3100 esté expuesto. La URL del driver debe ser 'http://loki:3100/loki/api/v1/push'.
  • Para que Prometheus pueda scrapear la app, el target debe ser 'app:5000' (o el puerto que uses). Verifica que la app esté en la misma red de docker-compose.