Saltar a contenido

Arquitectura — observability-stack

Visión general

observability-stack es el punto central de monitoreo de Compulandia. Un único docker-compose.yml levanta Prometheus, Alertmanager, Grafana, Loki y Blackbox Exporter en la VM devops de la nube. Prometheus scrapea directamente los servidores de la nube (node_exporter y mysqld_exporter) y lanza probes por Blackbox a endpoints públicos e internos; las redes remotas (red SAP y red de la oficina) empujan sus métricas y logs con colectores Grafana Alloy / Grafana Agent vía remote_write hacia los endpoints públicos prometheus.compulandia.com.py y loki.compulandia.com.py. Las alertas salen por correo a alertas-infra@compulandia.com.py.

Fuente de verdad: los archivos de configuración del repo https://github.com/CompulandiaTI/observability-stack. Todo lo que sigue sale de ellos; lo que no está ahí se marca como pendiente.

Componentes

Componente Responsabilidad Tecnología / imagen Puerto host
Prometheus Scrape de targets, evaluación de reglas, receptor de remote_write, TSDB con retención 30 días prom/prometheus:latest, flags --web.enable-lifecycle, --web.enable-remote-write-receiver, --web.enable-admin-api 9090
Alertmanager Agrupa y enruta alertas; receptor activo: correo (SMTP Gmail) prom/alertmanager:latest 9093
Grafana Tableros y exploración; datasources y dashboards provisionados desde el repo grafana/grafana:latest, volumen grafana_data 3200 → 3000
Loki Almacén de logs single-binary sobre filesystem, esquema TSDB v13, retención 30 días, compactor con retención activa grafana/loki:3.0.0, volumen loki_data 3100
Blackbox Exporter Probes HTTP / TCP / ICMP a pedido de Prometheus (/probe) prom/blackbox-exporter:latest, capability NET_RAW 9115

Todos comparten la red Docker monitoring. Configuración montada desde el repo: prometheus.yml, rules/, alertmanager/, blackbox/, loki/ y grafana/provisioning/.

Qué se monitorea

Scrape directo desde Prometheus (prometheus.yml):

Job Qué Cómo
prometheus El propio Prometheus localhost:9090
cloud-nodes node_exporter de las VMs integrador, ecommerce-srv, wp-server-1, devops-vm, postgres-db-vm y redis-srv-vm HTTP con basic auth, puerto 9100
cloud-mysql mysqld_exporter de integrador y wordpress HTTP con basic auth, puerto 9104
cloud-postgres postgres_exporter de postgres-db-vm (PostgreSQL 16, 7 bases) HTTP sin auth (pendiente alinear), puerto 9187
cloud-pgbouncer pgbouncer_exporter de postgres-db-vm HTTP sin auth (pendiente alinear), puerto 9127
stack Métricas propias de Alertmanager, Loki, Grafana y Blackbox Exporter Nombres de servicio en la red Docker monitoring
cloud-redis redis_exporter de medusa_redis (ecommerce-srv, en la red de Medusa), del Redis de redis-srv-vm (Chatwoot) y del Redis de n8n (devops-vm, cola Bull) HTTP sin auth, puerto 9121, ver redis_exporter/README.md
cloud-kafka kafka_exporter junto al broker de ecommerce-srv (red del compose de Kafka, listener kafka:29092): brokers, offsets por topic, lag y miembros por consumer group HTTP sin auth, puerto 9308, ver kafka_exporter/README.md
cloud-n8n Métricas propias de n8n main en devops-vm (N8N_METRICS=true): ejecuciones por estado y modo, líder, event loop, heap. El worker también expone /metrics pero sin puerto publicado HTTP sin auth, puerto 5678 publicado en la VM
cadvisor Métricas por contenedor de devops-vm (cadvisor:8080 en la red monitoring), ecommerce-srv (10.158.0.19:9101) y redis-srv-vm (10.158.0.23:9101). Descarta cgroups sin contenedor HTTP sin auth, ver ADR-0004
blackbox_http Storefront (shop, migrará a www), CMS y Medusa por sus /api/health, integrador, SAP Service Layer publicado, n8n /healthz y el sitio WordPress módulo http_2xx (solo 2xx; envía headers de Cloudflare Access)
blackbox_http_404 PC Manager (pcm) módulo http_2xx_or_404 (la raíz responde 404 y se considera up)
blackbox_http_internal Alertmanager de devops-vm, los tres /health del ecommerce y el /healthz/readiness de n8n por IP interna (separa caída de app de caída de túnel) módulo http_2xx_insecure
blackbox_tcp Puerto de la app Medusa módulo tcp_connect
blackbox_icmp Ping a las 4 VMs de la nube (devops, ecommerce, integrador, wordpress) módulo icmp

Recibido por remote_write (colectores fuera de esta VM; el repo guarda sus configs de referencia):

