sites: Inventar und Registry an echte Knoten (pve44–pve61) angepasst; ADR-0001/0002/0006 und PLAN §1.3/1.4/9 ergänzt
Inventar mit pve44–pve61 und den Ada-Rechnern gode-pc-05/06 (M4000 weiter disabled). Registry-ops auf gpu-pve44/gpu-pve48/infra laut Placement, bei den Ada-Slots entfallen pve_node und vmid (Worker-Registry v2, Task 6.4). Tests: neuer test_site_knaster_matches_real_nodes, Konsistenztest auf gpus-Liste umgestellt. ADR-Nachträge zu D1 (Variante B), D2 (eine GPU-VM pro Knoten) und D3 (Ada bleibt Windows, vorgeschlagen). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
9b2ad426da
commit
e88e0ea46b
@@ -52,6 +52,8 @@ def test_ops_labels_consistent_between_registry_and_prometheus():
|
|||||||
next(j for j in prom["scrape_configs"] if j["job_name"] == "vllm")["static_configs"]}
|
next(j for j in prom["scrape_configs"] if j["job_name"] == "vllm")["static_configs"]}
|
||||||
for slot, labels in vllm.items():
|
for slot, labels in vllm.items():
|
||||||
ops = reg["slots"][slot]["ops"]
|
ops = reg["slots"][slot]["ops"]
|
||||||
|
if "vmid" not in ops:
|
||||||
|
continue # Ada-Slots ohne Proxmox-VM (D3); die Prometheus-Ziele folgen in Task 5.2
|
||||||
assert ops["vmid"] == labels["vmid"] and ops["container"] == labels["container"]
|
assert ops["vmid"] == labels["vmid"] and ops["container"] == labels["container"]
|
||||||
|
|
||||||
|
|
||||||
@@ -160,22 +162,24 @@ def test_proxmox_site_consistent_with_registry_and_prometheus():
|
|||||||
reg = yaml.safe_load((REPO_ROOT / "sites/knaster/registry.yaml").read_text())
|
reg = yaml.safe_load((REPO_ROOT / "sites/knaster/registry.yaml").read_text())
|
||||||
vms = site["vms"]
|
vms = site["vms"]
|
||||||
for vm in vms.values():
|
for vm in vms.values():
|
||||||
assert vm["gpu"] in site["pci_mappings"]
|
assert vm["gpus"], vm # eine GPU-VM pro Knoten mit allen Karten des Knotens (D2)
|
||||||
assert site["pci_mappings"][vm["gpu"]]["node"] == vm["node"] # Mapping liegt auf dem Host der VM
|
for gpu in vm["gpus"]:
|
||||||
|
assert gpu in site["pci_mappings"]
|
||||||
|
assert site["pci_mappings"][gpu]["node"] == vm["node"] # Mapping liegt auf dem Host der VM
|
||||||
assert vm["driver_generation"] in {"legacy", "modern"}
|
assert vm["driver_generation"] in {"legacy", "modern"}
|
||||||
gpus = [vm["gpu"] for vm in vms.values()]
|
gpus = [gpu for vm in vms.values() for gpu in vm["gpus"]]
|
||||||
assert len(gpus) == len(set(gpus)) # keine GPU doppelt durchgereicht
|
assert len(gpus) == len(set(gpus)) # keine GPU doppelt durchgereicht
|
||||||
|
assert set(gpus) == set(site["pci_mappings"]) # jede gemappte Karte steckt in einer VM
|
||||||
by_vmid = {str(vm["vmid"]): (name, vm) for name, vm in {**vms, **site["containers"]}.items()}
|
by_vmid = {str(vm["vmid"]): (name, vm) for name, vm in {**vms, **site["containers"]}.items()}
|
||||||
for slot, spec in reg["slots"].items():
|
for slot, spec in reg["slots"].items():
|
||||||
ops = spec.get("ops")
|
ops = spec.get("ops")
|
||||||
if not ops:
|
if not ops or "vmid" not in ops:
|
||||||
continue
|
continue # Ada-Slots ohne Proxmox-VM (D3, Worker-Registry v2)
|
||||||
name, vm = by_vmid[ops["vmid"]]
|
name, vm = by_vmid[ops["vmid"]]
|
||||||
assert vm["node"] == ops["pve_node"], slot
|
assert vm["node"] == ops["pve_node"], slot
|
||||||
assert ops["docker_host"].startswith(name.split("-")[0]), slot
|
assert ops["docker_host"] == name, slot
|
||||||
|
# vLLM und PyTorch nur auf modernen Karten (ADR-0006)
|
||||||
|
if spec["engine"] in {"vllm", "pytorch"} and name in vms:
|
||||||
|
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))
|
||||||
# vLLM nur auf modernen Karten
|
|
||||||
for vm in vms.values():
|
|
||||||
if vm["role"] == "gpu-vllm":
|
|
||||||
assert vm["driver_generation"] == "modern"
|
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
|
import ipaddress
|
||||||
import json
|
import json
|
||||||
|
import re
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
from knaster.cli import main
|
from knaster.cli import main
|
||||||
from knaster.inventory.placement import load_catalog, load_nodes, plan
|
from knaster.inventory.placement import load_catalog, load_nodes, plan
|
||||||
@@ -45,12 +49,26 @@ def test_placement_knaster():
|
|||||||
assert p.free_gpus == []
|
assert p.free_gpus == []
|
||||||
assert len(p.disabled_gpus) == 4 and all("M4000" in g for g in p.disabled_gpus)
|
assert len(p.disabled_gpus) == 4 and all("M4000" in g for g in p.disabled_gpus)
|
||||||
tts = {a.role: a for a in p.assignments if a.role.startswith("tts")}
|
tts = {a.role: a for a in p.assignments if a.role.startswith("tts")}
|
||||||
assert tts["tts-hq"].mode == "shared" and tts["tts-hq"].node == "pve-ada"
|
assert tts["tts-hq"].mode == "shared" and tts["tts-hq"].node == by_role["gpu-flex"][0].node == "gode-pc-06"
|
||||||
assert "qwen3-tts-1.7b" in tts["tts-hq"].models_fitting
|
assert "qwen3-tts-1.7b" in tts["tts-hq"].models_fitting
|
||||||
assert tts["tts-cpu"].node == by_role["infra"][0].node
|
assert tts["tts-cpu"].node == by_role["infra"][0].node
|
||||||
assert tts["tts-cpu"].models_fitting == ["piper-de-thorsten-medium"]
|
assert tts["tts-cpu"].models_fitting == ["piper-de-thorsten-medium"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_site_knaster_matches_real_nodes():
|
||||||
|
"""Site-Dateien beschreiben die echten Knoten (Stand 12.10.2026), nicht mehr die Annahmen pve-ada/pve1–pve4."""
|
||||||
|
names = {n.name for n in load_nodes(REPO_ROOT / "sites/knaster/inventory.yaml")}
|
||||||
|
assert names == {"pve44", "pve46", "pve48", "pve52", "pve61", "gode-pc-05", "gode-pc-06"}
|
||||||
|
text = (REPO_ROOT / "sites/knaster/proxmox.yaml").read_text(encoding="utf-8")
|
||||||
|
old_net = ipaddress.ip_network("10.20.0.0/24")
|
||||||
|
ips = re.findall(r"\b\d{1,3}(?:\.\d{1,3}){3}\b", text)
|
||||||
|
assert [ip for ip in ips if ipaddress.ip_address(ip) in old_net] == []
|
||||||
|
site = yaml.safe_load(text)
|
||||||
|
gpu_nodes = {"pve44", "pve46", "pve48", "pve52"}
|
||||||
|
assert {m["node"] for m in site["pci_mappings"].values()} <= gpu_nodes
|
||||||
|
assert {vm["node"] for vm in site["vms"].values()} <= gpu_nodes
|
||||||
|
|
||||||
|
|
||||||
def test_placement_other_cluster():
|
def test_placement_other_cluster():
|
||||||
roles, models = load_catalog(REPO_ROOT / "catalog")
|
roles, models = load_catalog(REPO_ROOT / "catalog")
|
||||||
p = plan(load_nodes(REPO_ROOT / "sites/example-single-gpu/inventory.yaml"), roles, models)
|
p = plan(load_nodes(REPO_ROOT / "sites/example-single-gpu/inventory.yaml"), roles, models)
|
||||||
|
|||||||
+41
-16
@@ -47,30 +47,45 @@ Ziel: Ein starkes LLM (OpenRouter) erarbeitet neue Aufgaben einmalig und bricht
|
|||||||
|
|
||||||
### 1.3 Rollenverteilung (aktuelle Hardware)
|
### 1.3 Rollenverteilung (aktuelle Hardware)
|
||||||
|
|
||||||
| Rolle | Hardware | Engine | Aufgabe |
|
Stand 12.10.2026, per SSH geprüft. Proxmox-Cluster `knaster` mit fünf Knoten:
|
||||||
|
|
||||||
|
| Knoten | IP | Hardware (Kerne/Threads, RAM) | GPUs |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `gpu-main` | RTX 4000 Ada #1 (20 GB) | vLLM, Multi-LoRA | Slot `tier1-general` plus Skill-Adapter |
|
| pve44 | 192.168.100.210 | ThinkStation P310, i7-6700 (4/8), 32 GB | Quadro M4000 (8 GB) + Quadro P2000 (5 GB) |
|
||||||
| `gpu-flex` | RTX 4000 Ada #2 (20 GB) | vLLM / Unsloth | tagsüber zweiter Slot oder Reserve, nachts LoRA-Training |
|
| pve46 | 192.168.100.209 | ThinkStation P310, i7-6700 (4/8), 32 GB | M4000 + P2000 |
|
||||||
| `gpu-embed` | 2× Quadro P2000 (5 GB) | llama.cpp (Embedding/Rerank) | `embed-default`, `rerank-default`, redundant auf zwei Hosts |
|
| pve48 | 192.168.100.212 | ThinkStation P310, i7-6700 (4/8), 32 GB | M4000 + P2000 |
|
||||||
| `gpu-small` | Quadro P2000 (5 GB) | llama.cpp | `verifier-small`, `signature-extractor` (3–4B) |
|
| pve52 | 192.168.100.207 | ThinkStation P320, i7-7700 (4/8), 32 GB | nur M4000 |
|
||||||
| – | 4× Quadro M4000 (8 GB) | – | **vorerst außer Betrieb** (ADR-0006), nur nach Benchmark für llama.cpp-Rollen |
|
| pve61 | 192.168.100.214 | Surface Book, i5-8350U (4/8), 8 GB | keine NVIDIA (Intel UHD 620, Kiosk) |
|
||||||
| `tts-hq` | teilt RTX 4000 Ada #2 | PyTorch | `tts-de-hq`: Qwen3-TTS 1.7B (ADR-0006) |
|
|
||||||
| `tts-cpu` | Infra-LXC (CPU) | Piper (ONNX) | `tts-de`: schnelle Sprachausgabe, Fallback |
|
Jeder GPU-Knoten hat **eine** GPU-VM mit allen Karten (`gpu-pve44`, `gpu-pve46`, `gpu-pve48`, `gpu-pve52`, ADR-0001 Nachtrag). Die beiden RTX 4000 Ada (20 GB) stecken nicht im Cluster, sondern je eine in den Windows-Arbeitsplätzen GODE-PC-05 und GODE-PC-06 (64 GB RAM, ADR-0006 Nachtrag). Die Zuordnung unten ist die Ausgabe von `knaster placement` mit `sites/knaster/inventory.yaml`.
|
||||||
| `infra` | i7 6./7. Gen, 32 GB | Docker | n8n, LiteLLM, Postgres+pgvector, Redis, Router, Healer, Langfuse, Prometheus/Grafana |
|
|
||||||
| `strong` | extern | OpenRouter | `tier0-strong`, Skill-Autor (Hermes) |
|
| Rolle | Hardware | Ort | Engine | Aufgabe |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `gpu-main` | RTX 4000 Ada (20 GB) | gode-pc-05 (Windows) | vLLM, Multi-LoRA; unter Windows vorerst llama-server (D3, vorgeschlagen) | Slot `tier1-general` plus Skill-Adapter |
|
||||||
|
| `gpu-flex` | RTX 4000 Ada (20 GB) | gode-pc-06 (Windows) | vLLM / Unsloth; unter Windows vorerst llama-server (D3, vorgeschlagen) | tagsüber zweiter Slot oder Reserve, nachts LoRA-Training |
|
||||||
|
| `gpu-embed` | 2× Quadro P2000 (5 GB) | `gpu-pve44`, `gpu-pve46` | llama.cpp (Embedding/Rerank) | `embed-default`, `rerank-default`, redundant auf zwei Knoten |
|
||||||
|
| `gpu-small` | Quadro P2000 (5 GB) | `gpu-pve48` | llama.cpp | `verifier-small`, `signature-extractor` (3–4B) |
|
||||||
|
| – | 4× Quadro M4000 (8 GB) | je eine in den vier GPU-VMs | – | **vorerst außer Betrieb** (ADR-0006), durchgereicht, aber `disabled`; nur nach Benchmark für llama.cpp-Rollen |
|
||||||
|
| `tts-hq` | teilt die Ada von `gpu-flex` | gode-pc-06 | PyTorch | `tts-de-hq`: Qwen3-TTS 1.7B (ADR-0006), unter Windows erst nach der WSL2-Entscheidung |
|
||||||
|
| `tts-cpu` | Infra-LXC (CPU) | `infra` | Piper (ONNX) | `tts-de`: schnelle Sprachausgabe, Fallback |
|
||||||
|
| `infra` | LXC, 4 Kerne, 8 GB | pve44 in `tank`, HA nach pve52/pve48 | Docker | n8n, Postgres+pgvector, Redis, Router, Healer, Langfuse, Prometheus/Grafana; LiteLLM auf appsrv08 (D4, vorgeschlagen) |
|
||||||
|
| `strong` | extern | – | OpenRouter | `tier0-strong` über den LiteLLM mit PSE auf appsrv08, Skill-Autor (Hermes) |
|
||||||
|
|
||||||
|
Das Placement schlägt für `infra` pve52 vor, weil es CPU-Rollen auf Knoten ohne aktive GPU-Rolle legt. Festgelegt ist pve44 (`sites/knaster/proxmox.yaml`), pve52 ist Replikations- und HA-Ziel.
|
||||||
|
|
||||||
Wichtig: Auf Maxwell und Pascal läuft kein vLLM (das braucht Compute Capability ≥ 7.0), und CUDA 13 unterstützt sie nicht mehr. Daher auf diesen Nodes Treiber und Images pinnen. Auch PyTorch hat dort keine Zukunft mehr (2.14 ist die letzte Version mit CUDA-12-Wheels). Deshalb laufen auf alten Karten nur llama.cpp-Workloads (ADR-0006).
|
Wichtig: Auf Maxwell und Pascal läuft kein vLLM (das braucht Compute Capability ≥ 7.0), und CUDA 13 unterstützt sie nicht mehr. Daher auf diesen Nodes Treiber und Images pinnen. Auch PyTorch hat dort keine Zukunft mehr (2.14 ist die letzte Version mit CUDA-12-Wheels). Deshalb laufen auf alten Karten nur llama.cpp-Workloads (ADR-0006).
|
||||||
|
|
||||||
### 1.4 Netzwerk und Storage (entschieden, siehe ADR-0002)
|
### 1.4 Netzwerk und Storage (entschieden, siehe ADR-0002)
|
||||||
|
|
||||||
Ausgangslage: **1 GbE** zwischen den Nodes, pro physischer Maschine **2–3 SSDs mit 128–256 GB**.
|
Ausgangslage: **1 GbE** zwischen den Knoten, flaches Netz `192.168.100.0/24` (Gateway `.1`, DNS `.120`, kein VLAN). Pro Knoten **eine oder zwei SSDs mit 256 GB**, pve48 zusätzlich eine leere 128-GB-SSD (Tabelle in 1.3).
|
||||||
|
|
||||||
- **Kein Ceph.** Ceph braucht für brauchbare Latenz und Rebuild-Zeiten mindestens 10 GbE und mehr Platz pro OSD. Mit 1 GbE und kleinen SSDs würde jeder Schreibvorgang über das Netz gebremst und ein Rebuild das Netz tagelang belegen.
|
- **Kein Ceph.** Ceph braucht für brauchbare Latenz und Rebuild-Zeiten mindestens 10 GbE und mehr Platz pro OSD. Mit 1 GbE und kleinen SSDs würde jeder Schreibvorgang über das Netz gebremst und ein Rebuild das Netz tagelang belegen.
|
||||||
- **ZFS lokal pro Node.** SSD 1 trägt das Proxmox-System. SSD 2 und 3 bilden, wo vorhanden, einen ZFS-Mirror `tank` für VM-Disks. Auf Nodes mit nur zwei SSDs ist `tank` eine Einzel-SSD, die über Replikation abgesichert wird.
|
- **System auf ext4/LVM-thin, ZFS auf der zweiten SSD** (D1 = Variante B, ADR-0002 Nachtrag). SSD 1 trägt das Proxmox-System mit `local` und `local-lvm`. Die zweite SSD ist auf pve44, pve48 und pve52 der ZFS-Pool `tank` (Einzel-SSD), abgesichert über Replikation. pve46 und pve61 haben nur eine Platte und bleiben ohne ZFS.
|
||||||
- **ZFS-Replikation** (Proxmox-Job, alle 15 Min.) für die Infra-VMs auf einen zweiten Node. Zusammen mit Proxmox HA startet die VM nach einem Node-Ausfall dort mit maximal 15 Min. altem Stand. Für Postgres wird das zusätzlich durch PBS-Backups und WAL-Archivierung abgefedert.
|
- **ZFS-Replikation** (Proxmox-Job, alle 15 Min.) nur zwischen pve44, pve48 und pve52: `infra` liegt auf pve44 in `tank` und wird nach pve52 und pve48 repliziert. Zusammen mit Proxmox HA startet der LXC nach einem Knotenausfall dort mit maximal 15 Min. altem Stand. Für Postgres wird das zusätzlich durch PBS-Backups und WAL-Archivierung abgefedert.
|
||||||
- **Modelle liegen lokal** auf der GPU-VM, in einem eigenen Disk-Image (`models`, nicht repliziert, nicht gesichert). Quelle ist Hugging Face oder ein optionaler **Modell-Mirror** auf einem Infra-Node (HTTP/NFS, nur lesend). 10 GB über 1 GbE dauern etwa 90 s, das ist für Modellwechsel unkritisch.
|
- **Modelle liegen lokal** auf der GPU-VM (auf `local-lvm`), in einem eigenen Disk-Image (`models`, nicht repliziert, nicht gesichert). Quelle ist Hugging Face oder ein optionaler **Modell-Mirror** auf einem Infra-Node (HTTP/NFS, nur lesend). 10 GB über 1 GbE dauern etwa 90 s, das ist für Modellwechsel unkritisch.
|
||||||
- **Proxmox Backup Server** als VM oder LXC auf dem Node mit dem meisten freien SSD-Platz, oder besser auf einer externen Platte bzw. einem NAS. Gesichert werden Postgres, Konfiguration und Datensätze, keine Modellgewichte.
|
- **Proxmox Backup Server** als VM oder LXC auf dem Node mit dem meisten freien SSD-Platz, oder besser auf einer externen Platte bzw. einem NAS. Gesichert werden Postgres, Konfiguration und Datensätze, keine Modellgewichte.
|
||||||
- **Netztrennung per VLAN** (Proxmox SDN): `mgmt` (Proxmox, PBS), `svc` (Dienste untereinander), `ext` (Zugriff von außen auf n8n und Router). Ohne Bandbreitengewinn, aber Replikation und Backups lassen sich zeitlich steuern, damit sie den Inferenz-Traffic nicht stören.
|
- **IP-Adressen:** Die Knoten behalten .207–.214. Gäste bekommen vorerst DHCP-Adressen (D5 zurückgestellt); feste Gast-IPs müssen vor Phase 6 stehen, weil STT und die LiteLLM-Slots IP-Literale nutzen.
|
||||||
|
- **Netztrennung per VLAN** (Proxmox SDN) ist zurückgestellt, bis der Switch das zulässt: `mgmt` (Proxmox, PBS), `svc` (Dienste untereinander), `ext` (Zugriff von außen auf n8n und Router). Ohne Bandbreitengewinn, aber Replikation und Backups ließen sich zeitlich steuern, damit sie den Inferenz-Traffic nicht stören.
|
||||||
|
|
||||||
**Speicherbudget pro Rolle (Richtwerte):**
|
**Speicherbudget pro Rolle (Richtwerte):**
|
||||||
|
|
||||||
@@ -80,7 +95,7 @@ Ausgangslage: **1 GbE** zwischen den Nodes, pro physischer Maschine **2–3 SSDs
|
|||||||
| `gpu-embed` / `gpu-small` | 15–25 GB | Embedding-, Rerank- und kleine GGUF-Modelle (je 0,5–3 GB), Images |
|
| `gpu-embed` / `gpu-small` | 15–25 GB | Embedding-, Rerank- und kleine GGUF-Modelle (je 0,5–3 GB), Images |
|
||||||
| `infra` | 40–80 GB | Postgres (Fälle, Traces), Prometheus (15 Tage Retention), Langfuse |
|
| `infra` | 40–80 GB | Postgres (Fälle, Traces), Prometheus (15 Tage Retention), Langfuse |
|
||||||
|
|
||||||
Die genaue SSD-Verteilung wird beim Inventarlauf erfasst (`sites/knaster/inventory.yaml`).
|
`gpu-main` und `gpu-flex` liegen auf den Windows-Rechnern. Eine GPU-VM hat 40 GB Systemplatte und 60 GB Modellplatte auf `local-lvm` (141 GB je Knoten), `infra` 60 GB in `tank` (238 GB). Die SSD-Verteilung steht in `sites/knaster/inventory.yaml`, die Gäste in `sites/knaster/proxmox.yaml`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -303,6 +318,16 @@ Die Portabilität beruht also auf den **Daten und der Pipeline**, nicht auf Gewi
|
|||||||
- Infrastruktur heilt deterministisch, ohne LLM-Entscheidungen (ADR-0005)
|
- Infrastruktur heilt deterministisch, ohne LLM-Entscheidungen (ADR-0005)
|
||||||
- Alte GPUs nur llama.cpp, M4000 vorerst aus, TTS mit Piper (CPU) und Qwen3-TTS (Ada) (ADR-0006)
|
- Alte GPUs nur llama.cpp, M4000 vorerst aus, TTS mit Piper (CPU) und Qwen3-TTS (Ada) (ADR-0006)
|
||||||
|
|
||||||
|
### Entscheidungen 2026-10
|
||||||
|
|
||||||
|
Aus dem [Aufbauplan](superpowers/plans/2026-10-12-aufbau-cluster-knaster.md), Abschnitt „Entscheidungen“:
|
||||||
|
|
||||||
|
- **D1 Speicher – umgesetzt (12.10.):** Variante B ohne Neuinstallation. System-SSD ext4/LVM-thin mit `local-lvm`, zweite SSD als ZFS-Pool `tank` auf pve44, pve48 und pve52. Replikation und HA nur zwischen diesen drei, pve46 und pve61 ohne ZFS (ADR-0002, Nachtrag).
|
||||||
|
- **D2 GPU als VM – entschieden (12.10.):** eine Ubuntu-24.04-VM pro GPU-Knoten mit allen Karten des Knotens (`gpu-pve44` 244, `gpu-pve46` 246, `gpu-pve48` 248, `gpu-pve52` 252), darin ein Docker-Container pro Rolle (ADR-0001, Nachtrag).
|
||||||
|
- **D3 Ada-Rechner – vorgeschlagen, Bestätigung offen:** GODE-PC-05/06 bleiben Windows und stellen ihre Leistung über die Worker-Registry v2 bereit (ADR-0006, Nachtrag; Umsetzung in Task 6.4).
|
||||||
|
- **D4 LiteLLM – vorgeschlagen, Bestätigung offen:** kein eigener LiteLLM im knaster-Infra. Der Router nutzt den LiteLLM mit PSE auf appsrv08, die lokalen Slots werden dort als Modelle eingetragen.
|
||||||
|
- **D5 IP-Plan – zurückgestellt (12.10.):** Knoten behalten .207–.214, Gäste vorerst per DHCP. Feste Gast-IPs (Vorschlag `.220–.239`) über Reservierungen bzw. einen Ausschluss im DHCP-Server, spätestens vor Phase 6.
|
||||||
|
|
||||||
### Offen
|
### Offen
|
||||||
1. Erstes Basismodell für `tier1-general` (8–14B, gut quantisierbar, LoRA-freundlich, gutes Deutsch). Entscheidung per Kalibrierungslauf.
|
1. Erstes Basismodell für `tier1-general` (8–14B, gut quantisierbar, LoRA-freundlich, gutes Deutsch). Entscheidung per Kalibrierungslauf.
|
||||||
2. Kommen die M4000 zurück? `llama-bench` plus Strommessung am Deployment-Tag (bis dahin außer Betrieb, ADR-0006)
|
2. Kommen die M4000 zurück? `llama-bench` plus Strommessung am Deployment-Tag (bis dahin außer Betrieb, ADR-0006)
|
||||||
|
|||||||
@@ -15,3 +15,12 @@ Im Cluster stecken GPUs aus drei Generationen: Maxwell (M4000), Pascal (P2000) u
|
|||||||
- Der VM-RAM ist bei Passthrough vollständig reserviert. Er wird deshalb knapp bemessen: llama.cpp-VMs 6–8 GB, vLLM-VMs 24–32 GB.
|
- Der VM-RAM ist bei Passthrough vollständig reserviert. Er wird deshalb knapp bemessen: llama.cpp-VMs 6–8 GB, vLLM-VMs 24–32 GB.
|
||||||
- Keine Live-Migration für GPU-VMs. Bei einem Ausfall springt der Router auf einen anderen Slot oder auf Stufe 0.
|
- Keine Live-Migration für GPU-VMs. Bei einem Ausfall springt der Router auf einen anderen Slot oder auf Stufe 0.
|
||||||
- Treiber-Updates laufen pro VM, abgesichert durch einen Snapshot.
|
- Treiber-Updates laufen pro VM, abgesichert durch einen Snapshot.
|
||||||
|
|
||||||
|
## Nachtrag 2026-10-12: eine GPU-VM pro Knoten (D2)
|
||||||
|
- Statt einer VM pro Rolle bekommt jeder GPU-Knoten **eine** Ubuntu-24.04-VM, an die alle Karten des Knotens durchgereicht werden: `gpu-pve44` (vmid 244), `gpu-pve46` (246), `gpu-pve48` (248), `gpu-pve52` (252). Darin läuft pro Rolle ein Docker-Container.
|
||||||
|
- Gründe:
|
||||||
|
- **RAM:** Passthrough pinnt den gesamten VM-RAM. Eine VM mit 12 GB pro Knoten lässt auf 32 GB mehr für Container und Failover übrig als mehrere kleine VMs.
|
||||||
|
- **Nur Maxwell/Pascal im Cluster:** Die Ada-Karten bleiben in den Windows-Rechnern (ADR-0006, Nachtrag). M4000 und P2000 laufen mit demselben Legacy-Treiber (R580, CUDA 12.x) und passen deshalb in eine VM. Getrennte Treiber je Generation sind nicht mehr nötig.
|
||||||
|
- **Host-Kernel ohne NVIDIA:** Der Treiber (R580) kommt als Ubuntu-Paket in der VM und ist per Snapshot rückrollbar. Proxmox-Kernel-Updates berühren ihn nicht.
|
||||||
|
- Mappings heißen `gpu-<karte>-<knoten>` (z. B. `gpu-m4000-pve44`), Quelle ist `sites/knaster/proxmox.yaml`. Die M4000 werden mit durchgereicht, bleiben im Inventar aber `disabled` (ADR-0006).
|
||||||
|
- Folgen: Ein Neustart der GPU-VM trifft alle Rollen des Knotens. Der Router weicht dann auf die zweite `gpu-embed`-Instanz bzw. auf Stufe 0 aus. Die RAM-Richtwerte oben (6–8 GB, 24–32 GB) gelten nicht mehr.
|
||||||
|
|||||||
@@ -16,3 +16,10 @@ Die Nodes sind mit **1 GbE** verbunden. Jede physische Maschine hat 2–3 SSDs m
|
|||||||
- Bei einem Node-Ausfall gehen höchstens 15 Min. Daten der Infra-VMs verloren. Postgres sichert sich zusätzlich über WAL-Archivierung bzw. PBS.
|
- Bei einem Node-Ausfall gehen höchstens 15 Min. Daten der Infra-VMs verloren. Postgres sichert sich zusätzlich über WAL-Archivierung bzw. PBS.
|
||||||
- Modellwechsel verursachen Downloads (10 GB ≈ 90 s über 1 GbE). Das ist akzeptabel.
|
- Modellwechsel verursachen Downloads (10 GB ≈ 90 s über 1 GbE). Das ist akzeptabel.
|
||||||
- Die Speicherbudgets pro Rolle stehen in PLAN.md, Abschnitt 1.4.
|
- Die Speicherbudgets pro Rolle stehen in PLAN.md, Abschnitt 1.4.
|
||||||
|
|
||||||
|
## Nachtrag 2026-10-12: Umsetzung als Variante B (D1)
|
||||||
|
- Keine Neuinstallation. Die System-SSD bleibt bei ext4 mit LVM-thin: `local` für System, ISOs und Vorlagen, `local-lvm` für die Platten der Gäste.
|
||||||
|
- Die zweite SSD ist der ZFS-Pool `tank` (Einzel-SSD, kein Mirror) auf **pve44, pve48 und pve52**. pve46 und pve61 haben nur eine Platte und bleiben ohne ZFS. Die leere 128-GB-SSD `sdc` in pve48 ist noch nicht verplant.
|
||||||
|
- Replikation und HA laufen nur zwischen pve44, pve48 und pve52. `infra` (LXC, vmid 101) liegt auf pve44 in `tank` und wird nach pve52 und pve48 repliziert.
|
||||||
|
- Die GPU-VMs liegen auf `local-lvm` des eigenen Knotens. Sie werden nicht repliziert, Modelle lassen sich neu laden.
|
||||||
|
- Folgen: Fällt eine System-SSD aus, wird der Knoten neu installiert. Gesichert sind dann nur die replizierten Gäste in `tank` und die PBS-Backups. Variante A (Neuinstallation mit ZFS-Mirror, vor Ort) lässt sich später je Knoten nachholen.
|
||||||
|
|||||||
@@ -20,3 +20,9 @@
|
|||||||
## Folgen
|
## Folgen
|
||||||
- Bei `gpu-flex` muss sich der Lifecycle die Karte mit TTS teilen. Während des Trainings gehen sowohl `tier1-flex` als auch `tts-de-hq` auf `unavailable`, und LiteLLM fällt auf `tts-de` zurück.
|
- Bei `gpu-flex` muss sich der Lifecycle die Karte mit TTS teilen. Während des Trainings gehen sowohl `tier1-flex` als auch `tts-de-hq` auf `unavailable`, und LiteLLM fällt auf `tts-de` zurück.
|
||||||
- Die tatsächlichen RTF-Werte misst `knaster bench tts` beim Deployment. Sie ersetzen die Schätzungen im Katalog.
|
- Die tatsächlichen RTF-Werte misst `knaster bench tts` beim Deployment. Sie ersetzen die Schätzungen im Katalog.
|
||||||
|
|
||||||
|
## Nachtrag 2026-10-12: Ada-Rechner bleiben Windows (D3, vorgeschlagen, Bestätigung offen)
|
||||||
|
- Die beiden RTX 4000 Ada (20 GB) stecken nicht in einem Proxmox-Knoten, sondern je eine in den Windows-Arbeitsplätzen GODE-PC-05 und GODE-PC-06 (64 GB RAM). Sie bleiben Windows, Mitarbeitende haben tagsüber Vorrang.
|
||||||
|
- Vorschlag: `LocalLLMWorker.ps1` mit llama-server (CUDA) meldet sich über die Worker-Registry v2. `tier1-general` (gode-pc-05) und `tier1-flex` (gode-pc-06) lösen auf einen Worker auf, der online ist, sonst auf Stufe 0 über den LiteLLM mit PSE.
|
||||||
|
- In `sites/knaster/registry.yaml` behalten die Ada-Slots vorerst ihre Engine, nur `pve_node` und `vmid` entfallen. Die Umstellung folgt in Task 6.4.
|
||||||
|
- Folgen: vLLM und PyTorch laufen unter Windows nicht nativ. `tts-de-hq` (Qwen3-TTS), Multi-LoRA und QLoRA auf `gpu-flex` warten auf eine spätere WSL2-Entscheidung (Phase 9). Bis dahin trägt `tts-de` (Piper) die Sprachausgabe allein.
|
||||||
|
|||||||
@@ -1,38 +1,47 @@
|
|||||||
# Inventar des Clusters "knaster".
|
# Inventar des Clusters "knaster".
|
||||||
# ANNAHME: Die Verteilung der GPUs und SSDs auf die Hosts ist noch nicht erfasst. Bitte beim
|
# Stand 12.10.2026, per SSH geprüft (pve44–pve61). gode-pc-05/06 sind Windows-Arbeitsplätze ohne Proxmox;
|
||||||
# gemeinsamen Deployment mit `knaster inventory collect` auf jedem Node ersetzen.
|
# sie stehen nur hier, damit das Placement die Ada-Rollen kennt (Worker-Registry v2, D3).
|
||||||
|
# Plattenaufteilung (D1 = B): sda System mit `local-lvm`, sdb ZFS `tank` auf pve44, pve48 und pve52.
|
||||||
nodes:
|
nodes:
|
||||||
- name: pve-ada
|
- name: pve44
|
||||||
cpu: {model: "unbekannt", cores: 6, threads: 12}
|
cpu: {model: "Intel Core i7-6700", cores: 4, threads: 8}
|
||||||
memory_gb: 64
|
memory_gb: 32
|
||||||
disks: [{name: sda, size_gb: 256}, {name: sdb, size_gb: 256}]
|
disks: [{name: sda, size_gb: 256}, {name: sdb, size_gb: 256}]
|
||||||
gpus:
|
|
||||||
- {index: 0, name: "NVIDIA RTX 4000 Ada Generation", memory_gb: 20, compute_cap: 8.9}
|
|
||||||
- {index: 1, name: "NVIDIA RTX 4000 Ada Generation", memory_gb: 20, compute_cap: 8.9}
|
|
||||||
- name: pve1
|
|
||||||
cpu: {model: "Intel Core i7 (6. Gen) 3.4 GHz", cores: 4, threads: 8}
|
|
||||||
memory_gb: 32
|
|
||||||
disks: [{name: sda, size_gb: 128}, {name: sdb, size_gb: 256}]
|
|
||||||
gpus:
|
gpus:
|
||||||
- {index: 0, name: "Quadro M4000", memory_gb: 8, compute_cap: 5.2, disabled: true, note: "außer Betrieb bis llama-bench + Strommessung (ADR-0006)"}
|
- {index: 0, name: "Quadro M4000", memory_gb: 8, compute_cap: 5.2, disabled: true, note: "außer Betrieb bis llama-bench + Strommessung (ADR-0006)"}
|
||||||
- {index: 1, name: "Quadro M4000", memory_gb: 8, compute_cap: 5.2, disabled: true, note: "außer Betrieb bis llama-bench + Strommessung (ADR-0006)"}
|
|
||||||
- name: pve2
|
|
||||||
cpu: {model: "Intel Core i7 (7. Gen) 3.6 GHz", cores: 4, threads: 8}
|
|
||||||
memory_gb: 32
|
|
||||||
disks: [{name: sda, size_gb: 128}, {name: sdb, size_gb: 256}, {name: sdc, size_gb: 256}]
|
|
||||||
gpus:
|
|
||||||
- {index: 0, name: "Quadro M4000", memory_gb: 8, compute_cap: 5.2, disabled: true, note: "außer Betrieb bis llama-bench + Strommessung (ADR-0006)"}
|
|
||||||
- {index: 1, name: "Quadro M4000", memory_gb: 8, compute_cap: 5.2, disabled: true, note: "außer Betrieb bis llama-bench + Strommessung (ADR-0006)"}
|
|
||||||
- name: pve3
|
|
||||||
cpu: {model: "Intel Core i7 (7. Gen) 3.6 GHz", cores: 4, threads: 8}
|
|
||||||
memory_gb: 32
|
|
||||||
disks: [{name: sda, size_gb: 128}, {name: sdb, size_gb: 256}]
|
|
||||||
gpus:
|
|
||||||
- {index: 0, name: "Quadro P2000", memory_gb: 5, compute_cap: 6.1}
|
|
||||||
- {index: 1, name: "Quadro P2000", memory_gb: 5, compute_cap: 6.1}
|
- {index: 1, name: "Quadro P2000", memory_gb: 5, compute_cap: 6.1}
|
||||||
- name: pve4
|
- name: pve46
|
||||||
cpu: {model: "Intel Core i7 (7. Gen) 3.6 GHz", cores: 4, threads: 8}
|
cpu: {model: "Intel Core i7-6700", cores: 4, threads: 8}
|
||||||
memory_gb: 32
|
memory_gb: 32
|
||||||
disks: [{name: sda, size_gb: 128}, {name: sdb, size_gb: 256}, {name: sdc, size_gb: 256}]
|
disks: [{name: sda, size_gb: 256}]
|
||||||
gpus:
|
gpus:
|
||||||
- {index: 0, name: "Quadro P2000", memory_gb: 5, compute_cap: 6.1}
|
- {index: 0, name: "Quadro M4000", memory_gb: 8, compute_cap: 5.2, disabled: true, note: "außer Betrieb bis llama-bench + Strommessung (ADR-0006)"}
|
||||||
|
- {index: 1, name: "Quadro P2000", memory_gb: 5, compute_cap: 6.1}
|
||||||
|
- name: pve48
|
||||||
|
cpu: {model: "Intel Core i7-6700", cores: 4, threads: 8}
|
||||||
|
memory_gb: 32
|
||||||
|
disks: [{name: sda, size_gb: 256}, {name: sdb, size_gb: 256}, {name: sdc, size_gb: 128}] # sdc leer
|
||||||
|
gpus:
|
||||||
|
- {index: 0, name: "Quadro M4000", memory_gb: 8, compute_cap: 5.2, disabled: true, note: "außer Betrieb bis llama-bench + Strommessung (ADR-0006)"}
|
||||||
|
- {index: 1, name: "Quadro P2000", memory_gb: 5, compute_cap: 6.1}
|
||||||
|
- name: pve52
|
||||||
|
cpu: {model: "Intel Core i7-7700", cores: 4, threads: 8}
|
||||||
|
memory_gb: 32
|
||||||
|
disks: [{name: sda, size_gb: 256}, {name: sdb, size_gb: 256}]
|
||||||
|
gpus:
|
||||||
|
- {index: 0, name: "Quadro M4000", memory_gb: 8, compute_cap: 5.2, disabled: true, note: "außer Betrieb bis llama-bench + Strommessung (ADR-0006)"}
|
||||||
|
- name: pve61 # Surface Book, Kiosk (Intel UHD 620, keine NVIDIA)
|
||||||
|
cpu: {model: "Intel Core i5-8350U", cores: 4, threads: 8}
|
||||||
|
memory_gb: 8
|
||||||
|
disks: [{name: nvme0n1, size_gb: 256}]
|
||||||
|
gpus: []
|
||||||
|
- name: gode-pc-05 # Windows, tagsüber Arbeitsplatz; CPU offen (Task 0.1 e2)
|
||||||
|
cpu: {model: "unbekannt"}
|
||||||
|
memory_gb: 64
|
||||||
|
gpus:
|
||||||
|
- {index: 0, name: "NVIDIA RTX 4000 Ada Generation", memory_gb: 20, compute_cap: 8.9}
|
||||||
|
- name: gode-pc-06 # Windows, tagsüber Arbeitsplatz; CPU offen (Task 0.1 e2)
|
||||||
|
cpu: {model: "unbekannt"}
|
||||||
|
memory_gb: 64
|
||||||
|
gpus:
|
||||||
|
- {index: 0, name: "NVIDIA RTX 4000 Ada Generation", memory_gb: 20, compute_cap: 8.9}
|
||||||
|
|||||||
+13
-13
@@ -15,37 +15,37 @@ slots:
|
|||||||
tier: 1
|
tier: 1
|
||||||
device: gpu
|
device: gpu
|
||||||
engine: vllm
|
engine: vllm
|
||||||
node: gpu-main # RTX 4000 Ada #1
|
node: gpu-main # RTX 4000 Ada auf gode-pc-05 (Windows, D3)
|
||||||
litellm_model: tier1-general
|
litellm_model: tier1-general
|
||||||
base_model: qwen3-14b-awq # Startkandidat, Entscheidung per Kalibrierungslauf
|
base_model: qwen3-14b-awq # Startkandidat, Entscheidung per Kalibrierungslauf
|
||||||
max_context: 32768
|
max_context: 32768
|
||||||
effective_context: 16384
|
effective_context: 16384
|
||||||
supports_lora: true
|
supports_lora: true
|
||||||
fallbacks: [tier1-flex]
|
fallbacks: [tier1-flex]
|
||||||
ops: {docker_host: gpu-main, container: vllm, pve_node: pve-ada, vmid: "201"} # VM-IDs/Hosts: ANNAHME bis Deployment
|
ops: {docker_host: gpu-main, container: vllm} # keine Proxmox-VM; Anbindung über Worker-Registry v2 (Task 6.4)
|
||||||
|
|
||||||
tier1-flex:
|
tier1-flex:
|
||||||
tier: 1
|
tier: 1
|
||||||
device: gpu
|
device: gpu
|
||||||
engine: vllm
|
engine: vllm
|
||||||
node: gpu-flex # RTX 4000 Ada #2 (nachts Training → unavailable)
|
node: gpu-flex # RTX 4000 Ada auf gode-pc-06 (nachts Training → unavailable)
|
||||||
litellm_model: tier1-flex
|
litellm_model: tier1-flex
|
||||||
base_model: qwen3-14b-awq
|
base_model: qwen3-14b-awq
|
||||||
max_context: 32768
|
max_context: 32768
|
||||||
effective_context: 16384
|
effective_context: 16384
|
||||||
supports_lora: true
|
supports_lora: true
|
||||||
ops: {docker_host: gpu-flex, container: vllm, pve_node: pve-ada, vmid: "202"} # VM-IDs/Hosts: ANNAHME bis Deployment
|
ops: {docker_host: gpu-flex, container: vllm} # keine Proxmox-VM; Anbindung über Worker-Registry v2 (Task 6.4)
|
||||||
|
|
||||||
verifier-small:
|
verifier-small:
|
||||||
tier: 1
|
tier: 1
|
||||||
device: gpu
|
device: gpu
|
||||||
engine: llamacpp
|
engine: llamacpp
|
||||||
node: gpu-small # Quadro M4000 (Nutzen per Benchmark prüfen)
|
node: gpu-small # Quadro P2000 auf pve48 (Placement 12.10.)
|
||||||
litellm_model: verifier-small
|
litellm_model: verifier-small
|
||||||
base_model: qwen3-4b-q4km
|
base_model: qwen3-4b-q4km
|
||||||
max_context: 8192
|
max_context: 8192
|
||||||
effective_context: 6144
|
effective_context: 6144
|
||||||
ops: {docker_host: gpu-small, container: llamacpp-chat, pve_node: pve3, vmid: "231"} # VM-IDs/Hosts: ANNAHME bis Deployment
|
ops: {docker_host: gpu-pve48, container: llamacpp-chat, pve_node: pve48, vmid: "248"}
|
||||||
|
|
||||||
signature-extractor:
|
signature-extractor:
|
||||||
tier: 1
|
tier: 1
|
||||||
@@ -56,18 +56,18 @@ slots:
|
|||||||
base_model: qwen3-4b-q4km
|
base_model: qwen3-4b-q4km
|
||||||
max_context: 8192
|
max_context: 8192
|
||||||
effective_context: 6144
|
effective_context: 6144
|
||||||
ops: {docker_host: gpu-small, container: llamacpp-chat, pve_node: pve3, vmid: "231"} # VM-IDs/Hosts: ANNAHME bis Deployment
|
ops: {docker_host: gpu-pve48, container: llamacpp-chat, pve_node: pve48, vmid: "248"}
|
||||||
|
|
||||||
embed-default:
|
embed-default:
|
||||||
tier: 1
|
tier: 1
|
||||||
device: gpu
|
device: gpu
|
||||||
engine: llamacpp
|
engine: llamacpp
|
||||||
node: gpu-embed # Quadro P2000
|
node: gpu-embed # Quadro P2000 auf pve44 und pve46 (Anti-Affinität, Placement 12.10.)
|
||||||
litellm_model: embed-default
|
litellm_model: embed-default
|
||||||
base_model: qwen3-embedding-0.6b-q8
|
base_model: qwen3-embedding-0.6b-q8
|
||||||
max_context: 8192
|
max_context: 8192
|
||||||
kind: embedding
|
kind: embedding
|
||||||
ops: {docker_host: gpu-embed-1, container: llamacpp-embed, pve_node: pve3, vmid: "221"} # VM-IDs/Hosts: ANNAHME bis Deployment
|
ops: {docker_host: gpu-pve44, container: llamacpp-embed, pve_node: pve44, vmid: "244"} # zweite Instanz in gpu-pve46
|
||||||
|
|
||||||
rerank-default:
|
rerank-default:
|
||||||
tier: 1
|
tier: 1
|
||||||
@@ -78,7 +78,7 @@ slots:
|
|||||||
base_model: bge-reranker-v2-m3-q8
|
base_model: bge-reranker-v2-m3-q8
|
||||||
max_context: 8192
|
max_context: 8192
|
||||||
kind: rerank
|
kind: rerank
|
||||||
ops: {docker_host: gpu-embed-1, container: llamacpp-rerank, pve_node: pve3, vmid: "221"} # VM-IDs/Hosts: ANNAHME bis Deployment
|
ops: {docker_host: gpu-pve44, container: llamacpp-rerank, pve_node: pve44, vmid: "244"} # zweite Instanz in gpu-pve46
|
||||||
|
|
||||||
# --- Sprachausgabe (ADR-0006) ---
|
# --- Sprachausgabe (ADR-0006) ---
|
||||||
tts-de:
|
tts-de:
|
||||||
@@ -90,17 +90,17 @@ slots:
|
|||||||
base_model: piper-de-thorsten-medium
|
base_model: piper-de-thorsten-medium
|
||||||
max_context: 5000 # Zeichen pro Anfrage (TTS-Dienst-Limit)
|
max_context: 5000 # Zeichen pro Anfrage (TTS-Dienst-Limit)
|
||||||
kind: tts
|
kind: tts
|
||||||
ops: {docker_host: infra, container: tts-piper, pve_node: pve1, vmid: "101"} # ANNAHME bis Deployment
|
ops: {docker_host: infra, container: tts-piper, pve_node: pve44, vmid: "101"} # Knoten laut proxmox.yaml (HA nach pve52/pve48)
|
||||||
|
|
||||||
tts-de-hq:
|
tts-de-hq:
|
||||||
tier: 1
|
tier: 1
|
||||||
device: gpu
|
device: gpu
|
||||||
engine: pytorch
|
engine: pytorch
|
||||||
node: gpu-flex # teilt sich die Ada mit tier1-flex; während Training unavailable
|
node: gpu-flex # teilt sich die Ada auf gode-pc-06 mit tier1-flex; während Training unavailable
|
||||||
litellm_model: tts-de-hq
|
litellm_model: tts-de-hq
|
||||||
base_model: qwen3-tts-1.7b
|
base_model: qwen3-tts-1.7b
|
||||||
max_context: 5000
|
max_context: 5000
|
||||||
kind: tts
|
kind: tts
|
||||||
ops: {docker_host: gpu-flex, container: qwen-tts, pve_node: pve-ada, vmid: "202"} # ANNAHME bis Deployment
|
ops: {docker_host: gpu-flex, container: qwen-tts} # keine Proxmox-VM; Anbindung über Worker-Registry v2 (Task 6.4)
|
||||||
|
|
||||||
adapters: []
|
adapters: []
|
||||||
|
|||||||
Reference in New Issue
Block a user