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>
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
# 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:
|
||||
|
||||
```json
|
||||
{"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-cluster` → `dashboard 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.
|
||||
Reference in New Issue
Block a user