Verwaltung durch Personen (Weg 1), AGENTS.md, project-map.md, known-issues.md
ci / test (push) Failing after 24s
ci / test (push) Failing after 24s
Admin-Schlüssel je Person auf ops (Bastion) aus sites/knaster/proxmox.yaml (access.ops_ssh_keys/_revoked), Playbook admins.yml, Test, Übergabe in BETRIEB §15. Einstieg für Agenten und neue Admins: AGENTS.md, Projektkarte, bekannte Probleme. Weg 2 (Verwaltung über NEX) als Entwurf in Phase 9. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
039f57a435
commit
50acfa6a95
@@ -0,0 +1,77 @@
|
|||||||
|
# AGENTS.md – Arbeitsweise für Coding-Agenten (Claude Code, Codex, Hermes …)
|
||||||
|
|
||||||
|
Dieses Repo beschreibt den Proxmox-/k3s-Cluster **knaster** und den Router-Service `knaster`. Wer hier als Agent
|
||||||
|
arbeitet, ändert **Infrastruktur, die läuft**. Erst lesen: [project-map.md](project-map.md) (was wo läuft),
|
||||||
|
[known-issues.md](known-issues.md) (bekannte Fallen), der Aufbauplan
|
||||||
|
[docs/superpowers/plans/2026-10-12-aufbau-cluster-knaster.md](docs/superpowers/plans/2026-10-12-aufbau-cluster-knaster.md)
|
||||||
|
(was offen ist) und [docs/BETRIEB.md](docs/BETRIEB.md) (wie man es betreibt).
|
||||||
|
|
||||||
|
## Sprache und Stil
|
||||||
|
|
||||||
|
- Deutsch in Doku, Kommentaren, Commit-Nachrichten und Antworten; Umlaute ausschreiben (ä, ö, ü, ß).
|
||||||
|
- Code so schreiben wie der umgebende Code: Kommentardichte, Benennung, Muster (Playbook-Kopf mit Zweck und Aufruf,
|
||||||
|
Manifest-Kopf mit Zweck und Herkunft).
|
||||||
|
- Daten mit Datum notieren (`19.10.`), keine relativen Angaben („gestern“).
|
||||||
|
|
||||||
|
## Der Weg jeder Änderung
|
||||||
|
|
||||||
|
1. **Im Repo ändern** (auf dem Admin-Laptop bzw. dem Gerät der verwaltenden Person). Nie direkt auf ops oder in
|
||||||
|
einer VM editieren; `/root/cluster-knaster` auf ops und `/opt/knaster/repo` auf infra sind Kopien.
|
||||||
|
2. **Prüfen:** `make test` (bzw. `pytest core`), `make lint`, bei Ansible `ansible-playbook --syntax-check` auf ops,
|
||||||
|
bei Prometheus `promtool check config`/`check rules` (läuft im Prometheus-Image auf infra).
|
||||||
|
3. **Committen und pushen** (Branch laut Plan, derzeit `plan/aufbau-2026-10-12`). Nur eigene Dateien stagen –
|
||||||
|
es arbeiten oft mehrere Sitzungen parallel im selben Repo. Vor dem Push `git pull --rebase`.
|
||||||
|
4. **Auf ops ausrollen:** `ssh ops`, `cd /root/cluster-knaster && git pull`, dann in `platform/proxmox/ansible`
|
||||||
|
das passende Playbook. `kubectl`, `tofu` und Ansible gibt es **nur auf ops**.
|
||||||
|
5. **Live prüfen** (Pods bereit, Dienst antwortet, Prometheus-Ziel `up`) und erst dann abhaken.
|
||||||
|
6. **Plan und Doku nachziehen:** Checkbox im Aufbauplan mit Datum und Befund, Betriebswissen in `docs/BETRIEB.md`,
|
||||||
|
neue Fallen in `known-issues.md`, neue Dienste/Adressen in `project-map.md`.
|
||||||
|
|
||||||
|
## Zugang
|
||||||
|
|
||||||
|
- Einstieg ist **ops** (`192.168.100.173`, Bastion). Von dort erreicht man alle Knoten, VMs und infra mit dem
|
||||||
|
Schlüssel `root@ops`; Ansible-Inventar `platform/proxmox/ansible/inventory.yml` (+ `inventory.generated.yml`).
|
||||||
|
- Wer verwaltet, steht in `sites/knaster/proxmox.yaml` unter `access.ops_ssh_keys` (Playbook `playbooks/admins.yml`,
|
||||||
|
Übergabe: BETRIEB §15). Proxmox-Weboberfläche nur mit persönlichem Konto und TOTP.
|
||||||
|
- Muster für Befehle vom Laptop: `ssh ops '<befehl>'`, `ssh ops 'cd /root/cluster-knaster/platform/proxmox/ansible && ansible-playbook playbooks/<x>.yml'`.
|
||||||
|
In Ansible-Ad-hoc-Befehlen keine Go-Templates (`{{.Names}}`) – Ansible wertet `{{ }}` selbst aus; besser ein
|
||||||
|
kleines Skript mit `-m script`.
|
||||||
|
|
||||||
|
## Feste Regeln
|
||||||
|
|
||||||
|
- **Keine Geheimnisse im Repo.** Tokens und Passwörter liegen auf ops unter `/root/.config/knaster/` (0600), im LXC
|
||||||
|
infra unter `/opt/knaster/infra/.env` bzw. `secrets/`, beim Menschen in Password Depot. Playbooks lesen sie von dort,
|
||||||
|
Ausgaben mit `no_log: true`.
|
||||||
|
- **Dry-Run:** Healer und Router laufen mit `KNASTER_DRY_RUN=true`, `enabled_actions: []`. Nicht ohne ausdrückliche
|
||||||
|
Freigabe des Nutzers ändern.
|
||||||
|
- **IP-Literale statt DNS-Namen** in Konfigurationen (Netz `192.168.100.0/24`, Gateway `.1`, DNS `.120`). Adressen der
|
||||||
|
GPU-VMs und der k3s-VM kommen per DHCP; Prometheus-Ziele deshalb per file_sd, nicht fest.
|
||||||
|
- **GPUs:** In jeder GPU-VM ist Index 0 die Quadro M4000, Index 1 die P2000 (gpu-pve52 hat nur die M4000).
|
||||||
|
`nvidia.com/gpu` in k3s vergibt **nur die M4000**. Die P2000 gehört llama.cpp (fest per UUID). Neue GPU-Pods
|
||||||
|
bekommen also eine M4000; eine P2000 nur nach Absprache.
|
||||||
|
- **Was nach k3s umgezogen ist, nicht wieder per Compose starten:** `playbooks/nex-speech.yml` und
|
||||||
|
`playbooks/gpu-llamacpp.yml` brechen ab (Schalter nur für den Rückweg).
|
||||||
|
- **Images pinnen** (exakte Version, kein `latest`); bei k3s-Pods mit eigenem Build `imagePullPolicy: Never` und Import
|
||||||
|
per `docker save | k3s ctr images import -` (Muster: Rolle `nex-speech-k3s`, `gpu-llamacpp` `tasks/k3s.yml`).
|
||||||
|
- **Infra-LXC hat 4 GB RAM:** neue Prometheus-Jobs mit `metric_relabel_configs` auf das Nötige begrenzen.
|
||||||
|
- **Nach außen wirkende oder schwer umkehrbare Schritte** (Produktion auf appsrv08, DNS/DHCP, Löschen, Firewall,
|
||||||
|
Neustart von Knoten) vorher mit dem Nutzer abstimmen. Die Firewall-Rolle hat einen Totmannschalter, trotzdem
|
||||||
|
vorher prüfen, dass die eigene Adresse in `firewall.management` steht.
|
||||||
|
- **Namespace `nex`** gehört NEX (nex-admin legt dort Agenten an). Änderungen dort mit dem NEX-Team abstimmen.
|
||||||
|
|
||||||
|
## Muster im Repo
|
||||||
|
|
||||||
|
| Aufgabe | Vorlage |
|
||||||
|
|---|---|
|
||||||
|
| Neuer Dienst in k3s | `deploy/k8s/<dienst>/` + `playbooks/<dienst>-k3s.yml` (z. B. `nex-searxng`, `monitoring`) |
|
||||||
|
| Dienst mit GPU/Host-Netz in einer VM | `deploy/k8s/nex-speech/`, `deploy/k8s/llamacpp/llamacpp.yaml.j2` |
|
||||||
|
| Zugang für eine Anwendung zur k8s-API | `deploy/k8s/nex/admin-access.yaml` (ServiceAccount, Role, Token-Secret), Token-Datei auf ops |
|
||||||
|
| Neues Prometheus-Ziel | `deploy/prometheus/prometheus.yml`; wechselnde Adressen über `platform/proxmox/scripts/monitoring-targets.py` |
|
||||||
|
| Neuer Alert | `deploy/prometheus/alerts.yml` **und** passende Regel in `config/healer-rules.yaml` (Test erzwingt das) |
|
||||||
|
| Grafana-Dashboard | `deploy/grafana/dashboards/*.json`, Datasource-uid `prometheus`, Herkunft in `provisioning/dashboards/knaster.yml` |
|
||||||
|
|
||||||
|
## Testen unter Windows
|
||||||
|
|
||||||
|
- `.venv\Scripts\python -m pytest core` – `test_tts_server` scheitert unter Windows (kein Piper), das ist bekannt.
|
||||||
|
- `yamllint` meldet auf geänderten Dateien „wrong new line character“, weil Git die Arbeitskopie mit CRLF auscheckt.
|
||||||
|
Für eine echte Prüfung den Stand aus Git prüfen: `git show HEAD:<datei> | .venv/Scripts/yamllint -s -c .yamllint.yml -`.
|
||||||
@@ -15,6 +15,9 @@ Anfrage → n8n → Router-Service ──▶ Stufe 3 Embedding-Classifier (CPU)
|
|||||||
|
|
||||||
| Dokument | Inhalt |
|
| Dokument | Inhalt |
|
||||||
|---|---|
|
|---|---|
|
||||||
|
| [AGENTS.md](AGENTS.md) | **Einstieg für Agenten und neue Admins**: Arbeitsweise, Regeln, Muster |
|
||||||
|
| [project-map.md](project-map.md) | Was wo läuft: Rechner, Adressen, Dienste, Ports, Namespaces, Repo, Orte der Geheimnisse |
|
||||||
|
| [known-issues.md](known-issues.md) | Bekannte Probleme und Fallen |
|
||||||
| [docs/PLAN.md](docs/PLAN.md) | Architektur und Betrieb: Proxmox, Pflege, Autohealing, Lifecycle, Finetuning, Problemerkennung, Portabilität |
|
| [docs/PLAN.md](docs/PLAN.md) | Architektur und Betrieb: Proxmox, Pflege, Autohealing, Lifecycle, Finetuning, Problemerkennung, Portabilität |
|
||||||
| [docs/adr/](docs/adr/) | Architekturentscheidungen |
|
| [docs/adr/](docs/adr/) | Architekturentscheidungen |
|
||||||
| [docs/spec/](docs/spec/) | Spezifikationen der Komponenten |
|
| [docs/spec/](docs/spec/) | Spezifikationen der Komponenten |
|
||||||
|
|||||||
@@ -273,3 +273,17 @@ def test_proxmox_site_consistent_with_registry_and_prometheus():
|
|||||||
assert vm["driver_generation"] == "modern", slot
|
assert vm["driver_generation"] == "modern", slot
|
||||||
vmids = [str(v["vmid"]) for v in vms.values()] + [str(c["vmid"]) for c in site["containers"].values()]
|
vmids = [str(v["vmid"]) for v in vms.values()] + [str(c["vmid"]) for c in site["containers"].values()]
|
||||||
assert len(vmids) == len(set(vmids))
|
assert len(vmids) == len(set(vmids))
|
||||||
|
|
||||||
|
|
||||||
|
def test_ops_admin_keys_in_site_file():
|
||||||
|
"""Weg 1: Admins kommen mit eigenem Schlüssel auf ops (playbooks/admins.yml)."""
|
||||||
|
access = yaml.safe_load((REPO_ROOT / "sites/knaster/proxmox.yaml").read_text())["access"]
|
||||||
|
keys, revoked = access["ops_ssh_keys"], access["ops_ssh_keys_revoked"]
|
||||||
|
assert keys # nie leer, sonst sperrt sich die Verwaltung aus
|
||||||
|
names = [k["name"] for k in keys]
|
||||||
|
assert len(names) == len(set(names))
|
||||||
|
for k in [*keys, *revoked]:
|
||||||
|
parts = k["key"].split()
|
||||||
|
assert parts[0] in {"ssh-ed25519", "ssh-rsa"} and len(parts) >= 2, k["name"]
|
||||||
|
assert "PRIVATE" not in k["key"], k["name"]
|
||||||
|
assert not {k["key"].split()[1] for k in keys} & {k["key"].split()[1] for k in revoked}
|
||||||
|
|||||||
@@ -240,3 +240,45 @@ Die GPU-VMs bilden mit der Dienst-VM `k3s` einen k3s-Cluster (v1.36.5+k3s1). Ste
|
|||||||
- **ufw in den VMs:** die Rolle k3s-agent erlaubt 10250/tcp, 8472/udp, 30000–32767/tcp aus dem LAN und alles aus dem Pod-Netz 10.42.0.0/16.
|
- **ufw in den VMs:** die Rolle k3s-agent erlaubt 10250/tcp, 8472/udp, 30000–32767/tcp aus dem LAN und alles aus dem Pod-Netz 10.42.0.0/16.
|
||||||
- **Aktualisieren:** `k3s_version` in `roles/k3s-server/defaults` und `roles/k3s-agent/defaults` anheben, `playbooks/k3s.yml`; erst Server, dann Agents (so läuft das Playbook). Kubernetes-Versionen immer nur eine Minor-Stufe je Schritt.
|
- **Aktualisieren:** `k3s_version` in `roles/k3s-server/defaults` und `roles/k3s-agent/defaults` anheben, `playbooks/k3s.yml`; erst Server, dann Agents (so läuft das Playbook). Kubernetes-Versionen immer nur eine Minor-Stufe je Schritt.
|
||||||
- **Stromausfall:** VM 110 startet automatisch (onboot, HA). Agents verbinden sich neu, Pods starten mit den Knoten. Sollte `k3s` auf einen anderen Knoten umziehen, bleibt die Adresse (gleiche MAC), sobald die DHCP-Reservierung am DC steht.
|
- **Stromausfall:** VM 110 startet automatisch (onboot, HA). Agents verbinden sich neu, Pods starten mit den Knoten. Sollte `k3s` auf einen anderen Knoten umziehen, bleibt die Adresse (gleiche MAC), sobald die DHCP-Reservierung am DC steht.
|
||||||
|
|
||||||
|
## 15. Verwaltung übergeben oder teilen (seit 19.10.2026, „Weg 1“)
|
||||||
|
|
||||||
|
Die Verwaltung hängt an Personen, nicht an einem Gerät. Einstieg für jede Person ist **ops** (Bastion): eigener
|
||||||
|
SSH-Schlüssel dort, von ops aus laufen `kubectl`, Ansible und tofu und erreichen alle Knoten und Gäste. Ein Agent
|
||||||
|
(Claude Code o. Ä.) arbeitet auf dem Gerät der Person im Repo und nach [AGENTS.md](../AGENTS.md).
|
||||||
|
|
||||||
|
**Neue Person aufnehmen**
|
||||||
|
1. Die Person erzeugt auf ihrem Gerät einen Schlüssel (`ssh-keygen -t ed25519 -C <name>-<gerät>-<datum>`) und gibt
|
||||||
|
nur den öffentlichen Teil (`.pub`) weiter.
|
||||||
|
2. Eintrag in `sites/knaster/proxmox.yaml` unter `access.ops_ssh_keys` (`{name, key}`), committen, auf ops
|
||||||
|
`git pull` und `ansible-playbook playbooks/admins.yml` (zeigt danach die Fingerabdrücke).
|
||||||
|
3. Weboberfläche: Name in `access.admin_users`, `playbooks/cluster.yml`, dann `scripts/pve-admin-user.sh <name>`
|
||||||
|
(Passwort, TOTP, Recovery-Keys; Ergebnis in Password Depot). Geräteadresse(n) in `firewall.management`, erneut
|
||||||
|
`playbooks/cluster.yml` (Totmannschalter, Abschnitt 10).
|
||||||
|
4. Repo: Schreibzugang in Gitea (`gitea.mindboost.team/rorapp/cluster-knaster`) für die Person.
|
||||||
|
5. SSH-Konfiguration auf dem Gerät:
|
||||||
|
```
|
||||||
|
Host ops
|
||||||
|
HostName 192.168.100.173
|
||||||
|
User root
|
||||||
|
IdentityFile ~/.ssh/<schlüssel>
|
||||||
|
IdentitiesOnly yes
|
||||||
|
```
|
||||||
|
Alles Weitere läuft über ops (`ssh ops`, dann `ssh pve44`, `kubectl …`). Prüfen: `ssh ops kubectl get nodes`.
|
||||||
|
6. Zugänge, die nicht im Repo stehen, über Password Depot übergeben (Proxmox-root-Notfall, Grafana-Admin aus
|
||||||
|
`/opt/knaster/infra/.env`).
|
||||||
|
|
||||||
|
**Person oder Gerät entfernen**
|
||||||
|
1. Eintrag von `access.ops_ssh_keys` nach `access.ops_ssh_keys_revoked` verschieben, `playbooks/admins.yml`.
|
||||||
|
2. Name aus `access.admin_users` streichen und das Konto in Proxmox löschen (`pveum user delete <name>@pve`; das
|
||||||
|
Playbook legt nur an), Adresse aus `firewall.management`, `playbooks/cluster.yml`.
|
||||||
|
3. Gitea-Zugang entziehen. Hatte die Person Zugang zu Geheimnissen (Password Depot, ops), die betroffenen Token
|
||||||
|
rotieren (Abschnitt 8).
|
||||||
|
|
||||||
|
**Was ops nicht ersetzt:** Den Schlüssel des Admin-Laptops (`rrapp-laptop-2026-10-08`) gibt es zusätzlich direkt auf
|
||||||
|
den Knoten (`/etc/pve/priv/authorized_keys`), in infra und in den VMs. Neue Personen brauchen das nicht; bei einer
|
||||||
|
vollständigen Übergabe dort ebenfalls entfernen.
|
||||||
|
|
||||||
|
**Später („Weg 2“, Phase 9):** Verwaltung über nex-admin mit freigegebenen Aktionen auf ops statt persönlicher
|
||||||
|
Shell, siehe Aufbauplan.
|
||||||
|
|
||||||
|
|||||||
@@ -432,6 +432,20 @@ Die Software-Phasen aus PLAN §8 bleiben inhaltlich gültig und bekommen jeweils
|
|||||||
- „5 Lifecycle“: QLoRA-Training. Voraussetzung ist die Entscheidung über WSL2 bzw. vLLM auf den Ada-Rechnern nachts (D3, Folgeentscheidung).
|
- „5 Lifecycle“: QLoRA-Training. Voraussetzung ist die Entscheidung über WSL2 bzw. vLLM auf den Ada-Rechnern nachts (D3, Folgeentscheidung).
|
||||||
- „6 Portabilität“
|
- „6 Portabilität“
|
||||||
|
|
||||||
|
### Verwaltung durch mehrere Personen (Nutzerwunsch 19.10.)
|
||||||
|
|
||||||
|
- [x] **Weg 1 (19.10.):** Verwaltung an Personen statt an den Admin-Laptop gebunden. Einstieg ist ops (Bastion):
|
||||||
|
Schlüssel je Person in `sites/knaster/proxmox.yaml` (`access.ops_ssh_keys`, `ops_ssh_keys_revoked`), Playbook
|
||||||
|
`playbooks/admins.yml` (nur hinzufügen bzw. ausdrücklich entziehen), Test `test_ops_admin_keys_in_site_file`.
|
||||||
|
Aufnahme und Entzug in BETRIEB §15. Einstieg für Agenten: `AGENTS.md`, `project-map.md`, `known-issues.md`.
|
||||||
|
- [ ] **Weg 2 (später, Entwurf):** Verwaltung über NEX statt persönlicher Shell. nex-admin bekommt einen Bereich
|
||||||
|
„Infrastruktur“ (Anmeldung, Rollen und Protokoll von NEX). Dahinter auf ops ein kleiner Dienst, der nur freigegebene
|
||||||
|
Aktionen ausführt (bestimmtes Playbook, Dienst neu starten, Zustand zeigen), keine freie Shell. Neue Dinge entstehen
|
||||||
|
weiter im Repo: Ein Agent in NEX schreibt die Änderung als Branch in Gitea, ein Mensch gibt per Merge frei, der Dienst
|
||||||
|
rollt danach aus. Agenten schlagen nur vor (Grundsatz wie beim Healer). Anknüpfen an das Vorschlag-/Freigabe-Verfahren
|
||||||
|
von knaster (Policy „approve“). Offen vor dem Start: Wer verwaltet künftig (interne IT, Dienstleister, mehrere
|
||||||
|
Rollen)? Eigener Plan.
|
||||||
|
|
||||||
Am Ende von Phase 9 wird das Betriebshandbuch abgeschlossen („PVE-Config: Betriebshandbuch und Testvorgehen anlegen“). Das geschieht als Erweiterung von `docs/BETRIEB.md`, nicht als neues Dokument.
|
Am Ende von Phase 9 wird das Betriebshandbuch abgeschlossen („PVE-Config: Betriebshandbuch und Testvorgehen anlegen“). Das geschieht als Erweiterung von `docs/BETRIEB.md`, nicht als neues Dokument.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
# Bekannte Probleme und Fallen
|
||||||
|
|
||||||
|
Stand 19.10.2026. Je Eintrag: was passiert, Auswirkung, Umgang, wo es weitergeht. Behobene Einträge nicht löschen,
|
||||||
|
sondern unter „Erledigt“ verschieben (mit Datum), damit die Lehre erhalten bleibt. Siehe auch [AGENTS.md](AGENTS.md),
|
||||||
|
[project-map.md](project-map.md).
|
||||||
|
|
||||||
|
## Offen
|
||||||
|
|
||||||
|
### Netz und Zugang
|
||||||
|
|
||||||
|
- **DHCP-Reservierungen unvollständig.** Reserviert sind pve44, pve52, gpu-pve52 und der Laptop. Für infra, pve46 und
|
||||||
|
pve48 geht laut DC keine Reservierung (alle drei sind statisch konfiguriert, Risiko: doppelt vergebene Adresse).
|
||||||
|
Ohne Rückmeldung: ops, k3s-VM (.124), gpu-pve44/46/48, pve61, Gäste-Block `.220–.239` (am 19.10. erneut angefragt).
|
||||||
|
Auswirkung: Ändert sich die Adresse der k3s-VM, brechen Portainer CE (nginx-Ziel), `NEX_K3S_HOST` und die geplante
|
||||||
|
RustDesk-Umstellung. Ändern sich GPU-VM-Adressen: `refresh-vm-access.sh`, dann `playbooks/monitoring-targets.yml`.
|
||||||
|
→ Aufbauplan Task 0.2.
|
||||||
|
- **Firewall sperrt bei Adresswechsel aus.** Die Proxmox-Firewall lässt nur Adressen aus `firewall.management` an
|
||||||
|
22/8006. Rückweg über ops oder Konsole (BETRIEB §10).
|
||||||
|
- **Namespace `nex` ohne Pod Security.** nex-admin darf dort Pods mit `privileged`/`hostPath`/`hostNetwork` anlegen;
|
||||||
|
ein abgeflossenes Token wäre damit Root auf einer GPU-VM. → Task 6.6 (Sprachdienste nach `nex-speech`, dann
|
||||||
|
„baseline“).
|
||||||
|
|
||||||
|
### Monitoring und Healer
|
||||||
|
|
||||||
|
- **`SlotEndpointDown` feuert seit 15.10.** für tier1-flex, tier1-general (Ada-Rechner noch nicht angebunden, 6.4),
|
||||||
|
tts-de, tts-de-hq, embed-default, verifier-small, signature-extractor (Canaries laufen über LiteLLM, das kommt mit 6.3).
|
||||||
|
Erwartet, kein Handlungsbedarf bis 6.3/6.4.
|
||||||
|
- **vLLM-Ziele `gpu-main`/`gpu-flex` down** (DNS-Namen ohne Auflösung, Ada-Rechner nicht angebunden). → Task 6.4.
|
||||||
|
- **Healer kennt nur Docker-Container.** Registry und Healer-Regeln führen `docker_host`/`container`; llama.cpp und
|
||||||
|
die Sprachdienste laufen inzwischen in k3s. `restart_container` würde ins Leere greifen. Heute folgenlos
|
||||||
|
(Dry-Run, `enabled_actions: []`); vor dem Scharfschalten auf Pod-Neustart umstellen. → Task 6.1 (offen), Phase 9.
|
||||||
|
- **n8n-Receiver im Alertmanager nur vorbereitet** (auskommentiert, Format passt nicht). Alerts gehen nur an den
|
||||||
|
Router. → Task 5.2.
|
||||||
|
- **Prometheus-Neustart setzt Alert-Zustände zurück** (alles wieder „pending“). Nach einem Neustart des Stacks die
|
||||||
|
`for:`-Dauer abwarten, bevor man auf fehlende Alerts schließt.
|
||||||
|
- **Speicher von Prometheus auf infra beobachten** (4 GB RAM, seit 19.10. ~2000 Reihen mehr durch k3s). → Task 6.5.
|
||||||
|
- **`K3sPodCrashLooping` bleibt bis zu 5 min länger stehen,** nachdem ein Pod behoben oder gelöscht wurde
|
||||||
|
(`max_over_time[5m]`, nötig gegen Flackern).
|
||||||
|
|
||||||
|
### GPU und Modelle
|
||||||
|
|
||||||
|
- **Embedding auf der P2000 nur mit `-ub 4096`.** Mit 8192 braucht llama.cpp (Stand b6000) allein 5 GB Rechenpuffer
|
||||||
|
(ub × Vokabular), Flash-Attention hilft nicht. Belegung mit Rerank: 4,4–4,6 von 5 GB, wenig Reserve.
|
||||||
|
- **`nvidia.com/gpu` vergibt nur die M4000** (Device Plugin sieht nur Index 0). Wer einen GPU-Pod anlegt, bekommt
|
||||||
|
eine M4000; die P2000 gehört llama.cpp. Annahme: Index 0 ist in jeder GPU-VM die M4000 – das Playbook
|
||||||
|
`gpu-llamacpp-k3s.yml` prüft das vor jeder Änderung.
|
||||||
|
- **LiteLLM-Port auf appsrv08 nicht im LAN veröffentlicht.** Blockiert Task 6.3 (lokale Slots in LiteLLM) und damit
|
||||||
|
grüne Canaries. → Nutzer, Task 6.3.
|
||||||
|
|
||||||
|
### Knoten und Speicher
|
||||||
|
|
||||||
|
- **ZFS-Seiten-Cache nach HA-Umzug.** Nach `zfs receive` liefert ein Knoten für früher gemappte Dateien veralteten
|
||||||
|
Inhalt; Docker brach am 12.10. mit `invalid freelist page` ab. Abgefangen im LXC infra durch
|
||||||
|
`knaster-drop-stale-cache.service`; jeder neue replizierte LXC mit mmap-Nutzern braucht denselben Schutz (BETRIEB §7).
|
||||||
|
- **pve44: EFI-Partition nicht eingehängt** (`sda2` fehlt in `/etc/fstab`); GRUB-Updates erreichen sie nicht.
|
||||||
|
→ Aufbauplan Task 1.1, Nebenbefund.
|
||||||
|
- **pve61: Neustart nach dem Kernel-Update offen.** → Aufbauplan Task 1.1, Step 4.
|
||||||
|
- **Kein Backup.** Proxmox Backup Server und Ziel-Speicher stehen aus. → Task 5.1 (Entscheidung Nutzer).
|
||||||
|
|
||||||
|
### Übergänge (alt und neu laufen parallel)
|
||||||
|
|
||||||
|
- **Portainer:** EE auf appsrv08 läuft weiter, `portainer.mygodel.de` zeigt noch dorthin; CE im Cluster ist bereit.
|
||||||
|
Umstellung braucht Admin-Login und feste Adresse der k3s-VM (BETRIEB §14).
|
||||||
|
- **RustDesk:** Server läuft zusätzlich in k3s, Clients zeigen weiter auf `.29`.
|
||||||
|
- **SearXNG:** läuft in k3s und auf appsrv08; Umstellung der Agenten über `SEARXNG_URL` im nex-Admin.
|
||||||
|
|
||||||
|
### Arbeitsumgebung
|
||||||
|
|
||||||
|
- **Windows-Arbeitskopie mit CRLF:** `yamllint` meldet „wrong new line character“ für geänderte Dateien. Den Stand aus
|
||||||
|
Git prüfen (`git show HEAD:<datei> | yamllint -`), siehe AGENTS.md.
|
||||||
|
- **`test_tts_server` scheitert unter Windows** (kein Piper-Programm). Unter Linux grün.
|
||||||
|
- **Kein Python-venv auf ops:** `make test`/`make lint` laufen auf dem Laptop; auf ops nur Ansible-Syntaxprüfung.
|
||||||
|
- **Ansible-Ad-hoc mit Go-Templates** (`docker ps --format '{{.Names}}'`) scheitert, weil Ansible `{{ }}` auswertet.
|
||||||
|
Skript per `-m script` verwenden.
|
||||||
|
- **Mehrere Agenten-Sitzungen im selben Repo:** Eine Sitzung kann fremde, noch nicht committete Änderungen mit
|
||||||
|
committen. Nur eigene Dateien stagen, vor dem Push `git pull --rebase`.
|
||||||
|
|
||||||
|
## Erledigt
|
||||||
|
|
||||||
|
- 19.10.: Embedding/Rerank auf gpu-pve44/46 liefen nicht (Prometheus-Ziele 8081/8082 down) – seit Task 6.1 in k3s.
|
||||||
|
- 19.10.: Laptop wurde bei Lease-Wechsel von der Firewall ausgesperrt (14.10. passiert) – WLAN und Dock reserviert.
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# Verwaltung durch Personen statt durch ein Gerät („Weg 1“, 19.10.): SSH-Schlüssel der Admins auf ops (Bastion).
|
||||||
|
# Quelle: sites/<site>/proxmox.yaml, access.ops_ssh_keys und access.ops_ssh_keys_revoked. Läuft auf ops:
|
||||||
|
# ansible-playbook playbooks/admins.yml
|
||||||
|
# Fügt nur hinzu bzw. entfernt die ausdrücklich entzogenen Schlüssel; fremde Einträge (z. B. Notfallschlüssel) bleiben.
|
||||||
|
# Proxmox-Weboberfläche: access.admin_users + playbooks/cluster.yml + scripts/pve-admin-user.sh, Gerät in
|
||||||
|
# firewall.management (BETRIEB §8, §10, §15).
|
||||||
|
- name: ops – SSH-Schlüssel der Admins
|
||||||
|
hosts: localhost
|
||||||
|
connection: local
|
||||||
|
gather_facts: false
|
||||||
|
vars:
|
||||||
|
site_file: "{{ playbook_dir }}/../../../../sites/{{ knaster_site | default('knaster') }}/proxmox.yaml"
|
||||||
|
access: "{{ (lookup('file', site_file) | from_yaml).access }}"
|
||||||
|
admin_keys: "{{ access.ops_ssh_keys | default([]) }}"
|
||||||
|
revoked_keys: "{{ access.ops_ssh_keys_revoked | default([]) }}"
|
||||||
|
tasks:
|
||||||
|
- name: Liste prüfen (mindestens ein Schlüssel, Namen eindeutig, kein Schlüssel zugleich entzogen)
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- admin_keys | length > 0
|
||||||
|
- admin_keys | map(attribute='name') | unique | length == admin_keys | length
|
||||||
|
- admin_keys | map(attribute='key') | select('match', '^ssh-(ed25519|rsa) ') | list | length == admin_keys | length
|
||||||
|
- (admin_keys | map(attribute='key') | map('split', ' ') | map(attribute=1) | list)
|
||||||
|
| intersect(revoked_keys | map(attribute='key') | map('split', ' ') | map(attribute=1) | list) | length == 0
|
||||||
|
fail_msg: "access.ops_ssh_keys in {{ site_file }} prüfen"
|
||||||
|
|
||||||
|
- name: Schlüssel eintragen
|
||||||
|
ansible.posix.authorized_key:
|
||||||
|
user: root
|
||||||
|
key: "{{ item.key }}"
|
||||||
|
state: present
|
||||||
|
loop: "{{ admin_keys }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.name }}"
|
||||||
|
|
||||||
|
- name: Entzogene Schlüssel entfernen
|
||||||
|
ansible.posix.authorized_key:
|
||||||
|
user: root
|
||||||
|
key: "{{ item.key }}"
|
||||||
|
state: absent
|
||||||
|
loop: "{{ revoked_keys }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.name }}"
|
||||||
|
|
||||||
|
- name: Schlüssel auf ops (Fingerabdrücke)
|
||||||
|
ansible.builtin.command: ssh-keygen -lf /root/.ssh/authorized_keys
|
||||||
|
register: fingerprints
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Ergebnis
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "{{ fingerprints.stdout_lines }}"
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
# Projektkarte knaster
|
||||||
|
|
||||||
|
Stand 19.10.2026. Was wo läuft, wie man es erreicht und wo es im Repo steht. Bei jeder Änderung an Adressen,
|
||||||
|
Diensten oder Ports mitpflegen (siehe [AGENTS.md](AGENTS.md)). Bekannte Probleme: [known-issues.md](known-issues.md).
|
||||||
|
|
||||||
|
## Netz
|
||||||
|
|
||||||
|
Flaches Netz `192.168.100.0/24`, Gateway `.1`, DNS und DHCP auf dem DC `.120` (verwaltet vom DC-Support, nicht von uns).
|
||||||
|
Feste Adressen: Knoten, ops und infra statisch konfiguriert; GPU-VMs und k3s-VM per DHCP (Reservierungen Task 0.2).
|
||||||
|
|
||||||
|
## Rechner und Gäste
|
||||||
|
|
||||||
|
| Name | Art | Adresse | Feste Adresse | Rolle |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| pve44 | Proxmox-Knoten | .210 | statisch + Reservierung | GPU-Knoten (M4000 + P2000), Heimat von infra |
|
||||||
|
| pve46 | Proxmox-Knoten | .209 | statisch, keine Reservierung möglich | GPU-Knoten (M4000 + P2000), Heimat von ops |
|
||||||
|
| pve48 | Proxmox-Knoten | .212 | statisch, keine Reservierung möglich | GPU-Knoten (M4000 + P2000), Heimat der k3s-VM |
|
||||||
|
| pve52 | Proxmox-Knoten | .207 | statisch + Reservierung | GPU-Knoten (nur M4000) |
|
||||||
|
| pve61 | Proxmox-Knoten (Surface) | .214 | statisch | Quorum, später Kiosk (Task 5.3), kein `tank` |
|
||||||
|
| ops | LXC 100 auf pve46 | .173 | statisch | Steuerrechner/Bastion: kubectl, Ansible, tofu, Repo-Clone |
|
||||||
|
| infra | LXC 101 auf pve44 (HA) | .188 | statisch, keine Reservierung möglich | Monitoring und Router (Compose-Stack `knaster-infra`) |
|
||||||
|
| k3s | VM 110 auf pve48 (HA) | .124 | DHCP | k3s-Steuerung, Portainer CE, kube-state-metrics |
|
||||||
|
| gpu-pve44 | VM 244 | .186 | DHCP | k3s-Agent; llama.cpp Embedding + Rerank (P2000) |
|
||||||
|
| gpu-pve46 | VM 246 | .178 | DHCP | k3s-Agent; llama.cpp Embedding + Rerank (P2000) |
|
||||||
|
| gpu-pve48 | VM 248 | .183 | DHCP | k3s-Agent; llama.cpp Chat (P2000) |
|
||||||
|
| gpu-pve52 | VM 252 | .174 | DHCP + Reservierung | k3s-Agent; NEX-Sprachdienste (M4000) |
|
||||||
|
| appsrv08 | fremder Server | .29 | – | NEX-Produktion, LiteLLM (D4), Portainer EE, nex-admin, Traefik |
|
||||||
|
| Admin-Laptop | – | .151 (WLAN), .181 (Dock) | Reservierung | Arbeitsplatz von r.rapp |
|
||||||
|
|
||||||
|
## Dienste und Ports
|
||||||
|
|
||||||
|
| Dienst | Wo | Adresse | Zugang |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Proxmox-Weboberfläche | jeder Knoten | `https://<knoten>:8006` | persönliches Konto + TOTP, nur Adressen aus `firewall.management` |
|
||||||
|
| Grafana | infra | `http://192.168.100.188:3000` | `admin`, `kiosk` (Viewer); Dashboards: Übersicht, Proxmox, Knoten, GPU, k3s – Pods |
|
||||||
|
| Prometheus / Alertmanager | infra | `:9090` / `:9093` | ohne Anmeldung, nur LAN |
|
||||||
|
| knaster-Router | infra | `:8080` | Bearer-Token (Dry-Run) |
|
||||||
|
| TTS (Piper, knaster) | infra | `:8090` | – |
|
||||||
|
| k3s-API | k3s | `https://192.168.100.124:6443` | kubeconfig nur auf ops; nex-admin mit eigenem Token (nur Namespace `nex`) |
|
||||||
|
| Portainer CE | k3s | `https://192.168.100.124:30779` | Portainer-Konten (Datenbank von appsrv08 übernommen) |
|
||||||
|
| Portainer-Agent | jeder k3s-Knoten | `:30778` | für Portainer |
|
||||||
|
| SearXNG (NEX) | jeder k3s-Knoten | `:30880` | – |
|
||||||
|
| kube-state-metrics | jeder k3s-Knoten | `:30808` | für Prometheus |
|
||||||
|
| RustDesk hbbs/hbbr | jeder k3s-Knoten | `:21115–21119` | Clients zeigen noch auf `.29` |
|
||||||
|
| llama.cpp Embedding / Rerank | gpu-pve44, gpu-pve46 | `:8081` / `:8082` | LAN (ufw) |
|
||||||
|
| llama.cpp Chat (verifier-small) | gpu-pve48 | `:8080` | LAN (ufw) |
|
||||||
|
| NEX STT (whisper.cpp) / TTS (Piper) | gpu-pve52 | `:8178` / `:8179` | nur von appsrv08 (ufw) |
|
||||||
|
| node_exporter / nvidia_gpu_exporter | jede GPU-VM | `:9100` / `:9835` | LAN |
|
||||||
|
| kubelet | jeder k3s-Knoten | `:10250` | Token `prometheus-infra` |
|
||||||
|
|
||||||
|
## k3s-Namespaces
|
||||||
|
|
||||||
|
| Namespace | Inhalt | Ausgerollt mit |
|
||||||
|
|---|---|---|
|
||||||
|
| `kube-system` | k3s-Grundlagen, metrics-server, NVIDIA Device Plugin (nur M4000), kube-state-metrics | `playbooks/k3s.yml`, `playbooks/monitoring-k3s.yml` |
|
||||||
|
| `knaster` | llama.cpp je GPU-VM (`llamacpp-<vm>`) | `playbooks/gpu-llamacpp-k3s.yml` |
|
||||||
|
| `nex` | NEX-Agenten (von nex-admin), nex-stt, nex-tts, nex-searxng, Brücke zu appsrv08 | nex-admin, `playbooks/nex-k3s.yml`, `nex-speech-k3s.yml`, `nex-searxng-k3s.yml` |
|
||||||
|
| `portainer`, `portainer-ce` | Portainer-Agent, Portainer CE | `playbooks/k3s.yml`, `playbooks/portainer-k3s.yml` |
|
||||||
|
| `rustdesk` | RustDesk-Server | `playbooks/rustdesk-k3s.yml` |
|
||||||
|
|
||||||
|
## Repo
|
||||||
|
|
||||||
|
| Pfad | Inhalt |
|
||||||
|
|---|---|
|
||||||
|
| `AGENTS.md`, `project-map.md`, `known-issues.md` | Einstieg für Agenten und neue Admins |
|
||||||
|
| `docs/superpowers/plans/2026-10-12-aufbau-cluster-knaster.md` | Aufbauplan mit Stand je Task |
|
||||||
|
| `docs/BETRIEB.md` | Betriebshandbuch (Monitoring §7, Zugang §8, Firewall §10, k3s §14, Übergabe §15) |
|
||||||
|
| `docs/ANMELDUNG.md` | Anmeldung und Vertretung (Password Depot) |
|
||||||
|
| `docs/PLAN.md`, `docs/adr/`, `docs/spec/` | Architektur, Entscheidungen, Spezifikationen |
|
||||||
|
| `sites/knaster/proxmox.yaml` | Zielbild: Knoten, GPUs, VMs, LXC, Zugang, Firewall |
|
||||||
|
| `sites/knaster/registry.yaml`, `catalog/` | Slots (Modelle je Rolle) und Modellkatalog |
|
||||||
|
| `config/` | Policy, Healer-Regeln, Lifecycle |
|
||||||
|
| `core/` | Python-Paket `knaster` (Router, Healer, Canary, Inventar) und Tests |
|
||||||
|
| `deploy/k8s/` | Manifeste für k3s (Übersicht: `deploy/k8s/README.md`) |
|
||||||
|
| `deploy/compose/` | Compose-Stacks (infra aktiv; gpu-llamacpp, nex-speech nur noch Rückweg) |
|
||||||
|
| `deploy/prometheus/`, `deploy/alertmanager/`, `deploy/grafana/` | Monitoring-Konfiguration für infra |
|
||||||
|
| `platform/proxmox/tofu/` | VMs und LXC (OpenTofu, `bpg/proxmox`) |
|
||||||
|
| `platform/proxmox/ansible/` | Rollen und Playbooks (laufen auf ops) |
|
||||||
|
| `platform/proxmox/scripts/` | Bootstrap, Healthcheck, Zugang auffrischen, Monitoring-Ziele, Admin-Konto |
|
||||||
|
| `n8n/workflows/` | n8n-Workflows (inaktiv importieren) |
|
||||||
|
|
||||||
|
## Geheimnisse (nur Orte, nie Inhalte)
|
||||||
|
|
||||||
|
| Was | Wo |
|
||||||
|
|---|---|
|
||||||
|
| Proxmox-Admin-Passwort, TOTP, root-Passwörter | Password Depot; Kopie Passwörter auf dem Admin-Laptop `it/proxmox/.env` |
|
||||||
|
| SSH-Schlüssel Laptop | Admin-Laptop `it/proxmox/keys/` |
|
||||||
|
| tofu-, pve-exporter-, Healer-Token, k3s-Token, nex-admin-Token, Prometheus-Token, k3s-CA, Gitea-Registry | ops `/root/.config/knaster/` |
|
||||||
|
| Postgres, Router-Token, Grafana-Passwörter, Kopien der Exporter-/k3s-Zugänge | infra `/opt/knaster/infra/.env`, `/opt/knaster/infra/secrets/` |
|
||||||
|
| kubeconfig | ops `/root/.kube/config` |
|
||||||
@@ -46,6 +46,12 @@ access:
|
|||||||
admin_group: admins # Gruppe mit Administrator auf /; root@pam bleibt Notfallzugang
|
admin_group: admins # Gruppe mit Administrator auf /; root@pam bleibt Notfallzugang
|
||||||
admin_users: [r.rapp] # persönliche Konten <name>@pve; Passwort und TOTP per scripts/pve-admin-user.sh
|
admin_users: [r.rapp] # persönliche Konten <name>@pve; Passwort und TOTP per scripts/pve-admin-user.sh
|
||||||
mail_to: ki@mygodel.de # Ziel der Proxmox-Benachrichtigungen (Backup, Fencing, Updates, Replikation)
|
mail_to: ki@mygodel.de # Ziel der Proxmox-Benachrichtigungen (Backup, Fencing, Updates, Replikation)
|
||||||
|
# Verwaltung durch Personen statt durch ein Gerät (19.10., „Weg 1“): Jede Person kommt mit einem eigenen Schlüssel
|
||||||
|
# auf ops (Bastion) und arbeitet von dort (kubectl, Ansible, tofu). Öffentliche Schlüssel, kein Geheimnis.
|
||||||
|
# Ausrollen: playbooks/admins.yml auf ops. Übergabe und Entzug: docs/BETRIEB.md Abschnitt 15.
|
||||||
|
ops_ssh_keys:
|
||||||
|
- {name: r.rapp-laptop, key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHG5/rQMO4dtO8jOdAjQksFxZhdI7oSZAGNmFXVxQcoJ rrapp-laptop-2026-10-08"}
|
||||||
|
ops_ssh_keys_revoked: [] # ehemalige Schlüssel ({name, key}); playbooks/admins.yml entfernt sie von ops
|
||||||
firewall:
|
firewall:
|
||||||
enable: true
|
enable: true
|
||||||
# IPSET management: nur diese Quellen (plus alle Knoten) erreichen 8006, 22, 5900-5999, 3128 und
|
# IPSET management: nur diese Quellen (plus alle Knoten) erreichen 8006, 22, 5900-5999, 3128 und
|
||||||
|
|||||||
Reference in New Issue
Block a user