142 lines
7.8 KiB
Markdown
142 lines
7.8 KiB
Markdown
|
|
# Sesja 2026-07-15 — Prometheus cutover Etap 2 (analiza GO) + domknięcie rodziny bugów event-pipeline/checkpoint
|
|||
|
|
|
|||
|
|
## Kontekst
|
|||
|
|
|
|||
|
|
Kontynuacja projektu cutoveru liveności floty na Prometheus (start 2026-06-22,
|
|||
|
|
Etap 0 2026-07-06, Etap 1 shadow-read wdrożony). Równolegle: domknięcie rodziny
|
|||
|
|
bugów event-pipeline/checkpoint zapoczątkowanej fixem `d5139c9` (2026-07-14).
|
|||
|
|
|
|||
|
|
## ZROBIONE
|
|||
|
|
|
|||
|
|
### 1. fix(observer) `d5139c9` — checkpoint po timestampie, nie ścieżce leksykalnej
|
|||
|
|
|
|||
|
|
ROOT CAUSE cichej ~34-dniowej "śmierci" PIHA dla observera: plik
|
|||
|
|
`evt-unknown-<ts>-...` leksykalnie większy niż `evt-piha-<ts>-...` ("p" < "u")
|
|||
|
|
zatruł checkpoint węzła — każdy nowy event PIHA uznawany za starszy i pomijany
|
|||
|
|
na zawsze. Fix: checkpoint = ostatnio przetworzony TIMESTAMP (int epoch),
|
|||
|
|
parsowany z nazwy `evt-<node>-<ts>-...` (fallback mtime, **nigdy 0** dla
|
|||
|
|
istniejącego pliku — 0 = leksykalne "starszy niż checkpoint" = dokładnie ten
|
|||
|
|
poison), migracja starych path-checkpointów przy starcie. Zdeployowany na VPS
|
|||
|
|
(observer `StartedAt` 07-14). Zweryfikowany dziś jako kompletny i zdeployowany.
|
|||
|
|
Szczegóły: `docs/backlog.md` (sekcja "Bug: checkpoint observera po ścieżce
|
|||
|
|
leksykalnej").
|
|||
|
|
|
|||
|
|
### 2. docs(infra) analiza Etapu 2 shadow-run (Fable, `8fec62d`)
|
|||
|
|
|
|||
|
|
`docs/infra/prometheus-shadow-etap2-analiza-2026-07-15.md` — 165 mismatchy
|
|||
|
|
`SHADOW_LIVENESS_MISMATCH` solaria/lustro w dobie 2026-07-14 WYJAŚNIONE: dwa
|
|||
|
|
nocne wyłączenia węzłów (lustro 21:30 UTC — regularny power-off, solaria
|
|||
|
|
21:34 UTC). Wzorzec `event=fresh prom=down` to **nie** "żywy węzeł niewidziany
|
|||
|
|
przez Prometheusa", tylko **martwy węzeł wykryty przez Prometheus w ≤45 s**,
|
|||
|
|
podczas gdy tor eventowy potrzebował pełnych 600 s TTL (`last_seen_age` rósł
|
|||
|
|
monotonicznie 29→597 s; mismatche ustają dokładnie na granicy TTL-dead).
|
|||
|
|
|
|||
|
|
**Werdykt**: żaden z 165 mismatchy nie jest błędem Prometheusa — Prometheus
|
|||
|
|
był w każdym przypadku szybszy i miał rację. **Rekomendacja Etap 3: GO**:
|
|||
|
|
- vps, piha (always-on) — bez zastrzeżeń, 84 h ciągłego `up==1`, 0 mismatchy.
|
|||
|
|
- solaria, lustro (intermittent) — `prom=down` przy wyłączeniu jest prawdziwy,
|
|||
|
|
NIE dyskwalifikuje z cutoveru; planowe okna off → wątek anomaly detection
|
|||
|
|
(osobny, niezależny, nieblokujący).
|
|||
|
|
- Mapping: `prom-last_seen = timestamp(up)` wpuszczone w istniejące
|
|||
|
|
`compute_liveness` (potwierdzenie rekomendacji z recon).
|
|||
|
|
|
|||
|
|
Ograniczenie dowodowe: twarde logi docker pokrywają tylko ~20 h (recreate
|
|||
|
|
kontenera 07-14 16:30 skasował wcześniejsze) — wnioski podparte pośrednio
|
|||
|
|
84 h historii `up{}` + eventami przejść od 07-11; formalne "GO" czeka na
|
|||
|
|
~7 dni czystych danych z trwałego logu (pkt 3 niżej), cel ~2026-07-20.
|
|||
|
|
|
|||
|
|
### 3. feat(observer) `9e7ed3e` — trwały log SHADOW_LIVENESS_MISMATCH
|
|||
|
|
|
|||
|
|
Zapis do `/opt/homelab/logs/observer/shadow-liveness.log`
|
|||
|
|
(`RotatingFileHandler` 5MB×5, osobny logger `observer.shadow`,
|
|||
|
|
`propagate=False`, mismatch idzie i do stdout, i do pliku) — przeżywa docker
|
|||
|
|
recreate (recreate 07-14 zjadł materiał dowodowy analizy z pkt 2, stąd
|
|||
|
|
konieczność tego fixu). Świadomie **bez** nowego mountu (footgun: Docker
|
|||
|
|
tworzy brakujący bind-source jako root, uid 1000 nie miałby prawa zapisu) —
|
|||
|
|
wykorzystany istniejący mount `/opt/homelab`. Fail-safe: błąd zapisu do pliku
|
|||
|
|
nie wywala observera. Zdeployowany, potwierdzony (test lustro niżej wylądował
|
|||
|
|
w pliku, 162 KB).
|
|||
|
|
|
|||
|
|
### 4. Test kontrolowany event=dead / prom=up (lustro/pimirror2, 100.99.85.73)
|
|||
|
|
|
|||
|
|
Zatrzymano node-agenta na żywym węźle (host up, eventy przestają płynąć) →
|
|||
|
|
observer po TTL uznaje węzeł za dead → shadow zalogował
|
|||
|
|
`node=lustro event=dead prom=up` DO TRWAŁEGO PLIKU. Waliduje drugi kierunek
|
|||
|
|
rozbieżności (nie wystąpił naturalnie w oknie analizy z pkt 2) razem z
|
|||
|
|
działaniem trwałego logu na realnym zdarzeniu. Node-agent przywrócony po
|
|||
|
|
teście. Dostęp do lustro: tylko `pi@` (hasło) — klucz `oskar` z SOLARII
|
|||
|
|
nieautoryzowany na tym hoście; hostname faktyczny `pimirror2`.
|
|||
|
|
|
|||
|
|
### 5. fix(ha-diag) `f2ba81b` — node_name z env + fail-fast na "unknown"
|
|||
|
|
|
|||
|
|
ŹRÓDŁO trucizny checkpointu z pkt 1: `config.py`
|
|||
|
|
`Field(default="unknown", validate_default=True)` + validator odrzucający
|
|||
|
|
`""`/`"unknown"`; `main.py` → `SystemExit(1)` FATAL przy braku `NODE_NAME`;
|
|||
|
|
`EventEmitter.__init__` jako ostatnia bramka przed nazwą pliku eventu.
|
|||
|
|
+18 testów, 0 regresji. Zmergowany i **zdeployowany na PIHA** (agent
|
|||
|
|
`Up healthy` po rebuild — `NODE_NAME=piha` dochodzi do procesu). Pliki
|
|||
|
|
`evt-unknown-*` na VPS/PIHA: 0 (potwierdzone).
|
|||
|
|
|
|||
|
|
### 6. docs(backlog) `c858dbc` — bug deploy-node.sh brak `--build`
|
|||
|
|
|
|||
|
|
Deploy raportuje "OK", ale dla serwisów z Dockerfile bez zmiany compose/env
|
|||
|
|
kontener zostaje na starym obrazie (Docker cache'uje po tagu, nie po
|
|||
|
|
zawartości `src/`) — cicha rozbieżność repo↔runtime. Ugryzło dwa razy:
|
|||
|
|
fleet-prometheus (config nie wchodził bez `--force-recreate`) i ha-diag-agent
|
|||
|
|
dziś (fix z pkt 5 był w repo, `Running` zamiast rebuild — wymagał ręcznego
|
|||
|
|
`docker compose up -d --build --force-recreate`). Fix pozostaje TODO w
|
|||
|
|
backlogu.
|
|||
|
|
|
|||
|
|
## STAN CUTOVERU
|
|||
|
|
|
|||
|
|
- Etap 0 (dowód bojowy Prometheus→watchdog→Telegram): ✅ zamknięty (07-06).
|
|||
|
|
- Etap 1 (shadow-read, observer czyta oba źródła, nic nie przełącza): ✅
|
|||
|
|
wdrożony.
|
|||
|
|
- Etap 2 (parallel-run + analiza zgodności): **TRWA**. Dane po fixach z pkt 1
|
|||
|
|
i 3 czyste; analiza z pkt 2 gotowa z rekomendacją GO, ale formalnie czeka na
|
|||
|
|
dłuższe okno trwałego logu (cel ~2026-07-20).
|
|||
|
|
- Etap 3 (przełączenie per-węzeł, `PROM_LIVENESS_NODES`) — czeka na domknięcie
|
|||
|
|
Etapu 2.
|
|||
|
|
|
|||
|
|
## NASTĘPNE (po ~2026-07-20)
|
|||
|
|
|
|||
|
|
- Etap 3 per-node: najpierw `solaria,lustro`, potem `vps,piha` (kolejność z
|
|||
|
|
recon, minimalizacja blast-radius).
|
|||
|
|
- Watchdog na sam Prometheus (SPOF po cutoverze — dziś nikt nie alarmuje o
|
|||
|
|
jego śmierci; `mem_limit 512m` + `oom_score_adj 200` czynią go ubijalnym
|
|||
|
|
przed control-plane).
|
|||
|
|
- `deploy-node.sh --build` (pkt 6 / backlog `c858dbc`).
|
|||
|
|
- Observer powinien odrzucać/kwarantannować event, którego `node` w treści ≠
|
|||
|
|
katalog docelowy (druga warstwa obrony po fixie z pkt 5).
|
|||
|
|
|
|||
|
|
## Wnioski
|
|||
|
|
|
|||
|
|
- **Rodzina bugów uid/gid + checkpoint + node_name to jeden motyw:
|
|||
|
|
"ścieżka/nazwa pliku ≠ tożsamość".** Checkpoint leksykalny po ścieżce
|
|||
|
|
(pkt 1), plik `evt-unknown-*` lądujący w cudzym katalogu (pkt 5), stary
|
|||
|
|
tech-debt uid/gid per-host (backlog 2026-07-10) — wszystkie trzy to ten sam
|
|||
|
|
wzorzec: system ufa POŁOŻENIU/NAZWIE zamiast jawnie zweryfikowanej
|
|||
|
|
tożsamości, i cichnie zamiast fail-fastować, gdy te się rozjadą. Fix z
|
|||
|
|
pkt 5 domyka jeden konkretny wektor (`NODE_NAME` nieustawione → fail-fast),
|
|||
|
|
ale ogólny wzorzec (observer nie waliduje `node` w evencie względem
|
|||
|
|
katalogu) zostaje jako TODO.
|
|||
|
|
- **`deploy-node.sh` bez `--build` to osobny, ortogonalny motyw "cicha
|
|||
|
|
rozbieżność deploy↔runtime"** — nie dotyczy tylko configów (backlog
|
|||
|
|
2026-06-26), ale też kodu; ugryzło dwa razy w jednym dniu dzisiejszej sesji.
|
|||
|
|
- **Trwały log (pkt 3) był koniecznym fixem, nie kosmetyką** — recreate
|
|||
|
|
kontenera observera 07-14 skasował materiał dowodowy w trakcie samej analizy
|
|||
|
|
Etapu 2; bez trwałego logu każdy kolejny recreate zerowałby okno danych i
|
|||
|
|
cofał kryterium "≥7 dni czystych danych" do zera.
|
|||
|
|
- **Równoległe sesje CC dopisują commity do mastera w trakcie pracy** —
|
|||
|
|
commity `f57a01a`/`38cb204` (ollama/SOLARIA) wylądowały między `f2ba81b` i
|
|||
|
|
`9e7ed3e` w historii, mimo że nie są częścią tego wątku. Nie spowodowało
|
|||
|
|
konfliktu tym razem (worktree bazował na commicie sprzed rozjazdu, fast-
|
|||
|
|
forward czysty), ale potwierdza zasadę z CLAUDE.md: przed push zawsze
|
|||
|
|
ff-check + rebase, nie zakładać, że `master` stoi w miejscu.
|
|||
|
|
- **Terminal gubił wklejki blokami** podczas sesji (drobna operacyjna
|
|||
|
|
uciążliwość, bez wpływu na wynik) — odnotowane jako obserwacja, nie bug do
|
|||
|
|
śledzenia.
|
|||
|
|
|
|||
|
|
Hashe sesji: `d5139c9`, `8fec62d` (analiza Fable), `9e7ed3e`, `f2ba81b`,
|
|||
|
|
`c858dbc`.
|