Colector Config de referencia Qué envía
Grafana Alloy en la red SAP grafana-alloy/config.alloy node_exporter Linux y Windows, sap_host_exporter, exporter de HANA DB, probes TCP a HANA / Service Layer / RDP y HTTP a backend y frontend del OMS. Además envía a Loki los logs PM2 del backend y frontend del OMS, con etiquetas level y context extraídas del formato NestJS.
Grafana Agent en la red de la oficina (red_aviadores) agent-config-grafana/grafana-agent.yaml node_exporter y mysqld_exporter de los servidores dev y docker (staging dado de baja el 2026-09-04), más integraciones node_exporter y blackbox del propio agente.

Los tableros de red (mktxp_* de MikroTik, smokeping_*, speedtest_*) y el de backups SAP HANA (sap_backup_*) consumen métricas cuyos exporters no están en este repo: llegan por remote_write desde colectores mantenidos aparte. Pendiente: documentar dónde corren esos exporters.

Reglas de alerta

Archivo rules/alerts_rules.yml:

  • servidor_alertas: CPU (warning > 95 %, critical > 99 %), memoria (> 95 %), disco (> 90 % warning, > 95 % critical, predicción de llenado en 24 h), load15 por CPU > 2 y ServidorCaido (up == 0 por 1 min).
  • mysql_alertas: MySQLDown, conexiones > 80 % del máximo, más de 1 query lenta/s.
  • postgres_alertas (rules/postgres_alertas.yml): PostgresDown, exporter con error, conexiones > 80 % de max_connections, transacción > 5 min, deadlocks, cache hit < 90 %, PgBouncerDown, clientes esperando pool, espera > 5 s, conexiones cliente > 80 % de max_client_conn.
  • stack_alertas (rules/stack_alertas.yml): recarga de config fallida, notificaciones de Alertmanager fallando, disco de devops-vm < 15 %, Loki rechazando pushes y RemoteWriteSinDatos (absent() por job remoto). La caída de cada componente del stack la cubre ServidorCaido sobre el job stack. Sin Watchdog (decisión 2026-09-04): no hay alerta de latido hacia un receptor externo, así que la caída simultánea de Prometheus y Alertmanager, de la VM o del SMTP no avisa a nadie. Riesgo aceptado; revisar cuando exista un receptor fuera de devops-vm.
  • redis_alertas (rules/redis_alertas.yml): RedisDown, memoria alta (sin maxmemory en ninguno: umbral absoluto por instancia), conexiones rechazadas y snapshot RDB con más de 24 h.
  • kafka_alertas (rules/kafka_alertas.yml): KafkaDown, partición sin líder, lag por consumer group (> 1000 por 15 min, > 10.000 por 30 min), grupo sin miembros, absent() para sap-enrichment-worker e integrador-sap-items, y mensajes nuevos en los topics DLQ. Sin alerta de subreplicación (un broker, RF 1).
  • n8n_alertas (rules/n8n_alertas.yml): main sin rol de líder, workflow con más del 20 % de fallos (mínimo 3, con nombre), respaldo global de fallos, ejecuciones crashed, event loop p99 > 1 s, worker ausente (cAdvisor) y cola con más de 50 esperando.
  • contenedores_alertas (rules/contenedores_alertas.yml): contenedor reiniciando (2+ arranques en 30 min), OOM kill, memoria > 90 % del límite, CPU > 2 núcleos por 15 min y ContenedorCriticoAusente con absent() para Medusa backend, storefront, Payload, Kafka y los dos Redis.
  • sap_backup_alertas: backup completo del día ausente o no subido a GCS, días de retención en GCS < 7, lifecycle del bucket sin depurar, logs incrementales sin correr (silenciado de 00 a 08 hora Paraguay), fallos repetidos de upload y fallo del script de retención local.
  • blackbox_alertas (rules/blackbox_alertas.yml, desde 2026-09-04): ProbeCaido (3 min), HostSinRespuestaICMP (2 min), ProbeLatenciaAlta (> 5 s por 10 min) y ProbeHttpError5xx. La severity viene del target. Reemplaza al grupo blackbox comentado el 2026-04-06 por ruido; los umbrales nuevos salen de 7 días de historia. Sin alerta de certificados.

Enrutamiento (alertmanager.yml): todo va al receptor equipo-infra (correo a alertas-infra@compulandia.com.py, send_resolved: true), agrupado por alertname + instance, group_wait 30 s, repeat_interval 1 h. Existe un receptor critico-slack definido pero ninguna ruta lo usa y su webhook es un placeholder (ver runbook, falla 1).

Tableros provisionados

Archivo Título uid
oms-incidents-dashboard.json OMS + SAP — Incidentes y Correlación oms-sap-incidentes
integrador-incidents-dashboards.json Integrador — Logs Laravel integrador-logs-laravel
integrador-logs-search.json Integrador — Logs Search integrador-logs-search
network-monitoring.json Red — MikroTik RB5009 compulandia-network
network-health-check.json Red — Salud y Diagnóstico MikroTik compulandia-network-health
sap-backup.json SAP HANA Backup Monitor sap-hana-backup
postgres-db.json PostgreSQL — postgres-db-vm postgres-db
stack-observabilidad.json Stack de observabilidad — devops-vm stack-observabilidad
ecommerce-stack.json Ecommerce — Stack completo (vista de operación: disponibilidad, pipeline Kafka, n8n, datos, contenedores). Enlaza a los de detalle ecommerce-stack
n8n.json n8n — devops-vm n8n
kafka.json Kafka — ecommerce-srv kafka
redis.json Redis — medusa_redis y redis-srv-vm redis
ecommerce-app.json Ecommerce — Medusa, storefront y Payload ecommerce-app
blackbox-endpoints.json Blackbox — Monitoreo Completo de Endpoints (exportado de la UI el 2026-09-04) blackbox-compulandia-v2
contenedores.json Contenedores — cAdvisor contenedores

