sites: Inventar und Zielbild an echte Knoten (pve44–pve61) angepasst; ADR-0001/0002/0006 ergänzt
ci / test (push) Successful in 25s

Aufbauplan Task 0.4:
- inventory.yaml: pve44/46/48/52/61 und GODE-PC-05/06 (Ada, Windows, cpu_roles: false)
- proxmox.yaml: eine GPU-VM pro Knoten mit allen Karten (D2), local-lvm/tank (D1 B),
  192.168.100.0/24 ohne VLAN, Gast-IPs .220–.239 (ANNAHME bis Task 0.2)
- registry.yaml/prometheus.yml: ops-Labels auf gpu-pve44/46/48, Ada ohne pve_node/vmid
- tofu: dynamische hostpci, Speicher je Gast; Ansible: pve_gpu_hosts/pve_kiosk,
  Replikation auf mehrere Ziele
- Placement: cpu_roles=false schließt Arbeitsplätze/Kiosk von CPU-Rollen aus
- PLAN §1.3/§1.4/§9 und ADR-Nachträge (D1 entschieden, D2/D3 Bestätigung offen)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-13 01:39:49 +02:00
co-authored by Claude Opus 5.5
parent 8cf27b48ce
commit c81e372c49
20 changed files with 343 additions and 190 deletions
+1 -1
View File
@@ -182,7 +182,7 @@ def plan(nodes: list[NodeProfile], roles: list[RoleSpec], models: list[ModelSpec
missing = role.count - placed
if missing and (not role.needs.gpu or role.cpu_fallback):
mode: Literal["cpu", "cpu_fallback"] = "cpu" if not role.needs.gpu else "cpu_fallback"
candidates = [n for n in nodes if n.memory_gb >= role.needs.min_ram_gb]
candidates = [n for n in nodes if n.cpu_roles and n.memory_gb >= role.needs.min_ram_gb]
# bevorzugt Nodes ohne GPU-Rollen, dann wenig belastete, dann viel RAM
gpu_roles = {a.node for a in result.assignments if a.mode == "gpu"}
candidates.sort(key=lambda n: (n.name in gpu_roles, cpu_load[n.name], -n.memory_gb, n.name))
+1
View File
@@ -43,6 +43,7 @@ class NodeProfile(BaseModel):
disks: list[Disk] = Field(default_factory=list)
gpus: list[GPU] = Field(default_factory=list)
labels: dict[str, str] = Field(default_factory=dict)
cpu_roles: bool = True # False: Arbeitsplatz/Kiosk – keine Infra- oder CPU-Fallback-Rollen, nur eigene GPUs
NVIDIA_SMI_QUERY = ["nvidia-smi", "--query-gpu=index,name,memory.total,compute_cap,driver_version",
+23 -11
View File
@@ -24,7 +24,7 @@ def test_healer_labels_available_on_alerts():
"""Labels, die Healer-Regeln brauchen, müssen von Metrik oder Scrape-Target kommen."""
prom = yaml.safe_load((DEPLOY / "prometheus/prometheus.yml").read_text())
jobs = {j["job_name"]: j for j in prom["scrape_configs"]}
for job in ("node", "gpu", "vllm"):
for job in ("node", "gpu", "llamacpp"):
for sc in jobs[job]["static_configs"]:
assert {"slot", "pve_node", "vmid"} <= set(sc["labels"]), (job, sc)
from knaster.canary import OPS_LABELS
@@ -46,13 +46,17 @@ def test_compose_files_parse(role):
def test_ops_labels_consistent_between_registry_and_prometheus():
"""Das erste Scrape-Ziel eines Slots trägt dieselben ops-Labels wie die Registry (Healer-Ziel)."""
reg = yaml.safe_load((REPO_ROOT / "sites/knaster/registry.yaml").read_text())
prom = yaml.safe_load((DEPLOY / "prometheus/prometheus.yml").read_text())
vllm = {sc["labels"]["slot"]: sc["labels"] for sc in
next(j for j in prom["scrape_configs"] if j["job_name"] == "vllm")["static_configs"]}
for slot, labels in vllm.items():
first: dict[str, dict] = {}
for sc in next(j for j in prom["scrape_configs"] if j["job_name"] == "llamacpp")["static_configs"]:
first.setdefault(sc["labels"]["slot"], sc["labels"])
assert first
for slot, labels in first.items():
ops = reg["slots"][slot]["ops"]
assert ops["vmid"] == labels["vmid"] and ops["container"] == labels["container"]
assert {k: ops[k] for k in ("docker_host", "container", "pve_node", "vmid")} == \
{k: labels[k] for k in ("docker_host", "container", "pve_node", "vmid")}, slot
WORKFLOWS = sorted((REPO_ROOT / "n8n/workflows").glob("*.json"))
@@ -160,19 +164,27 @@ def test_proxmox_site_consistent_with_registry_and_prometheus():
reg = yaml.safe_load((REPO_ROOT / "sites/knaster/registry.yaml").read_text())
vms = site["vms"]
for vm in vms.values():
assert vm["gpu"] in site["pci_mappings"]
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"}
gpus = [vm["gpu"] for vm in vms.values()]
assert vm["node"] in site["nodes"] and site["nodes"][vm["node"]]["gpu_host"]
gpus = [g for vm in vms.values() for g in vm["gpus"]]
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()}
for slot, spec in reg["slots"].items():
ops = spec.get("ops")
if not ops:
ops = spec.get("ops") or {}
if "vmid" not in ops: # z. B. Ada-Worker unter Windows (D3), kein Proxmox-Gast
assert "pve_node" not in ops, slot
continue
name, vm = by_vmid[ops["vmid"]]
assert vm["node"] == ops["pve_node"], slot
assert ops["docker_host"].startswith(name.split("-")[0]), slot
assert ops["docker_host"] == name, slot
for c in site["containers"].values():
if c.get("ha"):
assert site["nodes"][c["node"]]["zfs_tank"] and c["storage"] == "tank"
assert all(site["nodes"][t]["zfs_tank"] for t in c["replicate_to"])
vmids = [str(v["vmid"]) for v in vms.values()] + [str(c["vmid"]) for c in site["containers"].values()]
assert len(vmids) == len(set(vmids))
# vLLM nur auf modernen Karten
+43 -2
View File
@@ -28,29 +28,70 @@ def test_parsers():
assert disks[0].size_gb == 256.1 and not disks[0].rotational
PVE_GPU_HOSTS = {"pve44", "pve46", "pve48", "pve52"}
def test_site_knaster_matches_real_nodes():
"""Aufbauplan Task 0.4: Site-Dateien beschreiben die echten Knoten, nicht mehr die Annahmen pve-ada/pve1–4."""
import ipaddress
from knaster.util import load_yaml
nodes = {n.name: n for n in load_nodes(REPO_ROOT / "sites/knaster/inventory.yaml")}
assert set(nodes) == {"pve44", "pve46", "pve48", "pve52", "pve61", "gode-pc-05", "gode-pc-06"}
pve = load_yaml(REPO_ROOT / "sites/knaster/proxmox.yaml")
old_net = ipaddress.ip_network("10.20.0.0/24")
ips = []
for section in ("vms", "containers"):
ips += [ipaddress.ip_interface(g["ip"]).ip for g in pve[section].values()]
ips += [ipaddress.ip_address(pve["cluster"][k]) for k in ("gateway", "dns")]
assert not [ip for ip in ips if ip in old_net]
assert {m["node"] for m in pve["pci_mappings"].values()} <= PVE_GPU_HOSTS
assert {v["node"] for v in pve["vms"].values()} <= PVE_GPU_HOSTS
# eine GPU-VM pro GPU-Knoten mit allen Karten des Knotens (D2)
assert sorted(v["node"] for v in pve["vms"].values()) == sorted(PVE_GPU_HOSTS)
for vm in pve["vms"].values():
mapped = {name for name, m in pve["pci_mappings"].items() if m["node"] == vm["node"]}
assert set(vm["gpus"]) == mapped
gpus_inv = [g for g in nodes[vm["node"]].gpus]
assert len(vm["gpus"]) == len(gpus_inv)
# Ada-Rechner bleiben Windows-Arbeitsplätze (D3): keine Infra-/CPU-Rollen
assert not nodes["gode-pc-05"].cpu_roles and not nodes["pve61"].cpu_roles
def test_placement_knaster():
roles, models = load_catalog(REPO_ROOT / "catalog")
p = plan(load_nodes(REPO_ROOT / "sites/knaster/inventory.yaml"), roles, models)
by_role = {}
for a in p.assignments:
by_role.setdefault(a.role, []).append(a)
assert {by_role["gpu-main"][0].node, by_role["gpu-flex"][0].node} == {"gode-pc-05", "gode-pc-06"}
assert by_role["gpu-main"][0].gpu_name.startswith("NVIDIA RTX 4000 Ada")
assert "qwen3-14b-awq" in by_role["gpu-main"][0].models_fitting
embed_nodes = {a.node for a in by_role["gpu-embed"]}
assert len(embed_nodes) == 2 # Anti-Affinität
assert all("P2000" in a.gpu_name for a in by_role["gpu-embed"] + by_role["gpu-small"])
assert by_role["infra"][0].mode == "cpu"
assert by_role["infra"][0].mode == "cpu" and by_role["infra"][0].node in PVE_GPU_HOSTS
assert not p.unplaced
# M4000 bewusst außer Betrieb (ADR-0006): weder frei noch belegt
assert p.free_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")}
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
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"].models_fitting == ["piper-de-thorsten-medium"]
def test_nodes_without_cpu_roles_get_no_infra():
"""Arbeitsplätze und Kiosk nehmen keine CPU-Rollen, auch wenn sie den meisten RAM haben."""
roles, models = load_catalog(REPO_ROOT / "catalog")
nodes = [NodeProfile(name="workstation", memory_gb=64, cpu_roles=False),
NodeProfile(name="server", memory_gb=32)]
p = plan(nodes, roles, models)
assert {a.node for a in p.assignments if a.mode in ("cpu", "cpu_fallback")} == {"server"}
def test_placement_other_cluster():
roles, models = load_catalog(REPO_ROOT / "catalog")
p = plan(load_nodes(REPO_ROOT / "sites/example-single-gpu/inventory.yaml"), roles, models)
+31 -35
View File
@@ -1,5 +1,8 @@
# Scrape-Ziele. Die Labels an den GPU-Zielen (slot, docker_host, container, pve_node, vmid) landen in den
# Alerts und damit beim Healer. Hostnamen = DNS-Namen der VMs (ANNAHME bis Deployment).
# Alerts und damit beim Healer. IP-Literale aus sites/knaster/proxmox.yaml (Global Constraints, Aufbauplan).
# ANNAHME bis Task 0.2 (Gast-IPs) und Task 3.3 (welcher Slot auf welcher P2000 läuft).
# Ada-Worker (GODE-PC-05/06, Windows, D3) werden nicht gescrapt: ihre Verfügbarkeit meldet die
# Worker-Registry v2 (Task 6.4), der Canary-Prober des Routers prüft die Slots zusätzlich.
global:
scrape_interval: 30s
evaluation_interval: 30s
@@ -15,52 +18,45 @@ scrape_configs:
metrics_path: /metrics
static_configs: [{targets: ["router:8080"]}]
- job_name: vllm
static_configs:
- targets: ["gpu-main:8000"]
labels: {slot: tier1-general, docker_host: gpu-main, container: vllm, pve_node: pve-ada, vmid: "201"}
- targets: ["gpu-flex:8000"]
labels: {slot: tier1-flex, docker_host: gpu-flex, container: vllm, pve_node: pve-ada, vmid: "202"}
# Erstes Ziel je Slot = Healer-Ziel in sites/knaster/registry.yaml (ops)
- job_name: llamacpp
static_configs:
- targets: ["gpu-embed-1:8081"]
labels: {slot: embed-default, docker_host: gpu-embed-1, container: llamacpp-embed, pve_node: pve3, vmid: "221"}
- targets: ["gpu-embed-1:8082"]
labels: {slot: rerank-default, docker_host: gpu-embed-1, container: llamacpp-rerank, pve_node: pve3, vmid: "221"}
- targets: ["gpu-small:8080"]
labels: {slot: verifier-small, docker_host: gpu-small, container: llamacpp-chat, pve_node: pve3, vmid: "231"}
- targets: ["192.168.100.224:8081"]
labels: {slot: embed-default, docker_host: gpu-pve44, container: llamacpp-embed, pve_node: pve44, vmid: "244"}
- targets: ["192.168.100.226:8081"]
labels: {slot: embed-default, docker_host: gpu-pve46, container: llamacpp-embed, pve_node: pve46, vmid: "246"}
- targets: ["192.168.100.224:8082"]
labels: {slot: rerank-default, docker_host: gpu-pve44, container: llamacpp-rerank, pve_node: pve44, vmid: "244"}
- targets: ["192.168.100.228:8080"]
labels: {slot: verifier-small, docker_host: gpu-pve48, container: llamacpp-chat, pve_node: pve48, vmid: "248"}
- job_name: gpu
static_configs:
- targets: ["gpu-main:9835"]
labels: {slot: tier1-general, pve_node: pve-ada, vmid: "201"}
- targets: ["gpu-flex:9835"]
labels: {slot: tier1-flex, pve_node: pve-ada, vmid: "202"}
- targets: ["gpu-embed-1:9835"]
labels: {slot: embed-default, pve_node: pve3, vmid: "221"}
- targets: ["gpu-embed-2:9835"]
labels: {slot: embed-default, pve_node: pve4, vmid: "222"}
- targets: ["gpu-small:9835"]
labels: {slot: verifier-small, pve_node: pve3, vmid: "231"}
- targets: ["192.168.100.224:9835"]
labels: {slot: embed-default, pve_node: pve44, vmid: "244"}
- targets: ["192.168.100.226:9835"]
labels: {slot: embed-default, pve_node: pve46, vmid: "246"}
- targets: ["192.168.100.228:9835"]
labels: {slot: verifier-small, pve_node: pve48, vmid: "248"}
- targets: ["192.168.100.232:9835"]
labels: {slot: "", pve_node: pve52, vmid: "252"} # nur M4000, außer Betrieb bis Task 3.3
- job_name: node # Labels werden für GpuXidError → Healer (VM-Neustart) gebraucht
static_configs:
- targets: ["gpu-main:9100"]
labels: {slot: tier1-general, pve_node: pve-ada, vmid: "201"}
- targets: ["gpu-flex:9100"]
labels: {slot: tier1-flex, pve_node: pve-ada, vmid: "202"}
- targets: ["gpu-embed-1:9100"]
labels: {slot: embed-default, pve_node: pve3, vmid: "221"}
- targets: ["gpu-embed-2:9100"]
labels: {slot: embed-default, pve_node: pve4, vmid: "222"}
- targets: ["gpu-small:9100"]
labels: {slot: verifier-small, pve_node: pve3, vmid: "231"}
- targets: ["192.168.100.224:9100"]
labels: {slot: embed-default, pve_node: pve44, vmid: "244"}
- targets: ["192.168.100.226:9100"]
labels: {slot: embed-default, pve_node: pve46, vmid: "246"}
- targets: ["192.168.100.228:9100"]
labels: {slot: verifier-small, pve_node: pve48, vmid: "248"}
- targets: ["192.168.100.232:9100"]
labels: {slot: "", pve_node: pve52, vmid: "252"}
- job_name: proxmox
metrics_path: /pve
params: {module: [default]}
static_configs: [{targets: ["pve1", "pve2", "pve3", "pve4", "pve-ada"]}]
static_configs:
- targets: ["192.168.100.210", "192.168.100.209", "192.168.100.212", "192.168.100.207", "192.168.100.214"]
relabel_configs:
- {source_labels: [__address__], target_label: __param_target}
- {source_labels: [__param_target], target_label: instance}
+33 -18
View File
@@ -33,6 +33,7 @@ Ziel: Ein starkes LLM (OpenRouter) erarbeitet neue Aufgaben einmalig und bricht
- **GPU-Nodes als VM.** Grund: Die alten Karten (Maxwell M4000, Pascal P2000) brauchen fest gepinnte Treiber mit CUDA 12.x, die Ada-Karten profitieren von aktuellen Treibern. Mit VMs lebt beides sauber getrennt auf einem Cluster. Treiber-Updates werden per Snapshot rückrollbar.
- **CPU-Infrastruktur als LXC oder leichte VM** (n8n, Postgres, LiteLLM, Monitoring). Das spart RAM auf den i7-Nodes.
- Innerhalb der VMs bzw. LXCs läuft Docker Compose, ein Compose-Projekt pro Rolle.
- **Stand Aufbauplan (D2):** eine GPU-VM pro Knoten mit allen Karten des Knotens statt einer VM pro Rolle. Im Cluster stecken nur noch Maxwell und Pascal, die Ada-Rechner bleiben Windows-Arbeitsplätze (ADR-0001 und ADR-0006, jeweils Nachtrag).
### 1.2 Was Proxmox zusätzlich ermöglicht
@@ -47,30 +48,35 @@ Ziel: Ein starkes LLM (OpenRouter) erarbeitet neue Aufgaben einmalig und bricht
### 1.3 Rollenverteilung (aktuelle Hardware)
Stand: Aufbauplan vom 12.10.2026 (`docs/superpowers/plans/2026-10-12-aufbau-cluster-knaster.md`). Die Knoten stehen in `sites/knaster/inventory.yaml`. Welche P2000 welche Rolle bekommt, legt erst der Benchmark-Tag fest (Task 3.3).
| Rolle | Hardware | Engine | Aufgabe |
|---|---|---|---|
| `gpu-main` | RTX 4000 Ada #1 (20 GB) | vLLM, Multi-LoRA | Slot `tier1-general` plus Skill-Adapter |
| `gpu-flex` | RTX 4000 Ada #2 (20 GB) | vLLM / Unsloth | tagsüber zweiter Slot oder Reserve, nachts LoRA-Training |
| `gpu-embed` | 2× Quadro P2000 (5 GB) | llama.cpp (Embedding/Rerank) | `embed-default`, `rerank-default`, redundant auf zwei Hosts |
| `gpu-small` | Quadro P2000 (5 GB) | llama.cpp | `verifier-small`, `signature-extractor` (3–4B) |
| – | 4× Quadro M4000 (8 GB) | – | **vorerst außer Betrieb** (ADR-0006), nur nach Benchmark für llama.cpp-Rollen |
| `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 |
| `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) |
| `gpu-main` | RTX 4000 Ada (20 GB) in GODE-PC-05, Windows | llama-server über Worker-Registry v2 (D3); vLLM/Multi-LoRA erst ab Phase 9 | Slot `tier1-general`, wenn der Worker online ist, sonst tier0 |
| `gpu-flex` | RTX 4000 Ada (20 GB) in GODE-PC-06, Windows | wie `gpu-main` | `tier1-flex`; Training erst ab Phase 9 |
| `gpu-embed` | 2× Quadro P2000 (5 GB) in `gpu-pve44` und `gpu-pve46` | llama.cpp (Embedding/Rerank) | `embed-default`, `rerank-default`, redundant auf zwei Knoten |
| `gpu-small` | Quadro P2000 (5 GB) in `gpu-pve48` | llama.cpp | `verifier-small`, `signature-extractor` (3–4B) |
| – | 4× Quadro M4000 (8 GB), je eine in pve44/46/48/52 | – | **vorerst außer Betrieb** (ADR-0006), werden aber an die GPU-VM durchgereicht; nach dem Benchmark nur für llama.cpp-Rollen |
| `tts-hq` | teilt die Ada in GODE-PC-06 | PyTorch | `tts-de-hq`: Qwen3-TTS 1.7B, erst ab Phase 9 (WSL2, D3) |
| `tts-cpu` | Infra-LXC (CPU) | Piper (ONNX) | `tts-de`: Sprachausgabe, vorerst einziger TTS-Slot |
| `infra` | LXC `infra` auf pve44 (`tank`, HA nach pve52/pve48) | Docker | Router, Postgres+pgvector, Healer, Prometheus/Grafana/Alertmanager; **kein eigener LiteLLM** (D4) |
| `ops` | LXC auf pve46 | – | Ansible, OpenTofu, Repo-Clone |
| `strong` | extern | OpenRouter **über den LiteLLM mit PSE auf appsrv08** | `tier0-strong`, Skill-Autor (Hermes) |
pve61 (Surface Book, 8 GB) nimmt keine Gäste auf und zeigt nur das Grafana-Dashboard als Kiosk. n8n läuft weiter auf n8n.mindboost.app, LiteLLM auf appsrv08 (D4).
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 mit Nachtrag D1)
Ausgangslage: **1 GbE** zwischen den Nodes, pro physischer Maschine **2–3 SSDs mit 128–256 GB**.
Ausgangslage: **1 GbE**, flaches Netz `192.168.100.0/24`. Die Knoten sind mit ext4 und LVM-thin auf einer System-SSD installiert, kein ZFS.
- **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.
- **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.
- **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.
- **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.
- **Variante B (D1, entschieden 12.10.):** Die System-SSD bleibt (`local`, `local-lvm` für die GPU-VMs). Auf pve44, pve48 und pve52 wird die zweite SSD nach Freigabe zur Einzel-SSD `tank`. pve46 und pve61 bleiben ohne ZFS. Der ARC ist auf 4 GiB begrenzt.
- **ZFS-Replikation** (Proxmox-Job, alle 15 Min.) für HA-Gäste (`infra`) zwischen den `tank`-Knoten. Zusammen mit Proxmox HA startet der Gast nach einem Knotenausfall mit höchstens 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`, 60 GB, 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 `pbs` auf pve52, Datastore auf einem externen Datenträger oder NAS (Task 5.1). Gesichert werden Postgres, Konfiguration und Datensätze, keine Modellgewichte.
- **Netz (D5):** Die Knoten behalten .207–.214. Gäste bekommen feste IPs aus einem Block außerhalb des DHCP-Bereichs, vorgeschlagen ist `.220–.239` (Prüfung in Task 0.2). Konfigurationen nutzen IP-Literale, keine DNS-Namen. **VLANs** (`mgmt`, `svc`, `ext`) kommen erst später, wenn der Switch das zulässt.
**Speicherbudget pro Rolle (Richtwerte):**
@@ -80,7 +86,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 |
| `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`).
Die SSD-Verteilung steht in `sites/knaster/inventory.yaml` (Bestandsaufnahme vom 12.10.).
---
@@ -303,10 +309,19 @@ Die Portabilität beruht also auf den **Daten und der Pipeline**, nicht auf Gewi
- 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)
### Entscheidungen 2026-10 (Aufbauplan)
| # | Thema | Stand |
|---|---|---|
| D1 | Speicher: Variante B, keine Neuinstallation, `tank` auf pve44/48/52 | **entschieden** 12.10. Gelöscht wird erst nach ausdrücklicher Freigabe der alten Windows-Platten |
| D2 | Eine Ubuntu-VM pro GPU-Knoten mit allen Karten (ADR-0001, Nachtrag) | empfohlen, Bestätigung offen (Task 0.3) |
| D3 | Ada-Rechner bleiben Windows, Anbindung über die Worker-Registry v2 (ADR-0006, Nachtrag) | empfohlen, Bestätigung offen |
| D4 | Kein eigener LiteLLM, der Router nutzt den LiteLLM mit PSE auf appsrv08 | empfohlen, Bestätigung offen |
| D5 | Knoten-IPs bleiben, Gäste aus `.220–.239`, kein VLAN | empfohlen, Bestätigung offen. Der IP-Block wird in Task 0.2 geprüft |
### Offen
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)
3. Verifier-Strategie pro Aufgabentyp: Code-Checks, Schema, kleines Judge-Modell, Stichproben durch das starke Modell
4. Schwellwerte: Graduierung (Start 90 %), Fallzahl fürs LoRA-Training (Start 300), Canary-Anteil (Start 10 %)
5. Kanal für den Pflege-Agenten: n8n Chat, Telegram oder Matrix
6. Standort des Proxmox Backup Servers (intern auf SSD oder extern)
6. Ziel-Speicher des Proxmox Backup Servers (Aufbauplan Task 5.1, Empfehlung: externer Datenträger oder NAS)
+13 -1
View File
@@ -1,6 +1,6 @@
# ADR-0001: GPU-Nodes als VM, Infrastruktur als LXC
- Status: angenommen
- Status: angenommen, ergänzt am 2026-10-13 (Nachtrag D2, Bestätigung offen)
- Datum: 2026-10-11
## Kontext
@@ -15,3 +15,15 @@ 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.
- 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.
## Nachtrag 2026-10-13: eine GPU-VM pro Knoten (Aufbauplan D2, Bestätigung offen)
Die echte Hardware (Aufbauplan, Ausgangslage) ändert die Voraussetzungen:
- Im Proxmox-Cluster stecken nur Maxwell und Pascal (pve44, pve46, pve48: M4000 + P2000, pve52: nur M4000). Die beiden RTX 4000 Ada sitzen in Windows-Arbeitsplätzen außerhalb des Clusters (D3, ADR-0006). Das Argument „gemischte Generationen in einem Host“ entfällt.
- Die Knoten haben 32 GB RAM. Eine VM pro Rolle würde den RAM mehrfach fest reservieren.
Deshalb gilt, sobald D2 bestätigt ist:
- **Pro GPU-Knoten eine Ubuntu-24.04-VM** (`gpu-pve44` … `gpu-pve52`, vmid 244–252) mit **allen** Karten des Knotens, 12 GB RAM fest, R580 (`driver_generation: legacy`). Darin laufen die Rollen als Docker-Compose-Dienste.
- Resource Mappings heißen `gpu-<typ>-<knoten>` (z. B. `gpu-p2000-pve44`).
- Ausgeschaltete Karten (M4000) werden trotzdem durchgereicht. So braucht ihr Wiedereinsatz nach dem Benchmark keine Änderung an der VM.
- Infra bleibt LXC (`infra`, vmid 101), dazu kommt ein kleiner `ops`-LXC (vmid 100) für Ansible und OpenTofu.
- Die oben genannten RAM-Richtwerte für vLLM-VMs entfallen. Das RAM-Budget je Knoten steht im Aufbauplan.
+11 -1
View File
@@ -1,6 +1,6 @@
# ADR-0002: ZFS lokal + Replikation statt Ceph
- Status: angenommen
- Status: angenommen, ergänzt am 2026-10-13 (Nachtrag D1, entschieden)
- Datum: 2026-10-11
## Kontext
@@ -16,3 +16,13 @@ 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.
- Modellwechsel verursachen Downloads (10 GB ≈ 90 s über 1 GbE). Das ist akzeptabel.
- Die Speicherbudgets pro Rolle stehen in PLAN.md, Abschnitt 1.4.
## Nachtrag 2026-10-13: Variante B ohne Neuinstallation (Aufbauplan D1, entschieden am 12.10.)
Die Knoten sind bereits mit ext4 und LVM-thin auf der System-SSD installiert. Entschieden ist **Variante B**:
- Die System-SSD bleibt, wie sie ist (`local` und `local-lvm`). Die GPU-VMs liegen auf `local-lvm`.
- Auf **pve44, pve48 und pve52** wird die zweite SSD (bisher altes Windows) nach ausdrücklicher Freigabe remote zur Einzel-SSD `tank` (`ashift=12`, `lz4`). Auf pve48 bleibt `sdc` unberührt.
- **pve46** (eine Platte) und **pve61** (Kiosk) bekommen kein ZFS. Dort laufen nur Gäste ohne HA (GPU-VM, `ops`).
- HA-Gäste (`infra`) liegen auf `tank` und werden alle 15 Min. auf die anderen `tank`-Knoten repliziert.
- Der ZFS-ARC wird auf 4 GiB begrenzt.
- Einen Spiegel für die Systemplatte gibt es nicht. Fällt eine System-SSD aus, wird der Knoten neu installiert, die HA-Gäste laufen repliziert weiter.
- Variante A (Neuinstallation mit ZFS-RAID1 vor Ort) lässt sich später pro Knoten nachholen.
@@ -1,6 +1,6 @@
# ADR-0006: Alte GPUs nur für llama.cpp, M4000 vorerst außer Betrieb, TTS auf Ada und CPU
- Status: angenommen
- Status: angenommen, ergänzt am 2026-10-13 (Nachtrag D3, Bestätigung offen)
- Datum: 2026-10-12
## Kontext
@@ -20,3 +20,9 @@
## 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.
- Die tatsächlichen RTF-Werte misst `knaster bench tts` beim Deployment. Sie ersetzen die Schätzungen im Katalog.
## Nachtrag 2026-10-13: Ada-Rechner bleiben Windows (Aufbauplan D3, Bestätigung offen)
Die beiden RTX 4000 Ada stecken in **GODE-PC-05 und GODE-PC-06** (je eine Karte, 64 GB RAM). Das sind Windows-Arbeitsplätze, tagsüber haben Mitarbeitende Vorrang. Sie laufen schon heute als LLM-Worker (`LocalLLMWorker.ps1`, llama-server mit CUDA).
- Die Slots `tier1-general` und `tier1-flex` lösen künftig über die **Worker-Registry v2** auf einen Worker auf, der gerade online ist. Ist keiner online, geht die Anfrage an tier0 über den PSE-LiteLLM (Aufbauplan Task 6.4). Bis dahin behalten sie in der Registry ihre Engine.
- **vLLM, Multi-LoRA, QLoRA und Qwen3-TTS (`tts-de-hq`)** laufen unter Windows nicht nativ. Sie kommen erst mit einer späteren Entscheidung über WSL2 bzw. nächtliches vLLM (Phase 9). Bis dahin ist `tts-de` (Piper, CPU) der einzige aktive TTS-Slot.
- Im Inventar stehen die Rechner mit `cpu_roles: false`. Das Placement verplant dort nur die GPU und nie Infra- oder CPU-Fallback-Rollen.
+1 -1
View File
@@ -29,7 +29,7 @@ Deterministisch:
- `prefer: largest` wählt die GPU mit dem meisten VRAM (für LLM-Rollen).
- `prefer: smallest_fitting` wählt die kleinste passende GPU (für Embedding und Rerank). So bleiben große Karten frei.
3. Rollen mit `share_with: <rolle>` teilen sich die GPU (bzw. bei `gpu: false` den Node) einer bereits platzierten Rolle. Ein Beispiel ist TTS auf `gpu-flex`.
4. Rollen mit `gpu: false` bzw. `cpu_fallback: true` werden ohne passende GPU den CPU-Nodes zugewiesen, sortiert nach RAM.
4. Rollen mit `gpu: false` bzw. `cpu_fallback: true` werden ohne passende GPU den CPU-Nodes zugewiesen, sortiert nach RAM. Nodes mit `cpu_roles: false` (Arbeitsplätze, Kiosk) sind davon ausgenommen. Dort wird nur die eigene GPU verplant.
5. Pro zugewiesener Rolle werden die Katalogmodelle gelistet, die passen (Engine, VRAM, CC).
6. Ergebnis: `assignments[{role, node, gpu_index?, models_fitting[]}]` und `unplaced[{role, reason}]`.
@@ -138,14 +138,15 @@ Ersetzt PLAN §8 „0 Fundament“ (Teil Inventar) und IMPLEMENTATION „Gemeins
**Interfaces:**
- Produces: Proxmox-Knoten `pve44`, `pve46`, `pve48`, `pve52`, `pve61`, dazu die Inventar-Knoten `gode-pc-05` und `gode-pc-06` (Ada, ohne Proxmox, nur im Inventar). PCI-Mappings `gpu-m4000-pve44`, `gpu-p2000-pve44`, `gpu-m4000-pve46`, `gpu-p2000-pve46`, `gpu-m4000-pve52`, `gpu-m4000-pve48`, `gpu-p2000-pve48`. GPU-VMs `gpu-pve44` (vmid 244), `gpu-pve46` (246), `gpu-pve48` (248), `gpu-pve52` (252). Infra-LXC `infra` (vmid 101). Ansible-Gruppen `pve_all`, `pve_gpu_hosts`, `pve_kiosk`.
- [ ] **Step 1: Failing test.** In `test_inventory_validate.py` den Test `test_site_knaster_matches_real_nodes` anlegen:
- [x] **Step 1: Failing test.** In `test_inventory_validate.py` den Test `test_site_knaster_matches_real_nodes` anlegen:
- Die Inventar-Knoten sind genau `{pve44, pve46, pve48, pve52, pve61, gode-pc-05, gode-pc-06}`.
- `proxmox.yaml` enthält keine IP aus `10.20.0.0/24`.
- Jedes `pci_mappings[*].node` und jedes `vms[*].node` liegt in `{pve44, pve46, pve48, pve52}`.
- [ ] **Step 2:** `make test` → FAIL (alte Annahmen `pve-ada`, `pve1`–`pve4`).
- [ ] **Step 3:** Site-Dateien umschreiben. Werte für IDs und Pfade aus `lspci -nn` und dem Healthcheck: M4000 `10de:13f1` auf `0000:01:00`, P2000 `10de:1c30` auf `0000:02:00`. Gateway `192.168.100.1`, DNS `192.168.100.120`, keine `vlan_svc`, Gast-IPs aus D5. Die M4000 bleiben `disabled: true` bis zum Benchmark-Tag (Task 3.3). Die Ada-Slots (`tier1-general`, `tier1-flex`, `tts-de-hq`) behalten vorerst ihre Engine. In `registry.yaml` entfallen bei ihnen nur `pve_node` und `vmid`. Die Umstellung auf die Worker-Registry folgt in Task 6.4.
- [ ] **Step 4:** `make test && make validate` → PASS. `knaster placement plan --inventory sites/knaster/inventory.yaml` liefert eine Zuordnung ohne Fehler.
- [ ] **Step 5:** Commit `sites: Inventar und Zielbild an echte Knoten (pve44–pve61) angepasst; ADR-0001/0002/0006 ergänzt`.
- [x] **Step 2:** `make test` → FAIL (alte Annahmen `pve-ada`, `pve1`–`pve4`).
- [x] **Step 3:** Site-Dateien umschreiben. Werte für IDs und Pfade aus `lspci -nn` und dem Healthcheck: M4000 `10de:13f1` auf `0000:01:00`, P2000 `10de:1c30` auf `0000:02:00`. Gateway `192.168.100.1`, DNS `192.168.100.120`, keine `vlan_svc`, Gast-IPs aus D5. Die M4000 bleiben `disabled: true` bis zum Benchmark-Tag (Task 3.3). Die Ada-Slots (`tier1-general`, `tier1-flex`, `tts-de-hq`) behalten vorerst ihre Engine. In `registry.yaml` entfallen bei ihnen nur `pve_node` und `vmid`. Die Umstellung auf die Worker-Registry folgt in Task 6.4.
- [x] **Step 4:** `make test && make validate` → PASS. `knaster placement plan --inventory sites/knaster/inventory.yaml` liefert eine Zuordnung ohne Fehler.
- [x] **Step 5:** Commit `sites: Inventar und Zielbild an echte Knoten (pve44–pve61) angepasst; ADR-0001/0002/0006 ergänzt`.
- Ergebnis (13.10.): Zusätzlich angepasst wurden `platform/proxmox/tofu` (mehrere Karten pro VM, Speicher je Gast, `vlan_svc: null`), `playbooks/site.yml` (`pve-host` nur für `pve_gpu_hosts`), die Replikation in `pve-host` (mehrere Ziele) und `deploy/prometheus/prometheus.yml` (IP-Literale, keine Ada-Ziele). Neues Inventarfeld `cpu_roles: false` für die Ada-Rechner und pve61. Damit verplant das Placement dort keine Infra- und keine CPU-Rollen. Hinweis: Das Placement schlägt `infra` auf pve52 vor, weil dort keine Karte aktiv ist. Laut RAM-Budget steht `infra` auf pve44, so ist es in `proxmox.yaml` eingetragen. Nicht mit `tofu validate` und `ansible-lint` geprüft, beides ist lokal nicht installiert. Das wird im ops-LXC nachgeholt (Task 3.1).
---
@@ -234,7 +235,7 @@ Ersetzt GPU-Knoten 3 (Teil Cluster). Mit 5 Stimmen ist **kein QDevice** nötig.
**Wo:** remote · **Files:** Modify `platform/proxmox/ansible/inventory.yml`, `platform/proxmox/ansible/playbooks/site.yml`, Create `platform/proxmox/README.md` (Abschnitt ops-LXC)
- [ ] LXC `ops` (Debian 13, vmid 100, auf pve44, IP aus D5) mit `ansible-core`, `opentofu`, `git` und dem Repo-Clone. Ansible läuft nicht auf dem Windows-Laptop.
- [ ] LXC `ops` (Debian 13, vmid 100, auf pve46 laut RAM-Budget, IP aus D5) mit `ansible-core`, `opentofu`, `git` und dem Repo-Clone. Ansible läuft nicht auf dem Windows-Laptop.
- [ ] `site.yml`: `pve-host` nur für `pve_gpu_hosts` (pve44, pve46, pve48, pve52). pve61 steht in `pve_kiosk` ohne VFIO und Blacklist.
- [ ] Rolle `pve-host` ergänzen: `/etc/modprobe.d/vfio.conf` mit `options vfio-pci ids=<liste>` und `softdep nvidia pre: vfio-pci`. Die Liste enthält die Grafik- **und** die Audio-Funktionen der Karten. Die IDs aus `lspci -nn -s 01:00` und `lspci -nn -s 02:00` auf dem Knoten lesen, nicht aus dem Gedächtnis eintragen; bekannt sind bisher nur die VGA-IDs `10de:13f1` und `10de:1c30`.
- [ ] `ansible-playbook playbooks/site.yml --limit pve_gpu_hosts --check`, danach der echte Lauf. Knoten **einzeln** neu starten.
+2 -2
View File
@@ -7,8 +7,8 @@ Proxmox-spezifische Schicht (austauschbar, siehe PLAN §7.2). Eingabe ist immer
## Ablauf
1. **Inventar:** auf jedem Node `knaster inventory collect` ausführen und `sites/knaster/inventory.yaml` ersetzen. Danach `knaster placement plan --inventory sites/knaster/inventory.yaml` und die Ergebnisse in `proxmox.yaml` übertragen (PCI-Pfade über `lspci -nn | grep -i nvidia`, IOMMU-Gruppen).
2. **Hosts vorbereiten:** `ansible-playbook playbooks/site.yml --limit pve_hosts`. Setzt IOMMU und VFIO und sperrt die NVIDIA-Treiber auf dem Host. Danach die Hosts neu starten. Der ZFS-Pool `tank` wird **nicht** automatisch angelegt (Datenverlustgefahr), die Rolle gibt dafür den Befehl aus.
3. **Template:** pro Node ein Ubuntu-24.04-Cloud-Image als Template mit VMID 9000.
2. **Hosts vorbereiten:** `ansible-playbook playbooks/site.yml --limit pve_gpu_hosts`. Setzt IOMMU und VFIO und sperrt die NVIDIA-Treiber, nur auf GPU-Knoten (pve61 bleibt unberührt, Kiosk). Danach die Hosts neu starten. Der ZFS-Pool `tank` wird **nicht** automatisch angelegt (Datenverlustgefahr), die Rolle gibt dafür den Befehl aus.
3. **Template:** pro GPU-Knoten ein Ubuntu-24.04-Cloud-Image als Template mit VMID 9000.
4. **VMs/LXC:** `cd tofu && tofu init && tofu plan -var proxmox_api_token=...` und danach `tofu apply`.
5. **Gäste:** `ansible-playbook playbooks/site.yml --limit gpu_vms,infra`. Installiert die Treiber pro Generation gepinnt (legacy/modern), das Container-Toolkit, Docker und den Xid-Exporter.
6. **Dienste:** `deploy/compose/<rolle>` auf den jeweiligen Gästen starten.
+25 -14
View File
@@ -1,29 +1,40 @@
# ANNAHME bis Deployment – Hosts und IPs aus sites/knaster/proxmox.yaml
# Hosts und IPs aus sites/knaster/proxmox.yaml (Aufbauplan Task 0.4). Ansible läuft im ops-LXC (Task 3.1).
# Gast-IPs: ANNAHME bis Task 0.2 (DHCP-Ausschluss .220–.239).
all:
vars:
knaster_site: knaster
children:
pve_hosts:
hosts:
pve-ada: {}
pve1: {}
pve2: {}
pve3: {}
pve4: {}
pve_all:
children:
pve_gpu_hosts:
hosts:
pve44: {ansible_host: 192.168.100.210}
pve46: {ansible_host: 192.168.100.209}
pve48: {ansible_host: 192.168.100.212}
pve52: {ansible_host: 192.168.100.207}
pve_kiosk: # keine VFIO-/NVIDIA-Sperre, iGPU bleibt beim Host (Kiosk)
hosts:
pve61: {ansible_host: 192.168.100.214}
vars:
ansible_user: root
gpu_vms:
hosts:
gpu-main: {ansible_host: 10.20.0.201, driver_generation: modern, knaster_role: gpu-vllm}
gpu-flex: {ansible_host: 10.20.0.202, driver_generation: modern, knaster_role: gpu-vllm}
gpu-embed-1: {ansible_host: 10.20.0.221, driver_generation: legacy, knaster_role: gpu-llamacpp, llamacpp_profile: embed}
gpu-embed-2: {ansible_host: 10.20.0.222, driver_generation: legacy, knaster_role: gpu-llamacpp, llamacpp_profile: embed}
gpu-small: {ansible_host: 10.20.0.231, driver_generation: legacy, knaster_role: gpu-llamacpp, llamacpp_profile: chat}
gpu-pve44: {ansible_host: 192.168.100.224}
gpu-pve46: {ansible_host: 192.168.100.226}
gpu-pve48: {ansible_host: 192.168.100.228}
gpu-pve52: {ansible_host: 192.168.100.232}
vars:
ansible_user: knaster
ansible_become: true
driver_generation: legacy # R580, Maxwell/Pascal
knaster_role: gpu-llamacpp
infra:
hosts:
infra: {ansible_host: 10.20.0.101}
infra: {ansible_host: 192.168.100.221}
vars:
ansible_user: root
ops:
hosts:
ops: {ansible_host: 192.168.100.220}
vars:
ansible_user: root
+4 -3
View File
@@ -1,6 +1,7 @@
# Reihenfolge: Hosts vorbereiten → (tofu apply) → GPU-VMs → Infra
- name: Proxmox-Hosts (IOMMU, VFIO, ZFS-Replikation)
hosts: pve_hosts
# Reihenfolge: GPU-Knoten vorbereiten → (tofu apply) → GPU-VMs → Infra
# pve61 (pve_kiosk) bekommt bewusst keine Host-Rolle: keine VFIO-Bindung, keine NVIDIA-Sperre (Review Focus 5).
- name: Proxmox-GPU-Knoten (IOMMU, VFIO, ZFS-Replikation)
hosts: pve_gpu_hosts
roles: [pve-host]
- name: GPU-VMs (Treiber gepinnt, Container-Toolkit, Docker, Xid-Exporter)
@@ -50,21 +50,28 @@
- name: Hinweis, falls tank fehlt (Pool wird bewusst NICHT automatisch angelegt – Datenverlustgefahr)
ansible.builtin.debug:
msg: "Auf {{ inventory_hostname }} fehlt der ZFS-Pool 'tank'. Anlegen z. B.: zpool create -o ashift=12 tank mirror /dev/disk/by-id/<ssd2> /dev/disk/by-id/<ssd3> && pvesm add zfspool tank -pool tank"
when: tank.rc != 0
msg: >-
Auf {{ inventory_hostname }} fehlt der ZFS-Pool 'tank'. Nach Freigabe (D1) und Prüfung mit
`lsblk -o NAME,SIZE,FSTYPE,LABEL /dev/sdb`: zpool create -o ashift=12 -O compression=lz4 -O atime=off
tank /dev/disk/by-id/<ssd2> && pvesm add zfspool tank --pool tank --content images,rootdir
when:
- tank.rc != 0
- knaster_pve.nodes[inventory_hostname].zfs_tank | default(false)
- name: Bestehende Replikationsjobs lesen
ansible.builtin.command: pvesr list
register: pvesr_jobs
changed_when: false
- name: ZFS-Replikation für Infra-Gäste
# Ein Job pro Ziel: <vmid>-0, <vmid>-1, … (Reihenfolge aus replicate_to)
- name: ZFS-Replikation für HA-Gäste
ansible.builtin.command: >
pvesr create-local-job {{ item.value.vmid }}-0 {{ item.value.replicate_to }}
--schedule '{{ knaster_pve.replication_schedule }}' --comment 'knaster {{ item.key }}'
loop: "{{ knaster_pve.containers | dict2items }}"
pvesr create-local-job {{ item.0.value.vmid }}-{{ idx }} {{ item.1 }}
--schedule '{{ knaster_pve.replication_schedule }}' --comment 'knaster {{ item.0.key }}'
loop: "{{ knaster_pve.containers | dict2items | subelements('value.replicate_to', skip_missing=true) }}"
vars:
idx: "{{ item.0.value.replicate_to.index(item.1) }}"
when:
- item.value.node == inventory_hostname
- item.value.replicate_to is defined
- (item.value.vmid | string) + '-0' not in pvesr_jobs.stdout
- item.0.value.node == inventory_hostname
- ((item.0.value.vmid | string) + '-' + (idx | string)) not in pvesr_jobs.stdout
changed_when: true
+23 -16
View File
@@ -41,12 +41,12 @@ resource "proxmox_virtual_environment_vm" "gpu" {
}
efi_disk {
datastore_id = local.site.cluster.storage_vm
datastore_id = try(each.value.storage, local.site.cluster.storage_default)
type = "4m"
}
disk {
datastore_id = local.site.cluster.storage_vm
datastore_id = try(each.value.storage, local.site.cluster.storage_default)
interface = "scsi0"
size = each.value.disk_gb
discard = "on"
@@ -55,7 +55,7 @@ resource "proxmox_virtual_environment_vm" "gpu" {
# Modelle: eigenes Disk-Image, nicht repliziert, nicht gesichert (ADR-0002)
disk {
datastore_id = local.site.cluster.storage_vm
datastore_id = try(each.value.storage, local.site.cluster.storage_default)
interface = "scsi1"
size = each.value.models_disk_gb
discard = "on"
@@ -64,20 +64,24 @@ resource "proxmox_virtual_environment_vm" "gpu" {
replicate = false
}
hostpci {
device = "hostpci0"
mapping = proxmox_virtual_environment_hardware_mapping_pci.gpu[each.value.gpu].name
pcie = true
rombar = true
# Alle Karten des Knotens in eine VM (D2); Reihenfolge = Index in der VM
dynamic "hostpci" {
for_each = each.value.gpus
content {
device = "hostpci${hostpci.key}"
mapping = proxmox_virtual_environment_hardware_mapping_pci.gpu[hostpci.value].name
pcie = true
rombar = true
}
}
network_device {
bridge = local.site.cluster.bridge
vlan_id = local.site.cluster.vlan_svc
vlan_id = local.site.cluster.vlan_svc # null = flaches Netz
}
initialization {
datastore_id = local.site.cluster.storage_vm
datastore_id = try(each.value.storage, local.site.cluster.storage_default)
ip_config {
ipv4 {
address = each.value.ip
@@ -94,16 +98,16 @@ resource "proxmox_virtual_environment_vm" "gpu" {
}
}
resource "proxmox_virtual_environment_container" "infra" {
resource "proxmox_virtual_environment_container" "lxc" {
for_each = local.site.containers
node_name = each.value.node
vm_id = each.value.vmid
tags = ["knaster", "infra"]
tags = ["knaster", each.key]
unprivileged = true
start_on_boot = true
features {
nesting = true # Docker im LXC
nesting = try(each.value.nesting, true) # Docker im LXC
}
cpu {
@@ -115,12 +119,12 @@ resource "proxmox_virtual_environment_container" "infra" {
}
disk {
datastore_id = local.site.cluster.storage_vm
datastore_id = try(each.value.storage, local.site.cluster.storage_default)
size = each.value.disk_gb
}
operating_system {
template_file_id = "${local.site.cluster.storage_iso}:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst"
template_file_id = local.site.cluster.lxc_template
type = "debian"
}
@@ -132,6 +136,9 @@ resource "proxmox_virtual_environment_container" "infra" {
initialization {
hostname = each.key
dns {
servers = [local.site.cluster.dns]
}
ip_config {
ipv4 {
address = each.value.ip
@@ -149,5 +156,5 @@ resource "proxmox_virtual_environment_haresource" "infra" {
resource_id = "ct:${each.value.vmid}"
state = "started"
comment = "knaster ${each.key}"
depends_on = [proxmox_virtual_environment_container.infra]
depends_on = [proxmox_virtual_environment_container.lxc]
}
+2 -2
View File
@@ -2,6 +2,6 @@ output "gpu_vms" {
value = { for k, v in proxmox_virtual_environment_vm.gpu : k => { node = v.node_name, vmid = v.vm_id } }
}
output "infra_containers" {
value = { for k, v in proxmox_virtual_environment_container.infra : k => { node = v.node_name, vmid = v.vm_id } }
output "containers" {
value = { for k, v in proxmox_virtual_environment_container.lxc : k => { node = v.node_name, vmid = v.vm_id } }
}
+50 -33
View File
@@ -1,38 +1,55 @@
# Inventar des Clusters "knaster".
# ANNAHME: Die Verteilung der GPUs und SSDs auf die Hosts ist noch nicht erfasst. Bitte beim
# gemeinsamen Deployment mit `knaster inventory collect` auf jedem Node ersetzen.
# Inventar des Clusters "knaster" – Stand 12.10.2026 (Aufbauplan docs/superpowers/plans/2026-10-12-…, Ausgangslage).
# Quelle: Healthcheck 08.10. + Inventar pve48 12.10. + Angaben des Nutzers. Beim Bootstrap mit
# `knaster inventory collect` gegenprüfen; Abweichungen hier eintragen.
# Speicher: Variante B (D1) – sda = System (local + local-lvm), sdb wird nach Freigabe zum ZFS-Pool `tank`.
nodes:
- name: pve-ada
cpu: {model: "unbekannt", cores: 6, threads: 12}
- name: pve44 # 192.168.100.210, ThinkStation P310
cpu: {model: "Intel Core i7-6700", cores: 4, threads: 8}
memory_gb: 32
disks: [{name: sda, size_gb: 256}, {name: sdb, size_gb: 256}] # sdb: altes Windows (NTFS) → tank
gpus:
- {index: 0, name: "Quadro M4000", memory_gb: 8, compute_cap: 5.2, disabled: true, note: "außer Betrieb bis Benchmark-Tag (Task 3.3, ADR-0006)"}
- {index: 1, name: "Quadro P2000", memory_gb: 5, compute_cap: 6.1}
- name: pve46 # 192.168.100.209, ThinkStation P310, Legacy-Boot, eine Platte (kein ZFS)
cpu: {model: "Intel Core i7-6700", cores: 4, threads: 8}
memory_gb: 32
disks: [{name: sda, size_gb: 256}]
gpus:
- {index: 0, name: "Quadro M4000", memory_gb: 8, compute_cap: 5.2, disabled: true, note: "außer Betrieb bis Benchmark-Tag (Task 3.3, ADR-0006)"}
- {index: 1, name: "Quadro P2000", memory_gb: 5, compute_cap: 6.1}
- name: pve48 # 192.168.100.212, ThinkStation P310, Legacy-Boot
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}] # sdb → tank, sdc bleibt unberührt
gpus:
- {index: 0, name: "Quadro M4000", memory_gb: 8, compute_cap: 5.2, disabled: true, note: "außer Betrieb bis Benchmark-Tag (Task 3.3, ADR-0006)"}
- {index: 1, name: "Quadro P2000", memory_gb: 5, compute_cap: 6.1}
- name: pve52 # 192.168.100.207, ThinkStation P320, nur M4000 verbaut
cpu: {model: "Intel Core i7-7700", cores: 4, threads: 8}
memory_gb: 32
disks: [{name: sda, size_gb: 256}, {name: sdb, size_gb: 256}] # sdb: altes Windows (BitLocker) → tank
gpus:
- {index: 0, name: "Quadro M4000", memory_gb: 8, compute_cap: 5.2, disabled: true, note: "außer Betrieb bis Benchmark-Tag (Task 3.3, ADR-0006)"}
- name: pve61 # 192.168.100.214, Surface Book, LAN über USB-NIC im Dock – nur Kiosk
cpu: {model: "Intel Core i5-8350U", cores: 4, threads: 8}
memory_gb: 8
disks: [{name: nvme0n1, size_gb: 256}]
cpu_roles: false
labels: {platform: proxmox, purpose: kiosk}
# Außerhalb des Proxmox-Clusters (D3): Windows-Arbeitsplätze, tagsüber haben Mitarbeitende Vorrang.
# Leistung kommt über die Worker-Registry v2 (LocalLLMWorker.ps1, llama-server). Nur die eigene GPU wird verplant.
- name: gode-pc-05
cpu: {model: "unbekannt (Task 0.1 e2)", cores: 0, threads: 0}
memory_gb: 64
disks: [{name: sda, size_gb: 256}, {name: sdb, size_gb: 256}]
cpu_roles: false
labels: {platform: windows, worker: worker-registry-v2}
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}]
- name: gode-pc-06
cpu: {model: "unbekannt (Task 0.1 e2)", cores: 0, threads: 0}
memory_gb: 64
cpu_roles: false
labels: {platform: windows, worker: worker-registry-v2}
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: 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}
- name: pve4
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 P2000", memory_gb: 5, compute_cap: 6.1}
- {index: 0, name: "NVIDIA RTX 4000 Ada Generation", memory_gb: 20, compute_cap: 8.9}
+34 -21
View File
@@ -1,31 +1,44 @@
# Proxmox-Zielbild für "knaster" (Eingabe für platform/proxmox/tofu und ansible).
# ANNAHMEN (Hosts, VM-IDs, IPs, PCI-Adressen) bis zum Inventarlauf – siehe docs/IMPLEMENTATION.md.
# Grundlage: Aufbauplan docs/superpowers/plans/2026-10-12-aufbau-cluster-knaster.md (D1 B, D2, D5).
# ANNAHME bis Task 0.2: Gast-IPs aus dem vorgeschlagenen Block .220–.239 (DHCP-Ausschluss noch nicht geprüft).
# ANNAHME bis Task 3.1: PCI-Pfade/IOMMU-Gruppen wie auf pve48 geprüft; auf jedem Knoten mit `lspci -nn` bestätigen.
cluster:
endpoint: https://pve1:8006/
storage_vm: tank # ZFS-Pool auf SSD 2+3 (ADR-0002)
endpoint: https://192.168.100.210:8006/ # pve44, IP-Literal (Global Constraints)
storage_default: local-lvm # D1 B: GPU-VMs auf der System-SSD; HA-Gäste auf `tank` (pve44/48/52)
storage_iso: local
template_vmid: 9000 # Ubuntu 24.04 Cloud-Image-Template (pro Node vorhanden)
template_vmid: 9000 # Ubuntu 24.04 Cloud-Image-Template (pro GPU-Knoten vorhanden)
lxc_template: "local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst" # ANNAHME: Dateiname nach `pveam update` prüfen
bridge: vmbr0
vlan_svc: 20
gateway: 10.20.0.1
dns: 10.20.0.1
vlan_svc: null # flaches Netz, vorerst kein VLAN (D5)
gateway: 192.168.100.1
dns: 192.168.100.120
ssh_public_key_file: ~/.ssh/id_ed25519.pub
pci_mappings: # Proxmox Resource Mappings (Datacenter → Resource Mappings → PCI)
gpu-ada-1: {node: pve-ada, path: "0000:01:00", id: "10de:27b2", subsystem_id: "", iommu_group: 0}
gpu-ada-2: {node: pve-ada, path: "0000:02:00", id: "10de:27b2", subsystem_id: "", iommu_group: 0}
gpu-p2000-1: {node: pve3, path: "0000:01:00", id: "10de:1c30", subsystem_id: "", iommu_group: 0}
gpu-p2000-2: {node: pve3, path: "0000:02:00", id: "10de:1c30", subsystem_id: "", iommu_group: 0}
gpu-p2000-3: {node: pve4, path: "0000:01:00", id: "10de:1c30", subsystem_id: "", iommu_group: 0}
nodes: # Proxmox-Knoten (Cluster-Mitglieder), Ansible-Gruppen siehe platform/proxmox/ansible
pve44: {ip: 192.168.100.210, gpu_host: true, zfs_tank: true}
pve46: {ip: 192.168.100.209, gpu_host: true, zfs_tank: false} # eine Platte
pve48: {ip: 192.168.100.212, gpu_host: true, zfs_tank: true}
pve52: {ip: 192.168.100.207, gpu_host: true, zfs_tank: true}
pve61: {ip: 192.168.100.214, gpu_host: false, zfs_tank: false} # Surface, Kiosk, keine Gäste
vms: # GPU-Rollen als VM (ADR-0001)
gpu-main: {node: pve-ada, vmid: 201, cores: 6, memory_mb: 28672, disk_gb: 40, models_disk_gb: 100, gpu: gpu-ada-1, ip: 10.20.0.201/24, role: gpu-vllm, driver_generation: modern}
gpu-flex: {node: pve-ada, vmid: 202, cores: 6, memory_mb: 28672, disk_gb: 40, models_disk_gb: 100, gpu: gpu-ada-2, ip: 10.20.0.202/24, role: gpu-vllm, driver_generation: modern}
gpu-embed-1: {node: pve3, vmid: 221, cores: 2, memory_mb: 6144, disk_gb: 30, models_disk_gb: 20, gpu: gpu-p2000-1, ip: 10.20.0.221/24, role: gpu-llamacpp, profile: embed, driver_generation: legacy}
gpu-embed-2: {node: pve4, vmid: 222, cores: 2, memory_mb: 6144, disk_gb: 30, models_disk_gb: 20, gpu: gpu-p2000-3, ip: 10.20.0.222/24, role: gpu-llamacpp, profile: embed, driver_generation: legacy}
gpu-small: {node: pve3, vmid: 231, cores: 2, memory_mb: 8192, disk_gb: 30, models_disk_gb: 20, gpu: gpu-p2000-2, ip: 10.20.0.231/24, role: gpu-llamacpp, profile: chat, driver_generation: legacy}
pci_mappings: # Datacenter → Resource Mappings → PCI; Pfad ohne Funktion = Grafik + Audio
gpu-m4000-pve44: {node: pve44, path: "0000:01:00", id: "10de:13f1", subsystem_id: "", iommu_group: 1}
gpu-p2000-pve44: {node: pve44, path: "0000:02:00", id: "10de:1c30", subsystem_id: "", iommu_group: 8}
gpu-m4000-pve46: {node: pve46, path: "0000:01:00", id: "10de:13f1", subsystem_id: "", iommu_group: 1}
gpu-p2000-pve46: {node: pve46, path: "0000:02:00", id: "10de:1c30", subsystem_id: "", iommu_group: 8}
gpu-m4000-pve48: {node: pve48, path: "0000:01:00", id: "10de:13f1", subsystem_id: "", iommu_group: 1}
gpu-p2000-pve48: {node: pve48, path: "0000:02:00", id: "10de:1c30", subsystem_id: "", iommu_group: 8}
gpu-m4000-pve52: {node: pve52, path: "0000:01:00", id: "10de:13f1", subsystem_id: "", iommu_group: 1}
containers: # Infrastruktur als LXC (ADR-0001), HA + ZFS-Replikation (ADR-0002)
infra: {node: pve1, vmid: 101, cores: 4, memory_mb: 12288, disk_gb: 60, ip: 10.20.0.101/24, ha: true, replicate_to: pve2}
vms: # eine GPU-VM pro Knoten mit allen Karten (D2), 12 GB fest (RAM-Budget)
gpu-pve44: {node: pve44, vmid: 244, cores: 4, memory_mb: 12288, disk_gb: 40, models_disk_gb: 60, gpus: [gpu-m4000-pve44, gpu-p2000-pve44], ip: 192.168.100.224/24, role: gpu-llamacpp, driver_generation: legacy}
gpu-pve46: {node: pve46, vmid: 246, cores: 4, memory_mb: 12288, disk_gb: 40, models_disk_gb: 60, gpus: [gpu-m4000-pve46, gpu-p2000-pve46], ip: 192.168.100.226/24, role: gpu-llamacpp, driver_generation: legacy}
gpu-pve48: {node: pve48, vmid: 248, cores: 4, memory_mb: 12288, disk_gb: 40, models_disk_gb: 60, gpus: [gpu-m4000-pve48, gpu-p2000-pve48], ip: 192.168.100.228/24, role: gpu-llamacpp, driver_generation: legacy}
gpu-pve52: {node: pve52, vmid: 252, cores: 4, memory_mb: 12288, disk_gb: 40, models_disk_gb: 60, gpus: [gpu-m4000-pve52], ip: 192.168.100.232/24, role: gpu-llamacpp, driver_generation: legacy}
containers: # LXC (Container zuerst); HA + Replikation nur auf `tank`-Knoten (Task 2.2)
ops: {node: pve46, vmid: 100, cores: 2, memory_mb: 1024, disk_gb: 16, ip: 192.168.100.220/24, storage: local-lvm, nesting: false} # Ansible/OpenTofu, kein HA (RAM-Budget)
infra: {node: pve44, vmid: 101, cores: 4, memory_mb: 8192, disk_gb: 60, ip: 192.168.100.221/24, storage: tank, nesting: true, ha: true, replicate_to: [pve52, pve48]}
# Später, eigene Tasks: pbs (VM 110, pve52, Task 5.1, .222), grp-test (LXC, pve48 auf tank, Phase 8, .223).
replication_schedule: "*/15"
+16 -13
View File
@@ -1,6 +1,9 @@
# Registry für den Cluster "knaster" (SPEC-core §1).
# Clients sprechen nur Slot-Namen an; litellm_model ist der Alias in deploy/litellm/config.yaml.
# ops: Labels für den Healer (Portainer-Endpoint, Container, Proxmox-Node, VM-ID).
# Stand Aufbauplan Task 0.4: P2000-Slots in den GPU-VMs gpu-pve44/46/48 (Zuordnung ANNAHME bis Benchmark-Tag,
# Task 3.3). Ada-Slots laufen auf Windows-Arbeitsplätzen (D3) ohne Proxmox; Umstellung auf die
# Worker-Registry v2 folgt in Task 6.4, bis dahin behalten sie ihre Engine.
slots:
tier0-strong:
tier: 0
@@ -15,37 +18,37 @@ slots:
tier: 1
device: gpu
engine: vllm
node: gpu-main # RTX 4000 Ada #1
node: gpu-main # RTX 4000 Ada, GODE-PC-05 (Windows, D3)
litellm_model: tier1-general
base_model: qwen3-14b-awq # Startkandidat, Entscheidung per Kalibrierungslauf
max_context: 32768
effective_context: 16384
supports_lora: true
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} # kein Proxmox (D3); bis Task 6.4
tier1-flex:
tier: 1
device: gpu
engine: vllm
node: gpu-flex # RTX 4000 Ada #2 (nachts Training → unavailable)
node: gpu-flex # RTX 4000 Ada, GODE-PC-06 (Windows, D3)
litellm_model: tier1-flex
base_model: qwen3-14b-awq
max_context: 32768
effective_context: 16384
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} # kein Proxmox (D3); bis Task 6.4
verifier-small:
tier: 1
device: gpu
engine: llamacpp
node: gpu-small # Quadro M4000 (Nutzen per Benchmark prüfen)
node: gpu-small # Quadro P2000 in gpu-pve48
litellm_model: verifier-small
base_model: qwen3-4b-q4km
max_context: 8192
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"} # ANNAHME bis Task 3.3
signature-extractor:
tier: 1
@@ -56,18 +59,18 @@ slots:
base_model: qwen3-4b-q4km
max_context: 8192
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"} # ANNAHME bis Task 3.3
embed-default:
tier: 1
device: gpu
engine: llamacpp
node: gpu-embed # Quadro P2000
node: gpu-embed # Quadro P2000, redundant in gpu-pve44 und gpu-pve46
litellm_model: embed-default
base_model: qwen3-embedding-0.6b-q8
max_context: 8192
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"} # ANNAHME bis Task 3.3
rerank-default:
tier: 1
@@ -78,7 +81,7 @@ slots:
base_model: bge-reranker-v2-m3-q8
max_context: 8192
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"} # ANNAHME bis Task 3.3
# --- Sprachausgabe (ADR-0006) ---
tts-de:
@@ -90,17 +93,17 @@ slots:
base_model: piper-de-thorsten-medium
max_context: 5000 # Zeichen pro Anfrage (TTS-Dienst-Limit)
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"} # HA: kann nach pve52/pve48 wandern
tts-de-hq:
tier: 1
device: gpu
engine: pytorch
node: gpu-flex # teilt sich die Ada mit tier1-flex; während Training unavailable
node: gpu-flex # teilt sich die Ada mit tier1-flex; unter Windows erst ab Phase 9 (WSL2, D3)
litellm_model: tts-de-hq
base_model: qwen3-tts-1.7b
max_context: 5000
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} # kein Proxmox (D3)
adapters: []