--- name: unibus_exporter lang: go domain: infra version: 0.1.0 description: "Exporter del cluster de mensajería unibus: sondea el /healthz de cada nodo por TLS (CA del cluster) en un bucle y empuja a VictoriaMetrics métricas de estado del cluster y posture (up/down, enforce/acl/tls/cluster, store-kv, cluster_size) sin instrumentar el bus." tags: [fleet-metrics, unibus, monitoring, daemon] uses_functions: - parse_unibus_health_go_infra - format_prom_exposition_go_infra - push_prom_remote_go_infra uses_types: - PromSample_go_infra framework: "" entry_point: "main.go" dir_path: "projects/fleet_monitoring/apps/unibus_exporter" repo_url: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/dataforge/unibus_exporter" --- # unibus_exporter Daemon que monitoriza el cluster de mensajería **unibus** (NATS + JetStream, desplegado como 3 nodos: magnus, homer, datardos) y lo hace visible en Grafana junto al resto de la flota. Es parte del project `fleet_monitoring`. No instrumenta el bus: solo **lee** el endpoint público de salud de cada nodo (`GET https://:8470/healthz`, verificado con la CA del cluster) y traduce su respuesta a métricas Prometheus que empuja a VictoriaMetrics. Así un único exporter (corriendo en magnus, que alcanza los 3 nodos por IP pública) cubre todo el cluster sin tocar la configuración de los nodos. ## Qué hace Compone tres funciones del registry (grupo `fleet-metrics`), no reimplementa nada: 1. `parse_unibus_health_go_infra` — convierte el JSON de `/healthz` de un nodo en `[]PromSample` con labels `node`/`instance`. 2. `format_prom_exposition_go_infra` — serializa los samples a texto formato Prometheus exposition. 3. `push_prom_remote_go_infra` — hace POST del texto a VictoriaMetrics, añadiendo la label común `job=unibus_exporter` vía `extra_label`. Como un solo exporter scrapea varios nodos, las labels `node` e `instance` se fijan **por serie** (en el parser) y no vía `extra_label`, que aplicaría un único valor a todo el lote. ## Métricas que produce | Serie | Labels | Significado | |---|---|---| | `unibus_up` | node, instance | 1 si el nodo respondió `/healthz`, 0 si falló el GET/parseo | | `unibus_status_ok` | node, instance | 1 si `status=="ok"` | | `unibus_posture_enforce` | node, instance | posture: enforcement de auth (1/0) | | `unibus_posture_acl` | node, instance | posture: ACL de subjects (1/0) | | `unibus_posture_tls` | node, instance | posture: TLS del transporte (1/0) | | `unibus_posture_cluster` | node, instance | posture: modo cluster activo (1/0) | | `unibus_store_kv` | node, instance | 1 si el backend de store es `kv` (JetStream KV) | | `unibus_scrape_error` | node, instance | 1 si el scrape de ese nodo falló | | `unibus_scrape_duration_seconds` | node, instance | latencia del GET `/healthz` | | `unibus_cluster_size` | (global) | número de nodos configurados (los vivos = `sum(unibus_up)`) | ## Por qué no lleva el tag `service` Es un daemon, pero igual que `metrics_agent` su liveness no se vigila con un health check propio por SSH (modelo `service:`/`services_monitor`): si el exporter cae, las series `unibus_*` se vuelven stale y eso es la señal. Por eso se etiqueta `daemon`. ## Configuración Config por archivo JSON (`-config`). Campos en `unibus.example.json`: | campo | descripción | default | |---|---|---| | `nodes[]` | lista de `{name, url}` por nodo (url = `/healthz` completo) | — (obligatorio) | | `ca_cert_path` | PEM de la CA del cluster unibus, para verificar el TLS de cada nodo | — (obligatorio) | | `hub_url` | endpoint de ingesta de VictoriaMetrics (`…/api/v1/import/prometheus`) | — (obligatorio) | | `user` / `pass` | basic-auth del hub (vacío si el hub es local sin auth) | "" | | `interval_sec` | periodo de scrape+push en segundos | 15 | | `timeout_sec` | timeout del GET `/healthz` por nodo | 8 | | `labels` | labels comunes añadidas vía `extra_label` | `{"job":"unibus_exporter"}` | Overrides por entorno: `UNIBUS_HUB_URL`, `UNIBUS_USER`, `UNIBUS_PASS`, `UNIBUS_CA_CERT`, `UNIBUS_INTERVAL`. Los secretos viven solo en el archivo de config (chmod 600), nunca en argv. ## Ejemplo ```bash cd projects/fleet_monitoring/apps/unibus_exporter go build -o unibus_exporter . # Scrape+push único de prueba (lee config y sale) ./unibus_exporter -config /etc/unibus-exporter/unibus.json -once # Bucle continuo (lo que hace el servicio systemd) ./unibus_exporter -config /etc/unibus-exporter/unibus.json ``` ## Cuando usarla Despliégalo en una máquina que alcance los 3 nodos del cluster (hoy magnus, por IP pública). No hay que tocar los nodos de unibus: el exporter solo lee su `/healthz`. El dashboard `hub/dashboards/unibus-cluster.json` visualiza estas series. ## Deploy Desde la raíz del project: `./hub/deploy_unibus_exporter.sh magnus om`. Compila el binario, sube binario + CA del cluster a `/opt/unibus-exporter` + `/etc/unibus-exporter`, escribe la config con el endpoint local de VM (`http://127.0.0.1:8428/...`, sin auth porque corre en el hub) e instala el servicio systemd. ## Gotchas - La CA del cluster es **secreta** (gitignored): no se versiona en el repo. El deploy la sube al nodo a `/etc/unibus-exporter/ca.crt` (chmod 600). Localmente vive en `projects/message_bus/apps/unibus/deploy/tls/ca.crt`. - El TLS se verifica **siempre** contra esa CA: una CA equivocada o ausente hace fallar el arranque, no se ignora. - `unibus_up=0` lo emite este exporter (no el parser) cuando el GET falla, para que un nodo caído sea visible en Grafana en vez de simplemente desaparecer. - Métricas profundas de NATS/JetStream (msgs/s, conexiones, RAFT leader por stream, NRestarts) NO las produce: requieren el monitoring embebido de NATS (puerto 8222), hoy cerrado en producción. Ver el report `unibus-grafana-monitoring` para el detalle del gap.