uid duplicado (corregido 2026-09-03)

Los dos tableros del Integrador compartían el uid integrador-logs-laravel. Grafana lo rechazaba y, peor, dejaba al proveedor de provisioning sin permiso de escritura para todos los tableros nuevos. Se cambió el uid del buscador a integrador-logs-search.

Dependencias externas

Dependencia Uso ¿Qué pasa si no está?
Docker Engine + Compose en la VM devops Ejecuta los 5 contenedores Nada funciona; no hay HA.
Cloudflare Tunnel / Access (prometheus.compulandia.com.py, loki.compulandia.com.py) Entrada pública para el remote_write de los colectores remotos, protegida con service token y basic auth (la capa que valida esas credenciales está fuera de este repo: pendiente de documentar) Las series de la red SAP y de la oficina dejan de llegar. Los agentes retienen en su WAL un tiempo y luego descartan. No se dispara ninguna alerta: no hay regla de ausencia de datos; los tableros quedan congelados.
Cloudflare Access sobre los sitios probeados Blackbox envía headers CF-Access-Client-Id/Secret en el módulo http_2xx Si el service token vence o se rota, los probes HTTP dan probe_success = 0 aunque el sitio esté bien (ver runbook, falla 2).
node_exporter / mysqld_exporter en las VMs de la nube Targets de cloud-nodes y cloud-mysql Dispara ServidorCaido / MySQLDown tras 1 min.
SMTP de Google (smtp.gmail.com:587) Único canal de notificación Las alertas quedan visibles solo en :9093; nadie recibe correo.
Colectores Grafana Alloy / Agent en cada red remota Producen las métricas y logs de SAP, OMS y oficina Igual que la caída del túnel: datos ausentes sin alerta.
Exporters externos (mktxp, smokeping, speedtest, script sap_backup) Alimentan los tableros de red y de backups SAP Tableros sin datos. Las alertas SAPBackup* que comparan con == 0 no se disparan por ausencia de la métrica.
GitHub Actions + secrets SERVER_HOST, SERVER_USER, SSH_KEY Deploy automático al hacer push a main No se despliega solo; se aplica a mano (runbook).

Diagrama

flowchart LR
    subgraph remotas["Redes remotas (SAP, oficina)"]
        EXR["Exporters: node, mysqld, sap_host, hanadb, mktxp..."]
        PM2["Logs PM2 del OMS"]
        AL["Grafana Alloy / Grafana Agent"]
        EXR --> AL
        PM2 --> AL
    end

    subgraph nube["VMs de la nube"]
        NE["node_exporter / mysqld_exporter"]
        PUB["Endpoints públicos e internos"]
    end

    subgraph stack["observability-stack · VM devops"]
        P["Prometheus<br/>TSDB 30d"]
        AM["Alertmanager"]
        G["Grafana :3200"]
        L["Loki 3.0<br/>filesystem 30d"]
        BB["Blackbox Exporter"]
    end

    AL -->|"remote_write HTTPS<br/>Cloudflare Access + basic auth"| P
    AL -->|"push HTTPS"| L
    P -->|"scrape 15s, basic auth"| NE
    P -->|"/probe"| BB
    BB -->|"HTTP / TCP / ICMP"| PUB
    P -->|"alertas"| AM
    AM -->|"SMTP"| MAIL["alertas-infra@compulandia.com.py"]
    G -->|"PromQL"| P
    G -->|"LogQL"| L

Flujo principal: una alerta de disco lleno en una VM de la nube

  1. Cada 15 s Prometheus scrapea el node_exporter de la VM (cloud-nodes, basic auth) y guarda node_filesystem_avail_bytes y node_filesystem_size_bytes.
  2. Cada 15 s evalúa rules/alerts_rules.yml. Cuando la partición supera el 90 % de uso, DiscoCasiLleno pasa a pending; si se sostiene 10 min pasa a firing (al 95 % por 5 min, además DiscoCritico, severidad critical).
  3. Prometheus envía la alerta a alertmanager:9093.
  4. Alertmanager la agrupa por alertname + instance, espera 30 s (group_wait) por más alertas del mismo grupo y la enruta al receptor equipo-infra.
  5. Se envía un correo por SMTP a alertas-infra@compulandia.com.py con el summary y la description de la regla (partición y porcentaje). Mientras siga activa, se repite cada 1 h.
  6. Cuando el uso baja del umbral, la alerta se resuelve y llega un correo de resolved (send_resolved: true).
  7. En paralelo, Grafana consulta el mismo Prometheus para los tableros; no participa del envío de la alerta.