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 yServidorCaido(up == 0por 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 % demax_connections, transacción > 5 min, deadlocks, cache hit < 90 %,PgBouncerDown, clientes esperando pool, espera > 5 s, conexiones cliente > 80 % demax_client_conn.stack_alertas(rules/stack_alertas.yml): recarga de config fallida, notificaciones de Alertmanager fallando, disco de devops-vm < 15 %, Loki rechazando pushes yRemoteWriteSinDatos(absent()por job remoto). La caída de cada componente del stack la cubreServidorCaidosobre el jobstack. 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 (sinmaxmemoryen 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()parasap-enrichment-workereintegrador-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 yContenedorCriticoAusenteconabsent()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) yProbeHttpError5xx. Laseverityviene del target. Reemplaza al grupoblackboxcomentado 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¶
- Cada 15 s Prometheus scrapea el node_exporter de la VM (
cloud-nodes, basic auth) y guardanode_filesystem_avail_bytesynode_filesystem_size_bytes. - Cada 15 s evalúa
rules/alerts_rules.yml. Cuando la partición supera el 90 % de uso,DiscoCasiLlenopasa a pending; si se sostiene 10 min pasa a firing (al 95 % por 5 min, ademásDiscoCritico, severidad critical). - Prometheus envía la alerta a
alertmanager:9093. - Alertmanager la agrupa por
alertname+instance, espera 30 s (group_wait) por más alertas del mismo grupo y la enruta al receptorequipo-infra. - Se envía un correo por SMTP a
alertas-infra@compulandia.com.pycon elsummaryy ladescriptionde la regla (partición y porcentaje). Mientras siga activa, se repite cada 1 h. - Cuando el uso baja del umbral, la alerta se resuelve y llega un correo de
resolved (
send_resolved: true). - En paralelo, Grafana consulta el mismo Prometheus para los tableros; no participa del envío de la alerta.