ci / test (push) Successful in 28s
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>
144 lines
12 KiB
Markdown
144 lines
12 KiB
Markdown
# 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`.
|