Files
infra-phytron/server/phy-srv-gpu01/notes/20260710-projektplan.md
T

299 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Projektplan — Setup phy-srv-gpu01 (LLM-Server mit SMB-Wissensbasis)
Date: 2026-07-10 (Stand-Update: 2026-09-03)
Status: **In Umsetzung** — Server geliefert, Basis-Setup + GPU-Treiber fertig
Basiert auf: `20260706-hardware-assessment.md`, `20260706-software-assessment.md`, `20260707-deep-dive.md`
---
## Stand 2026-09-03 (Kurzfassung — Details in den jeweiligen Abschnitten)
**Erledigt:** Server geliefert und im Rack; Basis-Setup (Ubuntu 24.04.4, Nutzer,
Netz, RAID/Filesystem — s. `SETUP.md` im Repo-Root); GPU-Treiber via Ansible-Rolle
`nvidia_gpu` (595er-Branch, **open** Kernel-Module — Blackwell läuft nicht mit den
proprietären Modulen, s. `manuals/20260714-nvidia-driver-install.md`); Docker
installiert. Share-Analyse gelaufen (§2.1).
**Als Nächstes:** CIFS-Mounts + Container-Stack (Rollen `cifs_mounts`, `llm_stack`)
— entspricht Phasen 35 in §3.
**Kritischer offener Punkt:** Der Bestand ist rund **12× größer** als die in §1
gesetzte Schwelle. Ohne Eingrenzung des Korpus (Kundengespräch) ist Phase 5 nicht
sinnvoll umsetzbar — s. §2.1 und §2.6.
## 0. Rahmenbedingungen (fixiert)
| Punkt | Entscheidung |
|---|---|
| Hardware | HPE DL380 Gen12, 2× Xeon 6714P, 128 GB RAM, RTX PRO 6000 96 GB, 2× 960 GB NVMe — s. `HW.md` |
| Betrieb | **Bare Metal**, keine Virtualisierung des LLM-Stacks |
| Nutzer | ~25, gemischt (technisch versiert + Nicht-Techniker) |
| Auth | **AD/LDAP** (Details Service-Account/Basis-DN als Pre-Work klären) |
| Berechtigungen | **Einfach: alle Nutzer sehen den gesamten indizierten Bestand** (Kunde bestätigt; Shares ohne HR-/Personal-Inhalte halten) |
| Sprache | Dokumente & Nutzer überwiegend **Deutsch** → Modell- und Embedding-Wahl daran ausrichten |
| Inference | **vLLM** mit fester GPU-Auslastung (kein Ollama als Basis) |
| Wissensbasis | SMB-Shares (einige TB, PDF/Office), **Sync ist die Kernanforderung** |
| Später | Jira-Anbindung u. a. Workloads; dafür ggf. **MIG** |
| Vorgehen | Setup vollständig über **Ansible** (dieses Repo), PoC vorab: nein — reine Planung |
## 1. Produktentscheidung: Open WebUI statt Onyx
**Empfehlung: Open WebUI** (bestätigt und verschärft die Empfehlung aus dem
Deep Dive §4.7). Ausschlaggebend, Stand 2026-07-10 verifiziert:
1. **Onyx' File-Connector ist reiner manueller Upload** — kein Verzeichnis-Watching,
kein periodischer Re-Sync eines gemounteten Shares
([Doku](https://docs.onyx.app/admins/connectors/official/file)). Die
Kernanforderung „Shares synchron halten" hieße bei Onyx: Eigenbau gegen
deren API. Open WebUI hat dafür das offizielle Sync-Tool
[`oikb`](https://github.com/open-webui/oikb) (inkrementell, Löschungen,
Cron-fähig — Deep Dive §4.3).
2. **Onyx: SSO (OIDC/SAML), User Groups, RBAC, Permission-Sync = Enterprise
Edition (kostenpflichtig)**
([Doku](https://docs.onyx.app/deployment/miscellaneous/enterprise_edition)).
Open WebUI: LDAP/AD-Anbindung und Gruppen sind frei.
3. Onyx bringt einen zweiten schweren Stack mit (Vespa-Index, Postgres,
Worker) — auf 128 GB RAM **neben** vLLM spürbar; widerspricht „it just works".
4. Bedienung: ein UI im ChatGPT-Stil, vorkonfigurierte Assistenten mit fester
Knowledge Base → passt für den gemischten Nutzerkreis.
**Wann die Entscheidung zu revidieren ist** (Trigger, nach der Share-Analyse §2.1):
- Der *relevante* Bestand liegt deutlich über ~10⁵ Dokumenten und lässt sich
nicht sinnvoll filtern → Suchqualität/Skalierung von Open WebUI prüfen,
Onyx (mit Eigenbau-Sync) oder Eigenbau-Pipeline (Deep Dive §4.5) neu bewerten.
- Datei-genaue NTFS-ACLs werden doch Anforderung → Neubewertung (Deep Dive §4.4/4.5).
**Status 2026-09-03:** Trigger 1 ist **eingetreten** (1,26 Mio. Office-Dateien,
§2.1). Die Produktentscheidung bleibt dennoch bei Open WebUI — aber **nur unter
der Bedingung**, dass der Korpus auf einen kuratierten Teilbestand eingegrenzt
wird (§2.6). Ein ungefilterter Vollindex über 1,26 Mio. Dokumente ist mit Open
WebUI nicht sinnvoll zu betreiben (und liefert auch inhaltlich schlechte
Antworten — RAG über einen kompletten, ungeordneten Fileserver retourniert
überwiegend Rauschen).
Jira später: für *Indizierung* von Jira-Inhalten hat Onyx zwar native
Konnektoren, Open WebUI deckt das aber über Tools/MCP (Atlassian-MCP) bzw.
API-Workloads direkt gegen vLLM ab — kein Entscheidungskriterium für heute.
## 2. Phase 0 — Pre-Work (jetzt, ohne Server)
### 2.1 Share-Analyse (wichtigster Punkt — ja, unbedingt vorab)
Read-only-Skript auf einer beliebigen Maschine mit Share-Zugriff (kein
GPU-Server nötig). Zu erheben:
- Volumen & Dateianzahl **pro Share und Dateityp** (pdf/docx/xlsx/…)
- Anteil **gescannter PDFs** (Stichprobe: PDFs ohne Textlayer) → OCR-Bedarf
- Duplikate/Altversionen („Kopie von …", Versionsordner) → Filterregeln
- Änderungsrate (mtime-Verteilung) → Sync-Frequenz
- Anteil nicht extrahierbarer Formate (CAD, Bilder, Archive) → Erwartungsmanagement
- Sonderzeichen/Pfadlängen, Encoding (Umlaute!)
Ergebnis bestimmt: Korpus-Umfang (→ §1-Trigger), OCR-Pipeline ja/nein,
Index-Größe (→ Storage-Check §2.3), Erst-Indexierungsdauer.
**Ergebnis (Lauf 2026-09-03, Skript `scripts/share-analysis.ps1` auf `D:` des
Fileservers Z-FILESERVER; Zahlen ggü. dem Erstlauf 2026-07-14 leicht gewachsen):**
| Kennzahl | Wert | Konsequenz |
|---|---|---|
| Gesamt | **3.338.643 Dateien, 2.231 GB** | Scan lief über `D:` gesamt, nicht je Share → Obergrenze, enthält evtl. nicht freigegebene Daten |
| `office` (extrahierbar) | **1.271.467 Dateien, 591 GB** | **12× über der §1-Schwelle** → Eingrenzung zwingend (§2.6) |
| `other` (unklassifiziert) | 1.576.747 Dateien, 919 GB | größter Block, **noch unidentifiziert**`D-file-types.csv` auswerten |
| Bilder / CAD / Archive / Medien | 403k / 60k / 12,6k / 6,2k Dateien | nicht extrahierbar → Erwartungsmanagement |
| PDFs | **935.186**, davon ~**22 % ohne Textlayer** (Stichprobe 200) | ≈ **200.000 Scan-PDFs → OCR**; eigener Zeit-/GPU-Aufwand, konkurriert mit vLLM |
| Änderungsrate | 94 % älter als 1 Jahr; nur ~158k in den letzten 365 Tagen | Sync-Frequenz unkritisch; **Alter ≠ Irrelevanz** (Datenblätter, Normen) → Filter primär ordner-/inhaltsbasiert, Aktualität nur sekundär |
| Duplikat-/Altversions-Muster | 33.378 Dateien | Filterregeln |
| Pfade > 240 Zeichen | 25.866 | Extraktions-Toolchain gezielt testen |
| Nicht-ASCII-Dateinamen | 126.899 | UTF-8 durchgängig (CIFS-Mount-Optionen → Extraktion → Index) |
**Bug im Erstlauf:** Beim Lauf über `D:` wurden **keine CSVs geschrieben**
(`Export-Csv` scheiterte an `D:` im Dateinamen). Behoben am 2026-09-03; für die
Ordner-Aufschlüsselung neu laufen lassen mit:
```powershell
.\share-analysis.ps1 -Paths "D:" -OutDir .\file-analysis -FolderDepth 2
```
`-FolderDepth 2` liefert Größe/Anzahl je `Abteilungen\<Share>` — genau die
Granularität der Freigaben (s. §2.7) und die Grundlage für die Include-/
Exclude-Liste. Ebenfalls auszuwerten: `D-file-types.csv` zur Auflösung der
`other`-Kategorie (1,58 Mio. Dateien, größter unidentifizierter Block).
### 2.2 Mit dem Kunden zu klären (Checkliste) — Stand 2026-09-03
- [x] AD/LDAP: AD-Gruppe **`llm_users`** wird angelegt. *Offen:* Bind-Service-Account
und Basis-DN (werden für die LDAP-Konfiguration in Open WebUI gebraucht)
- [x] **SMB-Service-Account read-only** wird separat angelegt (kein Nutzer-Account)
- [ ] **Welche Shares/Unterordner in den Index?** — offen, wird besprochen;
**wichtigster Punkt**, s. §2.1/§2.6
- [x] DNS-Name **`chat.phytron.local`** (vorerst); **kein TLS/keine lokalen
Zertifikate** zu Beginn → reines HTTP im LAN. *Hinweis:* AD-Passwörter gehen
damit im Klartext über das Netz; für später ein internes CA-Zertifikat
einplanen (Reverse Proxy ist vorbereitet, nur Zertifikat fehlt)
- [x] Internetzugang: **unbeschränkt**, keine Einschränkung nötig
- [ ] Protokollierung/Datenschutz: **noch zu besprechen** (Chat-Logs =
Verhaltensdaten; Betriebsrat/DSGVO) — vor dem Rollout klären, nicht vor der
technischen Umsetzung
- [x] Betrieb/Wartung/Updates: **wir** (Softbox)
- [x] „Andere Workloads": **Jira vorerst nicht relevant** → MIG bleibt aus, die
volle GPU geht an den LLM-Stack
### 2.3 Storage-Check — ✅ erledigt, unkritisch
Ist-Stand auf dem Server: **876 GB nutzbar** (`/`, LVM), davon 818 GB frei.
Gegenrechnung mit §2.1: selbst ein Vollindex des `office`-Bestands
(1,26 Mio. Dokumente → grob 150250 GB inkl. extrahiertem Text, Embeddings und
Index-Overhead) plus Modelle (~100 GB) passt. Strategie bleibt: **kein lokaler
Spiegel der Shares**, nur Index. **Keine zusätzlichen NVMe nötig.**
Der begrenzende Faktor ist damit *nicht* der Plattenplatz, sondern Retrieval-Qualität
und Indexgröße (§2.6).
### 2.4 Ansible — Stand 2026-09-03
- [x] `run.yml`: Play für `phy_srv_gpu01` (security, docker, nvidia_gpu, compose)
- [x] `nvidia_gpu` — Treiber (595 **open**), Container Toolkit; **läuft auf dem Server**
- [x] **LLM-Stack als Compose-Templates** statt eigener Rolle: über
`ironicbadger.docker_compose_generator` (auf **2.0.1 gepinnt**), analog zum
Homelab-Repo. Fragmente unter `ansible/services/phy-srv-gpu01/`:
`01-vllm`, `02-embeddings`, `03-openwebui` (+ pgvector), `99-network`.
Die Rolle **schreibt nur** `~/docker/compose.yaml`; das Starten bleibt ein
bewusster manueller Schritt.
- [ ] `cifs_mounts` — ro-Mounts, Credentials aus `group_vars/secrets.yml`
(erst sinnvoll, wenn die Freigaben-Auswahl steht, §2.6/§2.7)
- [ ] oikb-Sync (Share → Knowledge Base), ebenfalls nach der Auswahl
- [ ] Reverse Proxy + TLS — bewusst zurückgestellt (§2.2), Stack `04-proxy`
- [x] `group_vars/phy_srv_gpu01.yml`: Overrides (Modell, VRAM-Quoten, LDAP,
Compose-Pfade). **Offen:** echte LDAP-DNs und drei Vault-Secrets
(`vault_owui_secret_key`, `vault_owui_db_password`, `vault_ldap_bind_password`)
Warum Compose-Templates und keine eigene `llm_stack`-Rolle: gleiche Konvention
wie im Homelab-Repo (ein Ordner je Stack, `NN-`-Präfix bestimmt die Reihenfolge),
dadurch weniger Eigenbau und für den Wiedereinstieg vertraut.
### 2.5 Modell-Shortlist — Entscheidung 2026-09-03
**Startmodell: Qwen3-32B FP8.** Begründung: ~35 GB VRAM, damit reichlich Reserve
für KV-Cache (~25 parallele Nutzer) und das Embedding-Modell daneben; gutes
Deutsch; hoher Durchsatz. Alternative bei Qualitätsbedarf: gpt-oss-120b
(MoE, ~6065 GB) — weniger Parallelität, engerer Fit. Embeddings:
bge-m3 / multilingual-e5 (mehrsprachig, zwingend für Deutsch).
Das ist der **Startwert als Rollen-Variable**, keine endgültige Festlegung: Die
finale Wahl fällt gegen das Eval-Set (s. u.) und nach dem vLLM-Smoke-Test.
⚠️ **Offener technischer Punkt:** Die offiziellen vLLM-Docker-Images unterstützen
SM120 (Blackwell Workstation/Server) laut aktueller Quellenlage **nicht
zuverlässig out of the box**; auf RTX-PRO-6000-Systemen werden verbreitet
Nightly- oder Community-Builds eingesetzt
([vLLM-Forum](https://discuss.vllm.ai/t/support-for-rtx-6000-blackwell-96gb-card/1707),
[rtx6kpro](https://github.com/local-inference-lab/rtx6kpro/blob/master/inference-engines/vllm.md)).
Vor dem Bau der Rolle `llm_stack` daher **ein Container manuell auf dem Server
testen** und das funktionierende Image pinnen — dieselbe Fehlerklasse wie beim
Treiber (proprietäre vs. open Kernel-Module).
Kleines deutsches Eval-Set (2030 Fragen mit erwarteten Antworten aus dem
Bestand) mit dem Kunden sammeln — das ist später das Abnahmekriterium.
### 2.6 Korpus eingrenzen (neu, wichtigster offener Punkt)
Die Share-Analyse (§2.1) hat 1,26 Mio. potenziell extrahierbare Dokumente
ergeben. „Alles indizieren" ist weder technisch sinnvoll noch inhaltlich
wünschenswert. Vorschlag für das Kundengespräch:
1. **Ordner statt Alter als Filter.** Aus `D-toplevel-folders.csv` gemeinsam mit
dem Kunden die Ordner markieren, die tatsächlich Wissensbasis sind
(Doku, Normen, Datenblätter, Handbücher, Projektdoku) — Rest bleibt außen vor.
Reiner Aktualitätsfilter wäre falsch: Datenblätter und Normen sind über Jahre
gültig.
2. **Ausschlusslisten** für Duplikate/Altversionen (33k Treffer), Archiv- und
Backup-Ordner, nicht extrahierbare Formate (CAD, Bilder, Medien).
3. **Zielgröße nennen.** Als Orientierung: eine gut funktionierende
Wissensbasis liegt eher im Bereich **10.000100.000 Dokumente**. Das ist
kein hartes Limit, aber der Bereich, in dem Retrieval-Qualität und
Indexierungsdauer beherrschbar bleiben.
4. **Iterativ starten.** Phase 5 zuerst mit *einem* gut abgegrenzten Share/Ordner,
Qualität am Eval-Set (§2.5) messen, dann erweitern. Das erspart einen
tagelangen Fehlversuch über den Gesamtbestand.
**Erwartungsmanagement gegenüber dem Kunden:** Die ursprüngliche Anforderung
lautete sinngemäß „unsere Dokumente auslesen". Realistisch ist „ein kuratierter
Teil unserer Dokumente, dafür mit guten Antworten". Das früh sagen — nicht erst,
wenn der Vollindex schlechte Treffer liefert.
### 2.7 Freigaben-Inventar (Stand 2026-09-03)
`scripts/list-shares.ps1` auf Z-FILESERVER: **30 Freigaben, alle unterhalb `D:`**,
kein DFS. Vorschlag als *Gesprächsgrundlage* — die Zuordnung der Kürzel muss der
Kunde bestätigen, ebenso die endgültige Auswahl:
| Freigabe | Pfad | Vorschlag | Begründung |
|---|---|---|---|
| QM | `D:\Abteilungen\QM` | **rein** | Qualitätsmanagement — Normen, Prüfvorschriften: Kern der Wissensbasis |
| Arbeitssicherheit | `D:\Arbeitssicherheit` | **rein** | Vorschriften, Betriebsanweisungen |
| Competence Center | `D:\Abteilungen\Competence Center` | **rein** | Name deutet auf kuratiertes Wissen |
| EDV | `D:\Abteilungen\EDV` | **rein** | IT-Doku |
| Produktion | `D:\Produktion` | **prüfen** | vermutlich groß; ggf. nur Unterordner mit Doku |
| Projekte | `D:\Projekte` | **prüfen** | vermutlich der größte Block; nur mit Unterordner-Filter sinnvoll |
| Lagerlogistik, Marketing, Kalkulationen, Controlling, Immobilie | div. | **prüfen** | fachlich abhängig, eher zweite Welle |
| AA, AZ, BU, EE, EF, EK, ME, MF, MM, PL, SG, VT | `D:\Abteilungen\*` | **klären** | Abteilungskürzel — Bedeutung und Inhalt vom Kunden bestätigen lassen |
| Zeichnungen | `D:\Abteilungen\Zeichnungen` | **raus** | CAD/Zeichnungen — nicht extrahierbar (s. `cad`/`image` in §2.1) |
| Datenbank | `D:\Datenbank` | **raus** | Datenbank-Dateien, kein Fließtext |
| Tempwork | `D:\Tempwork` | **raus** | temporär, per Definition kein Wissensbestand |
| Benutzer | `D:\Benutzer` | **raus** | persönliche Ablagen — Datenschutz, kein geteiltes Wissen |
| **Personal** | `D:\Abteilungen\Personal` | **raus** | **HR — explizit ausgeschlossen** (§0: „alle sehen alles" gilt nur ohne Personaldaten) |
| **GF** | `D:\Abteilungen\GF` | **raus** | Geschäftsführung — vertraulich |
| **BR** | `D:\Abteilungen\BR` | **raus (klären)** | falls Betriebsrat: besonders schutzwürdig, auf keinen Fall indizieren |
⚠️ **Wichtig:** Weil alle Nutzer alles sehen (§0), ist jede indizierte Freigabe für
alle 25 Nutzer lesbar — unabhängig von den NTFS-Rechten auf dem Fileserver. Die
Ausschlüsse oben sind deshalb keine Kosmetik, sondern die einzige Zugriffskontrolle.
`list-shares.ps1` gibt die NTFS-Gruppen je Freigabe mit aus: Alles, was heute nur
einem eingeschränkten Personenkreis offensteht, gehört **nicht** in den Index.
## 3. Phasen ab Server-Lieferung
| Phase | Inhalt | Ergebnis/Abnahme |
|---|---|---|
| **1. Basis** (Woche 1) | Rack/Strom (600-W-GPU!), iLO, Firmware, RAID, Ubuntu 24.04 LTS, Eintrag in Ansible-Basis-Setup (Security, Pakete, Nutzer) | `just run phy_srv_gpu01` läuft grün |
| **2. GPU-Stack** (Woche 12) | Rolle `nvidia_gpu`: Treiber, Container Toolkit, DCGM; **Burn-in unter Dauerlast** (SM120-Risiken, Deep Dive §1.6); MIG erst mal **aus** | `nvidia-smi` ok, 48 h-Lasttest ohne Reset |
| **3. Inference** (Woche 2) | vLLM-Container (NVIDIA-Build) mit gewähltem Modell, feste VRAM-Quote, Embedding-Server daneben; Benchmark mit Eval-Set | deutsche Antworten ok, Ziel-Parallelität erreicht |
| **4. UI + Auth** (Woche 23) | Open WebUI + pgvector, LDAP-Login, Reverse Proxy + TLS, vorkonfigurierter „Phytron-Assistent" | Login mit AD-Konto, Chat läuft |
| **5. Wissensbasis** (Woche 35) | CIFS-Mounts (ro), Extraktion inkl. OCR-Engine (Tika/Docling), oikb-Sync erst auf **einen Teilbestand**, Qualität prüfen, dann Vollindex (Dauer aus §2.1 abschätzen) | Fragen aus dem Eval-Set werden mit korrekten Quellen beantwortet |
| **6. Pilot → Rollout** (Woche 57) | 35 Pilotnutzer (gemischt), Feedback, dann alle 25; Onboarding-Einseiter (was kann es, was nicht — CAD/Scans!) | Abnahme durch Kunden |
| **7. Betrieb** | Monitoring (GPU/DCGM, Dienste), Backup (Konfig + DBs — Modelle nicht), Update-Runbook `manuals/`, Doku im README | Runbooks vorhanden |
| **später** | Jira & weitere Workloads; **erst dann MIG-Layout** festlegen (bis 4 Instanzen) statt jetzt raten | — |
## 4. Risiken
| Risiko | Umgang |
|---|---|
| ~~Storage zu klein für Index~~ | ✅ erledigt — 876 GB nutzbar, reicht (§2.3) |
| **Korpus 12× über der Planungsschwelle (§2.1)** | **Eingrenzung auf kuratierten Teilbestand (§2.6); ohne Kundenentscheid keine Phase 5** |
| ~166.000 Scan-PDFs → OCR-Aufwand & -Qualität | eigener Arbeitsblock, GPU-Konkurrenz zu vLLM einplanen; Erwartungen dämpfen |
| ~~SM120/Treiber-Kinderkrankheiten~~ | ✅ erledigt — Treiber 595 **open** läuft (proprietäre Module scheitern an `RmInitAdapter`) |
| Kein TLS zu Beginn (§2.2) | AD-Passwörter im Klartext im LAN; internes CA-Zertifikat nachrüsten, Reverse Proxy vorbereiten |
| Software-Stand veraltet bis Lieferung | Versionen **erst bei Installation pinnen**; Wiedereinstiegs-Checkliste |
| Erst-Indexierung dauert Tage | Teilbestand zuerst; Sync nachts; Dauer vorab abschätzen |
| RAM-Konkurrenz Extraktion/OCR vs. vLLM | vLLM hat feste VRAM-Quote; Indexer-Jobs drosseln (nice/cgroups); 128 GB reichen für OWUI-Stack |
| Datenschutz/Betriebsrat bei Chat-Logs | Früh klären (§2.2), Logging-Policy dokumentieren |
| Kein HA — ein Server | Erwartung managen: Wartungsfenster = Dienst weg; Backup-Restore-Runbook |
## 5. Wiedereinstiegs-Checkliste (wenn der Server da ist)
1. [x] Dieses Dokument + Deep Dive lesen; offene Punkte aus §2.2 abhaken
2. [x] Ergebnis der Share-Analyse vorliegen? → §2.1
3. [ ] Neu sichten & **dann erst pinnen**: vLLM-Version (RTX-PRO-6000-/SM120-Support),
Open WebUI + oikb Release Notes, Modell-Shortlist §2.5 — NVIDIA-Treiber ✅ (595-open)
4. [x] Storage-Entscheidung §2.3 → unkritisch, nichts nachzubestellen
5. [ ] Dann Phasenplan §3 von oben abarbeiten; jede Phase = Ansible-Commit + ggf.
Runbook unter `manuals/`
## 6. Bewusst offen gelassen (kein Jetzt-Entscheid nötig)
- Exaktes Modell & Quantisierung, vLLM-Flags, MIG-Layout, OCR-Engine-Wahl,
Monitoring-Stack-Details, Reverse-Proxy-Produkt — alles bei Umsetzung mit
aktuellem Stand entscheiden; Rahmen steht oben.