docs: session 2026-06-09 + skill/backlog update
- docs/sessions/2026-06-09-flota-recovery-lustro-register.md: flota recovery (root cause aerbot group, 3 warstwy maskujące), lustro register stan+plan, fix-event-bloat i OOM pending, worktree gotcha - docs/backlog.md: nowy plik — tech-debt tracker; wpisy: --omit-dir-times, oskar∈aerbot deklaratywnie, worktree per task, observer staleness - .claude/skills/node-onboarding/SKILL.md: step table aktualizacja (PROVEN: 20-base, 30-node-agent; WRITTEN: 40-register, 50-verify), 3 nowe gotchas (rsync perm, observer restart, worktree branch) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
1304c8449f
commit
5c2516d097
|
|
@ -5,7 +5,7 @@ description: >
|
||||||
repo manifest, Tailscale mesh, node-agent, monitoring, and UI registration.
|
repo manifest, Tailscale mesh, node-agent, monitoring, and UI registration.
|
||||||
Keywords: "nowy node", "dodaj node", "onboarding", "onboard node".
|
Keywords: "nowy node", "dodaj node", "onboarding", "onboard node".
|
||||||
living_doc: true
|
living_doc: true
|
||||||
maturity: partial # PROVEN: 00-access; SCAFFOLD: base → verify. Update after each step lands on a real node.
|
maturity: partial # PROVEN: 00-access, 20-base, 30-node-agent; WRITTEN: 40-register, 50-verify (live pending). Update after each step lands on a real node.
|
||||||
---
|
---
|
||||||
|
|
||||||
> **Living document** — sections marked **SCAFFOLD** are stubs waiting for battle-testing on a real node.
|
> **Living document** — sections marked **SCAFFOLD** are stubs waiting for battle-testing on a real node.
|
||||||
|
|
@ -22,10 +22,10 @@ User asks to onboard / add a new node. Load this skill before touching any onboa
|
||||||
```
|
```
|
||||||
preflight (read-only)
|
preflight (read-only)
|
||||||
└─ 00-access [PROVEN]
|
└─ 00-access [PROVEN]
|
||||||
└─ base [SCAFFOLD]
|
└─ 20-base [PROVEN]
|
||||||
└─ node-agent [SCAFFOLD]
|
└─ 30-node-agent [PROVEN]
|
||||||
└─ register [SCAFFOLD]
|
└─ 40-register [WRITTEN — live pending]
|
||||||
└─ verify(50) [SCAFFOLD]
|
└─ 50-verify [WRITTEN — live pending]
|
||||||
```
|
```
|
||||||
|
|
||||||
Never skip ahead. Each step must exit 0 before the next begins.
|
Never skip ahead. Each step must exit 0 before the next begins.
|
||||||
|
|
@ -57,9 +57,11 @@ scripts/onboard/onboard.sh --node <name> --dry-run
|
||||||
| `00-preflight` | `steps/00-preflight.sh` | SCAFFOLD | Read-only: arch, RAM, docker, swap, MM runtime → YAML snippet for node.yaml |
|
| `00-preflight` | `steps/00-preflight.sh` | SCAFFOLD | Read-only: arch, RAM, docker, swap, MM runtime → YAML snippet for node.yaml |
|
||||||
| `00-access` | `steps/00-access.sh` | **PROVEN** | SSH key → `first_contact`, install Tailscale, `tailscale up` (interactive URL), verify over mesh |
|
| `00-access` | `steps/00-access.sh` | **PROVEN** | SSH key → `first_contact`, install Tailscale, `tailscale up` (interactive URL), verify over mesh |
|
||||||
| `10-bootstrap-runtime` | `steps/10-bootstrap-runtime.sh` | SCAFFOLD | Create `/opt/homelab/` layout, `chown <ssh_user>` |
|
| `10-bootstrap-runtime` | `steps/10-bootstrap-runtime.sh` | SCAFFOLD | Create `/opt/homelab/` layout, `chown <ssh_user>` |
|
||||||
|
| `20-base` | `steps/20-base.sh` | **PROVEN** | swap→zram, `/opt/homelab/` layout, event dir `/opt/homelab/events/<node>/` |
|
||||||
| `20-install-docker` | `steps/20-install-docker.sh` | SCAFFOLD | Install Docker Engine if `docker_present=false`; skip if already installed |
|
| `20-install-docker` | `steps/20-install-docker.sh` | SCAFFOLD | Install Docker Engine if `docker_present=false`; skip if already installed |
|
||||||
| `40-deploy-node-agent` | `steps/40-deploy-node-agent.sh` | SCAFFOLD | Deploy node-agent container; user 1000:1000; `mem_limit` from node.yaml |
|
| `30-node-agent` | `steps/30-node-agent.sh` | **PROVEN** | rsync base compose + override, `docker compose up -d --build`, verify container + events |
|
||||||
| `50-verify` | `steps/50-verify.sh` | SCAFFOLD | End-to-end smoke: event reaches control plane, visible in UI, Telegram alert path |
|
| `40-register` | `steps/40-register.sh` | WRITTEN | Dopisuje node do `inventory/topology.yaml` + tworzy `hosts/<node>/services.yaml`, commit na branchu (bez push) |
|
||||||
|
| `50-verify` | `steps/50-verify.sh` | WRITTEN | SSH node: container+events; SSH VPS: restart observer + heartbeat poll + world/nodes.json |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -122,6 +124,9 @@ Always run `--dry-run` first; dry-run must print real commands (`run()` propagat
|
||||||
| `yaml_get` drops value prefix after `:` | Non-greedy colon: `s/^[[:space:]]*[^:]*:[[:space:]]*//'` — handles `systemd:unit` correctly |
|
| `yaml_get` drops value prefix after `:` | Non-greedy colon: `s/^[[:space:]]*[^:]*:[[:space:]]*//'` — handles `systemd:unit` correctly |
|
||||||
| `yaml_get` keeps inline YAML comments | Strip with `s/[[:space:]]\+#.*$//` after extraction (requires ≥1 space before `#`) |
|
| `yaml_get` keeps inline YAML comments | Strip with `s/[[:space:]]\+#.*$//` after extraction (requires ≥1 space before `#`) |
|
||||||
| dry-run stops at orchestrator level | `run()` wrapper + `export DRY_RUN=1` propagated to all step scripts; probes execute for real |
|
| dry-run stops at orchestrator level | `run()` wrapper + `export DRY_RUN=1` propagated to all step scripts; probes execute for real |
|
||||||
|
| rsync push Permission denied to VPS events/ | ssh-user must be in the **group that owns `/opt/homelab/events/`** (aerbot/1000 on VPS). Symptom: silent WARNING in node-agent log, 292k files backlog, panel stale. Fix: `usermod -aG 1000 <user>` on VPS + re-login |
|
||||||
|
| observer not seeing new node after topology.yaml edit | `_load_inventory()` runs once at `__init__`. After `git pull` on VPS (bind-mount is live), **`docker restart control-plane-observer`** is required — no redeploy needed |
|
||||||
|
| worktree on wrong branch | Always check `git branch --show-current` on entry. One task = one worktree (`agent.sh new`). Never manually `git checkout` between task branches in the same worktree |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
57
docs/backlog.md
Normal file
57
docs/backlog.md
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
# Tech-debt backlog
|
||||||
|
|
||||||
|
Centralny tracker tech-długu i znanych usterek. Wpisy ze sesji — dodawaj z datą i kontekstem.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aktywne
|
||||||
|
|
||||||
|
### rsync `--omit-dir-times` (node-agent)
|
||||||
|
|
||||||
|
**Data**: 2026-06-09
|
||||||
|
**Źródło**: flota recovery session
|
||||||
|
**Objaw**: rsync exit code 23 po każdym push — `set-times` na katalogu `/opt/homelab/events/`
|
||||||
|
zwraca EPERM (oskar nie jest właścicielem katalogu; aerbot jest). Pliki są kopiowane poprawnie,
|
||||||
|
ale exit 23 zaśmieca logi i może maskować prawdziwe błędy.
|
||||||
|
**Fix**: dodać `--omit-dir-times` do wywołania `rsync` w `node-agent.py`.
|
||||||
|
**Lokalizacja**: `services/node-agent/src/node_agent.py` — wywołanie rsync w pętli push.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Deklaratywny zapis `oskar ∈ aerbot` w manifeście VPS
|
||||||
|
|
||||||
|
**Data**: 2026-06-09
|
||||||
|
**Źródło**: flota recovery — root cause: oskar spoza grupy aerbot(1000) → rsync Permission denied
|
||||||
|
**Problem**: przynależność do grupy jest zarządzana ręcznie (`usermod -aG 1000 oskar` ad-hoc).
|
||||||
|
Brak gwarancji po przeinstalowaniu VPS lub zmianie usera.
|
||||||
|
**Fix**: dodać do `hosts/vps/host.yaml` lub `hosts/vps/capabilities.yaml` sekcję
|
||||||
|
`users: oskar: groups: [aerbot]` — i wyegzekwować w deploy/bootstrap skrypcie VPS.
|
||||||
|
Alternatywa: zmienić właściciela `/opt/homelab/events/` na `oskar:oskar` i zaktualizować
|
||||||
|
node-agent deploy skrypty.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Rozdzielenie worktree per task (agent.sh)
|
||||||
|
|
||||||
|
**Data**: 2026-06-09
|
||||||
|
**Źródło**: sesja — `homelab-codex-ws-node-onboarding` używany raz dla `task/node-onboarding`,
|
||||||
|
raz dla `task/fix-event-bloat` przez ręczne `git checkout`.
|
||||||
|
**Problem**: jeden worktree współdzielony przez dwa branche = anty-wzorzec. `git branch`
|
||||||
|
mogło wskazywać zły branch; `+` w listingu = pozornie "w innym worktree" ale nieprawda.
|
||||||
|
Prowadzi do commitowania na złej gałęzi.
|
||||||
|
**Fix**: egzekwować — jeden task = jeden worktree (`agent.sh new <task-name>`). Przy wejściu
|
||||||
|
do worktree zawsze `git branch --show-current` i weryfikacja `.agent-task`.
|
||||||
|
Długoterminowo: `agent.sh new` powinien odmawiać jeśli żądana gałąź jest już sprawdzona.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Zamknięte
|
||||||
|
|
||||||
|
### Observer staleness — martwy node pokazywany NOMINAL
|
||||||
|
|
||||||
|
**Data**: 2026-06-08 (złapane), status: OTWARTY w sensie implementacji
|
||||||
|
**Problem**: observer/supervisor trzyma ostatni znany stan; brak heartbeat TTL.
|
||||||
|
Chelsty-infra milczy, ale status NOMINAL podważa zaufanie do panelu.
|
||||||
|
**Fix**: heartbeat TTL → po przekroczeniu oznacz status `stale` lub `down`.
|
||||||
|
**Powiązane**: brain-watchdog ślepy na per-node freshness.
|
||||||
|
*(Otwarty jako TODO implementacyjny — przeniesiony z sesji 2026-06-08)*
|
||||||
124
docs/sessions/2026-06-09-flota-recovery-lustro-register.md
Normal file
124
docs/sessions/2026-06-09-flota-recovery-lustro-register.md
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
# Sesja 2026-06-09 — flota recovery + LUSTRO register
|
||||||
|
|
||||||
|
## Cel
|
||||||
|
|
||||||
|
Diagnoza cichej awarii reportingu floty; dokończenie kroku REGISTER dla LUSTRO
|
||||||
|
(40-register.sh + 50-verify.sh); update skilla node-onboarding.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## GŁÓWNE: 8-dniowa cicha awaria reportingu floty — ROZWIĄZANA
|
||||||
|
|
||||||
|
### Root cause
|
||||||
|
|
||||||
|
`oskar` (uid 1002) **spoza grupy aerbot (1000)** na VPS.
|
||||||
|
`/opt/homelab/events/*` = `aerbot:aerbot 775` → `oskar` w "other" (r-x).
|
||||||
|
`rsync` push z każdego node'a (jako `oskar` przez SSH) = **Permission denied** przy
|
||||||
|
zapisie → `--remove-source-files` nie czyścił backlogu → **292 000 plików** nagromadzonych
|
||||||
|
w staging cache node-agentów.
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
|
||||||
|
```bash
|
||||||
|
usermod -aG 1000 oskar # na VPS; ssh re-login wymagany
|
||||||
|
```
|
||||||
|
|
||||||
|
### Weryfikacja
|
||||||
|
|
||||||
|
- VPS `events/piha` 3443 pliki (rośnie)
|
||||||
|
- `piha` lokalnie: 2 pliki (staging wyczyszczony)
|
||||||
|
- Panel agents.okit.pl: vps / piha / solaria — Last Seen świeże
|
||||||
|
|
||||||
|
### Diagnoza — 5 warstw, 4 obalone hipotezy
|
||||||
|
|
||||||
|
Verify-before-fix obalił kolejno:
|
||||||
|
1. `authorized_keys` missing — klucz był, SSH działał (piha→VPS ręcznie OK)
|
||||||
|
2. Remote agent down — procesy `rsync` widoczne w `ps`, logi bez crash
|
||||||
|
3. VPS IP zmiana — Tailscale IP niezmieniony 100.95.58.48
|
||||||
|
4. Bridge/relay cutoff — ping VPS→piha OK przez mesh
|
||||||
|
|
||||||
|
5 warstwa (błąd uprawnienia) odkryta przez ręczny `rsync` jako `oskar` na VPS →
|
||||||
|
`Permission denied (13)` → `stat /opt/homelab/events/` → `aerbot:aerbot 775`.
|
||||||
|
|
||||||
|
### Dlaczego awaria była CICHA (3 warstwy maskujące)
|
||||||
|
|
||||||
|
| Warstwa | Mechanizm |
|
||||||
|
|---------|-----------|
|
||||||
|
| (a) shipping fail | Logowany jako `WARNING`, nie crash — node-agent nie failował, milczał |
|
||||||
|
| (b) observer staleness | Stale node pokazywany NOMINAL — brak heartbeat TTL, observer trzyma ostatni znany stan |
|
||||||
|
| (c) brain-watchdog | Ślepy na per-node freshness — nie monitoruje świeżości eventów per-node |
|
||||||
|
|
||||||
|
### Pozostały drobny błąd
|
||||||
|
|
||||||
|
`rsync` exit code 23: `set-times` na katalogu = `EPERM` (oskar nie jest właścicielem
|
||||||
|
`/opt/homelab/events/` — `aerbot` jest). Kosmetyka — rsync działa poprawnie.
|
||||||
|
**Fix**: dodać `--omit-dir-times` do wywołania rsync w node-agent (wpisane do backlogu).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## LUSTRO register: stan po sesji
|
||||||
|
|
||||||
|
### Dokonane
|
||||||
|
|
||||||
|
- `40-register.sh` — napisany i zcommitowany na `task/node-onboarding`
|
||||||
|
- Idempotentny: grep topology, `[[ -f services.yaml ]]`, `git diff --quiet`
|
||||||
|
- Commituje tylko `inventory/topology.yaml` + `hosts/lustro/services.yaml` na bieżącym branchu
|
||||||
|
- BEZ `git push` (merge należy do operatora)
|
||||||
|
- `50-verify.sh` — napisany i zcommitowany
|
||||||
|
- 4 checki: node-agent running, eventy, observer restart + heartbeat poll, world/nodes.json
|
||||||
|
- Tabela pass/fail; exit 1 on failure
|
||||||
|
- `40-deploy-node-agent.sh` — scaffold usunięty (deploy w 30-node-agent.sh)
|
||||||
|
- Dry-run `40-register.sh --dry-run` przeszedł czysto
|
||||||
|
|
||||||
|
### Mechanizm aktywacji observera (zbadany)
|
||||||
|
|
||||||
|
Observer bind-mountuje repo root jako `/repo:ro` z `services/control-plane/docker-compose.yml`
|
||||||
|
(`../..:/repo:ro` → `/home/oskar/homelab-codex-ws` na VPS). `_load_inventory()` wywoływane
|
||||||
|
raz przy starcie. **Aktywacja po merge**: `git pull` VPS + `docker restart control-plane-observer`
|
||||||
|
— bez redeploy.
|
||||||
|
|
||||||
|
### Wpis lustro w topology.yaml (minimalistyczny, 1:1 z piha)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
lustro:
|
||||||
|
roles:
|
||||||
|
- edge
|
||||||
|
services:
|
||||||
|
- node-agent
|
||||||
|
```
|
||||||
|
|
||||||
|
### PENDING (jutro)
|
||||||
|
|
||||||
|
1. Commit B: `onboard.sh --node lustro --step 40-register` live → commit na branchu
|
||||||
|
2. `agent.sh merge task/node-onboarding` → master
|
||||||
|
3. `git pull` na VPS + `docker restart control-plane-observer`
|
||||||
|
4. `onboard.sh --node lustro --step 50-verify` → lustro widoczny w agents.okit.pl
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## fix-event-bloat (task/fix-event-bloat)
|
||||||
|
|
||||||
|
Commit `d483274` na branchu: batch rsync, backlog trim, timeout 120s, backlog warn.
|
||||||
|
**PENDING**: review + deploy na flotę.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## OOM ai-cluster (obserwacja live)
|
||||||
|
|
||||||
|
Zaobserwowany na VPS podczas sesji: cgroup OOM restart-loop, python workery ~195 MB,
|
||||||
|
0 swap. **PENDING**: migracja `ai-cluster` → SOLARIA + dodanie swap na VPS.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Gotcha sesji
|
||||||
|
|
||||||
|
**Worktree branch confusion**: `~/homelab-codex-ws-node-onboarding` był przełączony
|
||||||
|
ręcznie na `task/fix-event-bloat` (jeden worktree, dwa branche ręcznie switchwane).
|
||||||
|
Anty-wzorzec: zawsze sprawdzać `git branch --show-current` na wejściu do worktree.
|
||||||
|
Docelowo: osobny worktree per task.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tech-debt złapany w sesji
|
||||||
|
|
||||||
|
→ wpisany do `docs/backlog.md`
|
||||||
Loading…
Reference in a new issue