diff --git a/docs/sessions/2026-07-06.md b/docs/sessions/2026-07-06.md new file mode 100644 index 0000000..5fb24fb --- /dev/null +++ b/docs/sessions/2026-07-06.md @@ -0,0 +1,106 @@ +# Sesja 2026-07-06 — cutover liveności na Prometheus: recon starego toru + Etap 0 udowodniony w boju + +## Cel + +Główny projekt: Prometheus jako źródło prawdy liveności floty. Dwa kroki tej sesji: +(1) read-only recon starego toru liveności z dokładnymi punktami przełączenia +i planem etapowym; (2) Etap 0 cutoveru — bojowy dowód end-to-end toru +Prometheus → brain-watchdog → Telegram, którego brakowało od 2026-06-30. + +--- + +## ZROBIONE + +### Recon cutoveru — wmergowany (commit `d94bb38`) + +Wynik: `docs/infra/prometheus-cutover-recon-2026-07-06.md` (517 linii, read-only, +zero zmian w kodzie). Kluczowe ustalenia: + +- **Cutover to podmiana klasyfikacji liveności w JEDNYM miejscu** — + `_prune_stale_world` w `scripts/observer/observer.py:312-323` + neutralizacja + event-driven flipów `:438-446` — a NIE odłączanie rury. Fizycznie nic nie znika. +- **`node_health` wozi też metryki** disk/mem/cpu do panelu i `disk_pressure` + do supervisora → rsync + eventy ZOSTAJĄ w całości. +- **Supervisor-remediacja i executor są CAŁKOWICIE niezależne od liveności węzłów** + — supervisor czyta z `nodes.json` wyłącznie `disk_pressure`. Jedyny konsument + liveności po stronie supervisora to syntetyczne eventy przejść + (`node_offline/stale/online` → alert_only → Telegram). +- **Różnica semantyk**: `up{}` mierzy "host żyje (exporter odpowiada)", + eventowy `node_health` mierzy "node-agent żyje + cały łańcuch SSH/rsync działa". + Cutover ZWĘŻA definicję liveności — padnięty node-agent przy żywym węźle zmienia + werdykt. Do świadomej akceptacji w etapie 2; rozważyć `up{job=node-agent}`. +- **chelsty-infra ma liveność WYŁĄCZNIE z eventów** (Prometheus go nie scrape'uje + po LTE) → cutover MUSI być per-węzeł (`PROM_LIVENESS_NODES`), nigdy globalny. +- **Prometheus po cutoverze = niepilnowany SPOF** (`mem_limit 512m`, + `oom_score_adj 200` — ubijalny przed control-plane) → fail-open na stary tor + obowiązkowy + watchdog na sam Prometheus (mały task przy etapie 3). +- Exportery piha/solaria/lustro istnieją **poza GitOps** (brak definicji w repo) + — rozjazd, osobny wątek. + +### Etap 0 cutoveru — DONE, dowód bojowy end-to-end + +Tor **Prometheus → brain-watchdog → Telegram udowodniony END-TO-END po raz +pierwszy w produkcji**. Test punktowy na VPS (celowo NIE w repo): tymczasowa +reguła `AlertTestEtap0` (`up{node="vps"}==1`, `for: 0s`) dodana do +`services/fleet-prometheus/rules/` na VPS + reload. Cztery punkty potwierdzenia: + +1. **Firing w Prometheusie**: `/api/v1/alerts` → `AlertTestEtap0` firing, node vps. +2. **Watchdog spollował z PIHA**: log `[prometheus] sent alert: AlertTestEtap0:vps` + — przy okazji widoczne dwa niezależne tory (ping mózgu OK co 60 s + poll + Prometheusa osobno), zgodnie z architekturą A. +3. **Alert doleciał na Telegram** (@okitaialerts_bot): „Prometheus alert: + AlertTestEtap0, Node: vps, Summary: …". +4. **Rollback czysty**: reguła usunięta, reload, firing=0. + +Zamyka pending z 2026-06-30 („czy watchdog realnie polluje" — recon 2026-07-02 +potwierdził konfigurację, dziś potwierdzone działanie end-to-end). Reguła testowa +NIE trafiła do repo — to diagnostyka, nie architektura; katalog `rules/` ma +nadal tylko `liveness.yml`. + +### Realny dowód „czemu cutover" — chelsty NOMINAL-zonk + +Panel agents.okit.pl pokazuje **chelsty NOMINAL + Runtime online, mimo że chelsty +jest OFFLINE od 34 dni** (`tailscale status`). U wszystkich węzłów: Last Seen +„Invalid Date", Connectivity/Incidents `undefined`, a status NOMINAL — observer +NIE egzekwuje świeżości `last_seen` (bug TTL wraca, patrz backlog „Observer +staleness"). Cutover na Prometheus `up{}` to naprawia: chelsty nie-scrape'owany → +down, nie fałszywie NOMINAL. + +Snapshot panelu potwierdził też dwa zaległe drobiazgi (dopisane do backlogu): + +- **Ghosty B wróciły/nieposprzątane** — hash-prefixed kontenery control-plane + widoczne na VPS (wpis „zniknęły" z 2026-07-02 zdezaktualizowany). +- **elasticsearch + diskover w stanie error na PIHA** — usunięte w module 0 + (2026-07-02), ale observer wciąż je zna i raportuje. + +--- + +## PLAN — następne etapy cutoveru (osobne sesje) + +- **Etap 1 — shadow-read**: observer czyta OBA źródła (eventy I Prometheus + `up{}`), pisze nienaruszające pola `prom_*` w `nodes.json`, loguje rozbieżności, + NIC nie przełącza (parallel-run, fail-open). Realna zmiana w + `observer.py:312-323` → worktree/CC. +- **Etap 2 — analiza**: ≥ 7 dni zgodności → klasyfikacja rozbieżności + (semantyka vs bug) → decyzja mappingu (rekomendacja: `timestamp(up)` jako + prom-last_seen przez istniejące `compute_liveness`). +- **Etap 3 — przełączenie per-węzeł** za flagą `PROM_LIVENESS_NODES`: + najpierw solaria+lustro (najmniejsza szkoda przy pomyłce), potem vps+piha. + **chelsty-infra ZOSTAJE na eventach.** Rollback = wyczyszczenie flagi. + +--- + +## Wnioski + +- **Etap 0 zamknął fundamentalną niepewność wiszącą od 2026-06-30**: „czy tor + alertowy Prometheus→watchdog→Telegram w ogóle działa" przestało być wnioskowaniem + z konfiguracji (recon 2026-07-02), a stało się faktem dowiedzionym w produkcji. + Dalsze etapy cutoveru budują na sprawdzonym fundamencie, nie na założeniu. +- **verify-before-fix w reconie obalił założenie architektoniczne**: observer NIE + jest jednym modułem w `services/control-plane/src/` — to `scripts/observer/observer.py` + + wydzielona maszyna stanów `liveness.py` importowana przez trzy konsumentów. + Bez reconu plan cutoveru celowałby w złe miejsce. +- **Test punktowy (reguła for:0s + rollback) to tani sposób na dowód bojowy toru + alertowego** — bez czekania na realną awarię i bez commitowania diagnostyki do repo. +- Hashe sesji: `d94bb38` (recon cutoveru); Etap 0 = czynność operatorska na + runtime (bez commitu, zgodnie z planem z reconu).