--- okf: "0.1" type: session-log visibility: private status: active updated: 2026-07-06 links: [] --- # 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).