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

12 KiB
Raw Blame History

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:

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.