Files
message_bus/reports/0001-2026-06-07-unibus-grafana-monitoring.md
T
egutierrez d43ffae3ae chore: auto-commit (17 archivos)
- reports/0001-2026-06-07-unibus-grafana-monitoring.md
- reports/0008-2026-06-07-unibus-admin-users-wired.md
- reports/0008-2026-06-07-unibus-decentralization-audit.md
- reports/0009-2026-06-07-unibus-cluster-hardening.md
- reports/0010-2026-06-07-unibus-android-native.md
- reports/0011-2026-06-07-unibus-cluster-deploy.md
- reports/0012-2026-06-07-unibus-deploy-gaps-closed.md
- reports/0013-2026-06-07-unibus-admin-panel.md
- reports/0014-2026-06-07-unibus-users-http-admin-api.md
- reports/0015-2026-06-07-unibus-web-wired.md
- ...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-08 01:57:00 +02:00

8.2 KiB
Raw Blame History

Report 0001 — Monitorización de unibus en Grafana/VictoriaMetrics

  • Fecha: 07/06/2026
  • Autor: agente (Claude Opus 4.8)
  • Ámbito: projects/fleet_monitoring/ (exporter + dashboard + deploy) + 1 función nueva en el registry (functions/infra/). Solo lectura de projects/message_bus/apps/unibus/ (endpoint /healthz + CA).
  • Estado: done (métricas de /healthz); gap declarado en métricas profundas NATS/JetStream.

Resumen

