From 50acfa6a95a591fa1fbb549919af589e0a48ba43 Mon Sep 17 00:00:00 2001 From: Robert Rapp Date: Mon, 19 Oct 2026 18:54:02 +0200 Subject: [PATCH] Verwaltung durch Personen (Weg 1), AGENTS.md, project-map.md, known-issues.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- AGENTS.md | 77 ++++++++++++++++ README.md | 3 + core/tests/test_artifacts.py | 14 +++ docs/BETRIEB.md | 42 +++++++++ .../2026-10-12-aufbau-cluster-knaster.md | 14 +++ known-issues.md | 81 +++++++++++++++++ platform/proxmox/ansible/playbooks/admins.yml | 52 +++++++++++ project-map.md | 90 +++++++++++++++++++ sites/knaster/proxmox.yaml | 6 ++ 9 files changed, 379 insertions(+) create mode 100644 AGENTS.md create mode 100644 known-issues.md create mode 100644 platform/proxmox/ansible/playbooks/admins.yml create mode 100644 project-map.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..58dd45c --- /dev/null +++ b/AGENTS.md @@ -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 ''`, `ssh ops 'cd /root/cluster-knaster/platform/proxmox/ansible && ansible-playbook playbooks/.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//` + `playbooks/-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: | .venv/Scripts/yamllint -s -c .yamllint.yml -`. diff --git a/README.md b/README.md index 27d4d4d..5fcc3cd 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,9 @@ Anfrage → n8n → Router-Service ──▶ Stufe 3 Embedding-Classifier (CPU) | 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/adr/](docs/adr/) | Architekturentscheidungen | | [docs/spec/](docs/spec/) | Spezifikationen der Komponenten | diff --git a/core/tests/test_artifacts.py b/core/tests/test_artifacts.py index 02513a4..f50a78d 100644 --- a/core/tests/test_artifacts.py +++ b/core/tests/test_artifacts.py @@ -273,3 +273,17 @@ def test_proxmox_site_consistent_with_registry_and_prometheus(): assert vm["driver_generation"] == "modern", slot vmids = [str(v["vmid"]) for v in vms.values()] + [str(c["vmid"]) for c in site["containers"].values()] 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} diff --git a/docs/BETRIEB.md b/docs/BETRIEB.md index 9b831aa..97626e6 100644 --- a/docs/BETRIEB.md +++ b/docs/BETRIEB.md @@ -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. - **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. + +## 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 --`) 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 ` + (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/ + 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 @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. + diff --git a/docs/superpowers/plans/2026-10-12-aufbau-cluster-knaster.md b/docs/superpowers/plans/2026-10-12-aufbau-cluster-knaster.md index 626c9e8..92d8794 100644 --- a/docs/superpowers/plans/2026-10-12-aufbau-cluster-knaster.md +++ b/docs/superpowers/plans/2026-10-12-aufbau-cluster-knaster.md @@ -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). - „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. --- diff --git a/known-issues.md b/known-issues.md new file mode 100644 index 0000000..103ddcc --- /dev/null +++ b/known-issues.md @@ -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: | 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. diff --git a/platform/proxmox/ansible/playbooks/admins.yml b/platform/proxmox/ansible/playbooks/admins.yml new file mode 100644 index 0000000..3d59a72 --- /dev/null +++ b/platform/proxmox/ansible/playbooks/admins.yml @@ -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//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 }}" diff --git a/project-map.md b/project-map.md new file mode 100644 index 0000000..807a15d --- /dev/null +++ b/project-map.md @@ -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://: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-`) | `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` | diff --git a/sites/knaster/proxmox.yaml b/sites/knaster/proxmox.yaml index 210d8eb..541d59e 100644 --- a/sites/knaster/proxmox.yaml +++ b/sites/knaster/proxmox.yaml @@ -46,6 +46,12 @@ access: admin_group: admins # Gruppe mit Administrator auf /; root@pam bleibt Notfallzugang admin_users: [r.rapp] # persönliche Konten @pve; Passwort und TOTP per scripts/pve-admin-user.sh 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: enable: true # IPSET management: nur diese Quellen (plus alle Knoten) erreichen 8006, 22, 5900-5999, 3128 und