Files
cluster-knaster/docs/BETRIEB.md
T
Robert RappandClaude Opus 5.5 456fce2386
ci / test (push) Successful in 28s
infra-stack: Schutz gegen veralteten ZFS-Seiten-Cache nach HA-Umzug, Betriebsabschnitt 7
Bei der HA-Probe startete dockerd auf pve48 nicht (bbolt „invalid freelist page“): Nach zfs receive
liefert der Zielknoten für früher gemappte Dateien alten Inhalt aus dem Seiten-Cache. Ein oneshot-Dienst
im LXC verwirft vor containerd/docker den Cache aller Dateien (posix_fadvise DONTNEED).
BETRIEB.md Abschnitt 7: URLs, Geheimnisse (ohne Werte), file_sd auffrischen, Wiederherstellung.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 20:49:49 +02:00

144 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Betrieb: Skill-Lebenszyklus, Hermes, Pflege, Freischaltung
## 1. Lebenszyklus eines Skills
```
Hermes schreibt SKILL.md
│
draft ──(Mensch/Agent: aktivieren)──▶ active, Stufe 0
│ läuft auf dem starken Modell mit Skill-Text,
│ sammelt verifizierte Fälle
Service-Routine: stabil ≥ 95 % ────┤
▼
request_graduation (Freigabe)
│ Hermes erzeugt Varianten →
│ POST /v1/skills/{name}/graduate
▼
promote_skill (Freigabe) ──▶ active, Stufe 1 (schwaches Modell)
│
Drift (Pass-Rate < 80 %) ──▶ demote_skill (automatisch) ──▶ demoted (Stufe 0)
│
≥ 300 Trainingsfälle, Stufe-1-Rate < 90 % ─▶ train_adapter (Freigabe) ──▶ Lifecycle-Pipeline
```
Stufe 2 (Programm) und Stufe 3 (Classifier) gibt es, wenn der Skill `execution: program|classifier` deklariert und die passende Stufe freigegeben ist. Programme liegen in `knaster/programs.py` und gehen wie normaler Code durch ein Review. Classifier entstehen aus gelabelten Fällen (`EmbeddingClassifier.train`) und liegen unter `config/classifiers/<name>.json`.
## 2. Rolle von Hermes
Hermes läuft als Skill-Autor mit dem starken Modell. Er zeigt auf LiteLLM, Alias `tier0-strong`. Hermes steht **nicht** im Pfad jeder Anfrage.
| Auslöser | Hermes tut | Schnittstelle |
|---|---|---|
| Job `graduate_skill` (über n8n `knaster-jobs`) | 10–30 realistische Varianten erzeugen, Graduierung aufrufen. Schlägt sie fehl, den Skill anhand von `failures` überarbeiten | `POST /v1/skills/{name}/graduate` |
| Job `merge_skills` | zwei Skills zu einem zusammenführen, alten Skill auf `retired` setzen | Git-Commit in `skills/`, dann `POST /v1/skills/reload` |
| Neue Aufgabenklasse (häufige `no_skill`-Fälle) | neuen `SKILL.md`-Entwurf schreiben (`status: draft`) | Git-Commit + Reload |
Skills sind Dateien im Repo. Jede Änderung ist dadurch ein nachvollziehbarer Commit und lässt sich zurückdrehen.
## 3. Pflege per Prompt (Beispiele)
```
> routine
Service-Routine
Befunde:
- Drift: zusammenfassung Pass-Rate 72% (n=40)
Automatisch erledigt:
- P-12: Skill zusammenfassung auf Stufe 0 zurückstufen → Skill zusammenfassung auf Stufe 0 zurückgestuft
Offene Vorschläge (genehmige P-x / lehne P-x ab):
- P-13 [low] LoRA-Adapter für rechnungen trainieren – 412 verifizierte Fälle, Stufe-1-Rate 86%
> genehmige P-13
P-13 executed: Kandidat 7f3e… (adapter) angelegt und Build gestartet
> wechsle tier1-general auf qwen3-32b-awq
Vorschlag P-14 [medium] Basismodell von tier1-general auf qwen3-32b-awq wechseln …
Bestätige mit „genehmige P-14“.
```
Freitext, der keinem Befehl entspricht, bildet ein kleines Modell auf einen Befehl ab. Der Agent fragt dann mit „Meinst du …?“ zurück.
## 4. Dry-Run aufheben (schrittweise)
1. Eine Woche mit `KNASTER_DRY_RUN=true` laufen lassen. Die Healer-Ergebnisse stehen als `[dry-run] …` in den Antworten bzw. Logs.
2. `KNASTER_DRY_RUN=false` setzen. Interne Aktionen (Slot-Status, Skill-Stufen, Kandidaten) liefen schon vorher. Jetzt werden auch Jobs per Webhook verschickt.
3. Infrastruktur-Aktionen einzeln freischalten, über `config/healer-rules.yaml` → `enabled_actions: [restart_container]`, später `restart_vm`. `reboot_host` bleibt freigabepflichtig (Policy) und läuft nur im Wartungsfenster.
## 5. Rollback-Wege
| Was | Wie |
|---|---|
| Adapter/Basismodell | Prompt `rollback <slot>` → Freigabe |
| Skill-Stufe | Prompt `stufe <skill> zurück` bzw. Git-Revert der SKILL.md |
| Treiber/Engine | Proxmox-Snapshot der GPU-VM (vor jedem Update anlegen) |
| Konfiguration | Git-Revert und Neustart des Router-Containers |
## 6. Proxmox: Updates und Neustarts (rollierend, Knoten für Knoten)
Stand 12.10.2026, getestet mit dem Wartungsmodus auf pve44. Gilt für pve44, pve46, pve48, pve52 und pve61.
**Wichtig vorab**
- Auf den GPU-Knoten gehört die M4000 (Boot-Grafik) der VM. Nach dem frühen Start bleibt der lokale Bildschirm schwarz. Zugang nur per SSH oder Weboberfläche; das GRUB-Menü ist beim Start noch sichtbar (Notzugang: Eintrag bearbeiten, `initcall_blacklist=sysfb_init` entfernen).
- pve61 hängt am Netz nur über die USB-NIC im Dock. Neustart nur, wenn jemand vor Ort ist. Kommt er nach etwa 2 Minuten nicht zurück: Dock aus- und wieder einstecken.
- Nie zwei Knoten gleichzeitig. Mit 5 Stimmen bleibt das Quorum bei einem fehlenden Knoten erhalten.
**Ablauf je Knoten**
1. Vorher: `pvecm status` (Quorate: Yes), `ha-manager status`, `pvesr status` (alle Jobs OK).
2. Wartungsmodus: `ha-manager crm-command node-maintenance enable <knoten>`. HA-Gäste ziehen um (`infra`: Test 36 s nach pve48).
3. GPU-VM des Knotens sauber stoppen: `qm shutdown <vmid>` (244/246/248/252). Sie hat kein HA und kann nicht wandern (Passthrough).
4. Update: `platform/proxmox/scripts/bootstrap-node.sh <knoten>` vom Laptop (idempotent, meldet `Neustart nötig`), dann `reboot`.
5. Nachher prüfen: Kernel, `systemctl --failed`, `ip -br a show vmbr0`, NTP; auf GPU-Knoten `lspci -nnk -d 10de:` → überall `vfio-pci`.
6. GPU-VM startet von selbst (`onboot`). Danach auf ops: `cd /root/cluster-knaster/platform/proxmox/tofu && . /root/.config/knaster/tofu.env && bash ../scripts/refresh-vm-access.sh` (DHCP-Adressen können wechseln, Hostschlüssel kommen über den Gast-Agenten) und `ansible gpu_vms -m shell -a "nvidia-smi -L"`.
7. Wartungsmodus aus: `ha-manager crm-command node-maintenance disable <knoten>`. HA holt `infra` auf seinen Heimatknoten pve44 zurück (Test 41 s).
**GPU-VMs aktualisieren**
- Pakete per `apt` in der VM, die NVIDIA-Pakete sind auf `hold` (R580). Vor Treiberwechseln einen Snapshot anlegen: `qm snapshot <vmid> vor-treiber`.
- Nach einem VM-Neustart immer `refresh-vm-access.sh`, solange es keine DHCP-Reservierungen gibt (D5).
## 7. Infra-Stack und Monitoring (LXC `infra`)
Stand 12.10.2026 (Task 5.2). Der LXC `infra` (vmid 101, HA, Heimat pve44, Replikation nach pve48/pve52) führt einen Compose-Stack `knaster-infra`: Router (Dry-Run), TTS (Piper), Postgres, Prometheus, Alertmanager, Grafana, pve-exporter, node-exporter, autoheal. Kein eigener LiteLLM (D4).
**Adressen** (DHCP, die IP kann sich nach einem Umzug ändern; aktuelle IP: `ansible infra -m debug -a var=ansible_host` auf ops oder `refresh-vm-access.sh`)
| Dienst | URL | Anmeldung |
|---|---|---|
| Grafana | `http://<infra-IP>:3000` | `admin` bzw. `kiosk` (Viewer) |
| Prometheus | `http://<infra-IP>:9090` | ohne (nur LAN) |
| Alertmanager | `http://<infra-IP>:9093` | ohne (nur LAN) |
| Router | `http://<infra-IP>:8080` | Bearer-Token |
**Wo liegt was**
- Konfiguration im Repo: `deploy/compose/infra`, `deploy/prometheus`, `deploy/alertmanager`, `deploy/grafana` (Datasource, Dashboards). Im LXC als Kopie unter `/opt/knaster/repo` (wird bei jedem Ansible-Lauf abgeglichen, dort nichts ändern).
- Geheimnisse nur im LXC, nie im Repo: `/opt/knaster/infra/.env` (0600) mit `POSTGRES_PASSWORD`, `KNASTER_API_TOKEN`, `GRAFANA_ADMIN_PASSWORD`, `GRAFANA_KIOSK_PASSWORD` (beim ersten Lauf mit `openssl rand` erzeugt, danach nie überschrieben). Lesen: `ssh root@<infra-IP> grep ^GRAFANA_KIOSK_PASSWORD= /opt/knaster/infra/.env` (für den Kiosk, Task 5.3).
- `/opt/knaster/infra/secrets/router_token` (Kopie von `KNASTER_API_TOKEN` für den Alertmanager) und `secrets/pve-exporter.env` (Kopie von ops `/root/.config/knaster/pve-exporter.env`, Token `monitoring@pve!prom`, PVEAuditor).
- Einstellungen ohne Geheimnis (Dry-Run, `LITELLM_BASE_URL` …) stehen in `platform/proxmox/ansible/roles/infra-stack/defaults/main.yml` und werden bei jedem Lauf in die `.env` geschrieben.
**Ausrollen** (auf ops, idempotent): `cd /root/cluster-knaster && git pull`, Adressen auffrischen (Abschnitt 6, Schritt 6), dann in `platform/proxmox/ansible`:
- `ansible-playbook playbooks/monitoring.yml` – Exporter in den GPU-VMs (ohne Treiber-Rolle, kein Neustart) und Stack im LXC.
- Ein Image-Update heißt: Tag im Compose-File ändern, committen, Playbook laufen lassen.
**Ziele der GPU-VMs (file_sd) auffrischen**
Die GPU-VMs haben DHCP-Adressen (D5). `platform/proxmox/scripts/monitoring-targets.py` schreibt aus `tofu output -json gpu_vms` und der Registry `node.json` und `gpu.json` (Labels `instance` = VM-Name, `pve_node`, `vmid`, `slot`, `slots`). Nach jedem VM-Neustart:
```bash
cd /root/cluster-knaster/platform/proxmox/tofu && . /root/.config/knaster/tofu.env && bash ../scripts/refresh-vm-access.sh
cd ../ansible && ansible-playbook playbooks/monitoring-targets.yml
```
Prometheus liest die Dateien in `/opt/knaster/infra/file_sd` selbst neu, ein Neustart ist nicht nötig.
**Exporter in den GPU-VMs:** `prometheus-node-exporter` (Port 9100, Ubuntu-Paket, liest `/var/lib/node_exporter/textfile` mit den Xid-Zählern) und `nvidia_gpu_exporter` (Port 9835, nvidia-smi; DCGM unterstützt Maxwell nicht). ufw-Regeln mit Kommentar `knaster-monitoring` erlauben beide Ports aus `192.168.100.0/24`; ufw selbst wird von der Rolle nicht ein- oder ausgeschaltet.
**Alerts:** Alertmanager meldet an den Router (`/v1/healer/alertmanager`, Healer im Dry-Run). Der Receiver für n8n `knaster-notify` ist in `deploy/alertmanager/alertmanager.yml` nur vorbereitet (auskommentiert). Test: `docker exec knaster-infra-alertmanager-1 amtool --alertmanager.url=http://localhost:9093 alert add test severity=warning` und `… alert query`.
**Dashboards** (Ordner `knaster`, Startseite „knaster – Übersicht“): Proxmox via Prometheus (grafana.com 10347), Node Exporter Full (1860), Nvidia GPU Metrics (14574), knaster – Übersicht (eigenes).
**Achtung HA-Umzug und ZFS-Seiten-Cache (gefunden 12.10.2026 bei der HA-Probe)**
Nach `zfs receive` (Replikation) liefert ein Knoten für Dateien, die dort früher per mmap geöffnet waren, weiter den alten Inhalt aus seinem Seiten-Cache (ZFS 2.4.4, Kernel 7.0.14; Beleg auf pve44: `zfs diff <snap>` leer, `written` 0, md5 der Datei trotzdem ungleich der im Snapshot). Folge beim ersten Umzug nach pve48: dockerd las veraltete bbolt-Datenbanken, brach mit `panic: invalid freelist page` ab, danach fehlten Netzwerk und Volume-Labels. Betroffen ist jeder replizierte LXC mit mmap-Nutzern (Docker, Prometheus-TSDB, später `grp-test`), sobald er auf einen Knoten zurückkehrt, auf dem er schon lief.
- Schutz im Container: Dienst `knaster-drop-stale-cache.service` (Rolle `infra-stack`) verwirft vor containerd/docker per `posix_fadvise(DONTNEED)` den Cache aller Dateien des Containers. Protokoll: `journalctl -u knaster-drop-stale-cache -b` (zeigt geänderte Datenbank-Prüfsummen).
- Eine Lösung auf den Knoten (OpenZFS-Fehler melden bzw. Update, Hookscript) entscheidet der Nutzer.
**Wiederherstellung, falls Docker nach einem Umzug nicht startet** (im LXC, nichts löschen):
1. `systemctl stop docker containerd`; `journalctl -u docker -b | grep -A12 panic` zeigt die betroffene Datenbank (z. B. `buildkit/cache.db`).
2. Die defekte Datenbank bzw. das Verzeichnis beiseiteschieben, z. B. `mv /var/lib/docker/buildkit /var/lib/docker/buildkit.defekt-<datum>`.
3. `systemctl start docker`, dann `docker compose -p knaster-infra --env-file /opt/knaster/infra/.env -f /opt/knaster/repo/deploy/compose/infra/docker-compose.yml up -d --force-recreate` (Volumes bleiben, kein `down -v`).
4. Danach auf ops `ansible-playbook playbooks/monitoring.yml --limit infra`.