Se añadió monitorización del cluster de mensajería unibus (NATS+JetStream, 3 nodos: magnus, homer, datardos) a la stack de Grafana/VictoriaMetrics que ya corre en magnus, sin instrumentar el bus. Un nuevo daemon unibus_exporter sondea el endpoint público de salud de cada nodo (GET https://<nodo>:8470/healthz, TLS verificado con la CA del cluster) en un bucle de 15 s, traduce la respuesta a métricas Prometheus y las empuja a la VictoriaMetrics local de magnus. Un dashboard dedicado las visualiza. No se tocó nada del código de unibus ni la stack existente (Caddy/Gitea/VM/Grafana/fleet-agent intactos).

Qué métricas se recolectan y cómo

El /healthz de cada nodo devuelve, verificado en producción:

{"posture":{"enforce":true,"acl":true,"tls":true,"cluster":true,"store":"kv"},"status":"ok"}

De ahí se derivan estas series (labels node e instance = nombre del nodo, label común job=unibus_exporter):

Serie Origen Significado
unibus_up exporter 1 si el nodo respondió /healthz, 0 si el GET/parseo falló
unibus_status_ok healthz status 1 si status=="ok"
unibus_posture_enforce healthz posture.enforce enforcement de auth (1/0)
unibus_posture_acl healthz posture.acl ACL de subjects (1/0)
unibus_posture_tls healthz posture.tls TLS del transporte (1/0)
unibus_posture_cluster healthz posture.cluster modo cluster activo (1/0)
unibus_store_kv healthz posture.store 1 si el store es kv (JetStream KV)
unibus_scrape_error exporter 1 si el scrape de ese nodo falló
unibus_scrape_duration_seconds exporter latencia del GET /healthz
unibus_cluster_size exporter (config) nº de nodos configurados (los vivos = sum(unibus_up))

unibus_up=0 lo emite el exporter (no el parser) cuando el GET falla, para que un nodo caído sea visible en Grafana, no simplemente ausente.

Componentes entregados

Función del registry — parse_unibus_health_go_infra

  • functions/infra/parse_unibus_health.go + .md + _test.go (grupo fleet-metrics, tags unibus).
  • func ParseUnibusHealth(node string, body []byte) ([]PromSample, error) — pura de transformación (clasificada impure solo por el error de unmarshal). Tests golden/edge/error.
  • Nota de proceso: el prompt pedía delegar esta función a fn-constructor, pero ese subagent_type no existe en este entorno. Tras confirmar con el usuario ("crear en el registry"), se creó a mano siguiendo el flujo (archivos + tests + fn index).

App — unibus_exporter (sub-repo Gitea propio)

  • projects/fleet_monitoring/apps/unibus_exporter/: main.go, config.go, unibus.example.json, systemd/unibus-exporter.service, app.md, .gitignore.
  • Compone parse_unibus_health + format_prom_exposition + push_prom_remote del registry (no reescribe push ni formato). Config JSON; secretos (CA, basic-auth) fuera de argv. Verifica TLS siempre contra la CA del cluster (sin InsecureSkipVerify).
  • git init -b master + commit inicial hecho (apps/* está gitignored en el project; sin sub-repo el código se perdería). Falta crear el repo Gitea remoto: lo hará /full-git-push.

Dashboard — unibus-cluster.json

  • projects/fleet_monitoring/hub/dashboards/unibus-cluster.json (formato de los fleet-*.json, datasource victoriametrics, carpeta Fleet, uid unibus-cluster, 9 paneles): nodos up, cluster size, nodos caídos, posture homogénea segura, up/down por nodo, matriz de posture por nodo (state-timeline enforce/acl/tls/cluster/store-kv × 3 nodos), latencia de scrape y tabla de estado por nodo. Panel "Meta-leader" preparado (muestra n/d sin métricas NATS).

Deploy — deploy_unibus_exporter.sh

  • projects/fleet_monitoring/hub/deploy_unibus_exporter.sh: compila el binario linux/amd64, sube binario + CA del cluster a magnus (/opt/unibus-exporter, /etc/unibus-exporter/ca.crt chmod 600 la config) e instala el servicio systemd apuntando a http://127.0.0.1:8428/... (VM local, sin auth porque corre en el propio hub).

Verificación (evidencia ejecutable)

1. Acceso y healthz de los 3 nodos (CA del cluster por path):

$ curl -s --cacert .../deploy/tls/ca.crt https://135.125.201.30:8470/healthz
{"posture":{"enforce":true,"acl":true,"tls":true,"cluster":true,"store":"kv"},"status":"ok"}
   (idéntico en homer 141.94.69.66 y datardos 51.91.100.142)

2. Tests de la función:

$ go test -tags fts5 -run ParseUnibusHealth ./functions/infra/
ok  	fn-registry/functions/infra	0.004s
$ ./fn index   # → "Indexed 1450 functions ..."; ./fn show parse_unibus_health_go_infra → OK

3. Exporter build + scrape/push único de prueba (local → VM):

$ ./unibus_exporter -config <test> -once
unibus_exporter starting: nodes=3 hub="https://metrics-…/api/v1/import/prometheus" interval=15s
pushed 28 samples for 3 nodes        # 1 cluster_size + 3 nodos × 9 series

4. Daemon systemd en magnus:

$ systemctl is-active unibus-exporter   → active
$ systemctl is-enabled unibus-exporter  → enabled
$ journalctl -u unibus-exporter         → "pushed 28 samples for 3 nodes"

5. Series en VictoriaMetrics (magnus, 127.0.0.1:8428):

sum(unibus_up)        = 3
unibus_cluster_size   = 3
count(unibus_up==1)   = 3
unibus_posture_enforce: magnus=1 homer=1 datardos=1  (job=unibus_exporter)
unibus_store_kv:        magnus=1 homer=1 datardos=1
unibus_scrape_duration_seconds: magnus≈4ms homer≈32ms datardos≈19ms

6. Dashboard en Grafana (visto en el navegador):

  • https://grafana-…/d/unibus-cluster — carpeta Fleet, 9 paneles renderizando datos reales.
  • Nodos up: 3 · Cluster size: 3 · Nodos caídos: 0 · Posture homogénea segura: OK (enforce+acl+tls+cluster+kv) · matriz de posture: 15 celdas en verde · tabla de estado por nodo con ✓ en up/enforce/acl/tls.
  • Captura: unibus-grafana-monitoring-dashboard.png (junto a este report).
  • API: GET /api/dashboards/uid/unibus-clusterdashboard CARGADO: unibus — Cluster | folder: Fleet | paneles: 9.

Gaps / pendientes

  • Métricas profundas NATS/JetStream (msgs/s, conexiones, KV bucket msgs, RAFT leader por stream, NRestarts) — NO incluidas, gap recomendado. La vía es el monitoring embebido de NATS (puerto 8222), que se confirmó cerrado en los 3 nodos en producción (curl 127.0.0.1:8222/varz → CLOSED en magnus/homer/dd). Activarlo (bindeado a 127.0.0.1 + scrape local, o sacar /jsz por SSH) exige tocar la config/unit de los nodos del cluster, que además están siendo trabajados por otros agentes ahora mismo. Se decidió no forzarlo por riesgo en producción. Para abordarlo después: añadir UNIBUS_NATS_DEBUG/equivalente bindeado a loopback en cada nodo (cambio aditivo, coordinado con unibus/deploy/cluster/), un scrape local del /jsz+/varz y nuevas series unibus_jsz_*. El panel "Meta-leader" del dashboard ya está preparado para cuando exista unibus_meta_leader.
  • unibus_cluster_size refleja el nº de nodos configurados en el exporter (3), no un recuento que el bus reporte (healthz no lo expone). Los nodos vivos se ven con sum(unibus_up).
  • Commit en el repo padre fn_registry sin pushear (a propósito): la función nueva quedó commiteada en local (82f1f1bd, master ahead 1) pero no se hizo push del padre, para respetar el aislamiento pedido. El humano debería revisarlo y pushearlo (junto con fn index para regenerar registry.db, que está gitignored).
  • Repo Gitea del exporter: apps/unibus_exporter tiene su git init + commit local pero aún no tiene remoto en Gitea; /full-git-push lo creará (dataforge/unibus_exporter).
  • Vida útil (DoD capa 3): validado funcionalmente hoy; falta la ventana de uso real ≥7 días.