From be6efa7b346b6b92a8b7f4a34cc3939e42211579 Mon Sep 17 00:00:00 2001 From: Petar Cubela Date: Thu, 3 Sep 2026 14:10:14 +0200 Subject: [PATCH] Update projektplan and TODO with share analysis results MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - projektplan: status section (server delivered, driver done), share analysis results in §2.1, customer checklist answers in §2.2, storage resolved in §2.3, new §2.6 on scoping the corpus, updated risks - scripts/list-shares.ps1: enumerate SMB shares incl. paths, permissions and DFS namespaces on the file server - TODO.md: restructured into blocking/server/done Co-Authored-By: Claude Opus 4.8 --- TODO.md | 44 +++++- .../notes/20260710-projektplan.md | 137 ++++++++++++++---- .../phy-z-srv-gpu01/scripts/list-shares.ps1 | 100 +++++++++++++ 3 files changed, 243 insertions(+), 38 deletions(-) create mode 100644 server/phy-z-srv-gpu01/scripts/list-shares.ps1 diff --git a/TODO.md b/TODO.md index 1eb46a9..8a7bd0c 100644 --- a/TODO.md +++ b/TODO.md @@ -2,12 +2,40 @@ ## phy-z-srv-gpu01 -Pre-work per [projektplan](server/phy-z-srv-gpu01/notes/20260710-projektplan.md) (§ references below): +Per [projektplan](server/phy-z-srv-gpu01/notes/20260710-projektplan.md) (§ references below). +Server is delivered; base setup + NVIDIA driver are done (see `SETUP.md`). -- [x] Write a script to analyse phytrons whole file share which should be feeded to the llm: file number, structure, pdf kind, etc. → `server/phy-z-srv-gpu01/scripts/share-analysis.ps1` -- [ ] Run the share analysis on the Windows server; evaluate: corpus size (§1 revisit trigger), scanned-PDF/OCR share, index size estimate -- [ ] Storage decision (§2.3): index estimate vs. ~960 GB usable NVMe — order extra disks **before** delivery if tight -- [ ] Customer checklist (§2.2): AD bind account, read-only SMB service account, share include/exclude list, DNS name + TLS, internet access at install, chat-logging/GDPR (works council), maintenance ownership, concretize "other workloads" -- [ ] Ansible prep (§2.4): `cifs_mounts` and `llm_stack` role skeletons (`nvidia_gpu` exists); fill `group_vars/phy_z_srv_gpu01.yml` overrides; test non-GPU parts in a throwaway VM -- [ ] German eval set (§2.5): collect 20–30 Q&A with the customer — becomes the acceptance criterion -- [ ] At install time: re-check and pin versions (driver / vLLM / Open WebUI / oikb / model shortlist) per re-entry checklist §5 +### Blocking the knowledge base (customer conversation) + +- [ ] **Scope the corpus (§2.6)** — share analysis found 1.26M extractable documents, + ~12× the planning threshold. Decide with the customer which shares/folders + actually form the knowledge base. Everything in Phase 5 depends on this. +- [ ] Evaluate the two CSVs still sitting on the file server: `D-toplevel-folders.csv` + (basis for the include/exclude list) and `D-file-types.csv` (resolve the + 1.58M unidentified `other` files) +- [ ] Run `scripts/list-shares.ps1` on Z-FILESERVER — map shares → local paths, + so the analysis of `D:` can be tied to actual shares +- [ ] Data protection / works council: are chat logs stored? (§2.2) — before rollout +- [ ] AD details still needed: bind service account + base DN (group `llm_users` and + the read-only SMB account are agreed) +- [ ] German eval set (§2.5): 20–30 Q&A with the customer — acceptance criterion + +### Server / Ansible + +- [ ] Role `cifs_mounts` — read-only mounts, credentials from `group_vars/secrets.yml` +- [ ] Role `llm_stack` — docker compose: vLLM (fixed `--gpu-memory-utilization`), + embedding server, Open WebUI + pgvector, reverse proxy, oikb timer. + Tag compose tasks with `compose` so `just compose` works. +- [ ] Pin versions at install time (vLLM / Open WebUI / oikb / model) per §5 +- [ ] OCR pipeline for ~166k scanned PDFs (§2.1) — own work block, competes with vLLM for the GPU +- [ ] TLS: currently plain HTTP on `chat.phytron.local`; retrofit an internal CA + certificate (AD passwords travel in clear text until then) +- [ ] Hostname mismatch: server reports `phy-srv-gpu01`, repo/inventory/label use + `phy-z-srv-gpu01` — align +- [ ] Finish base hardening in `SETUP.md` (ssh, updates) via `just run phy_z_srv_gpu01` + +### Done + +- [x] Share analysis script → `scripts/share-analysis.ps1`, run on 2026-07-14 (§2.1) +- [x] Storage decision (§2.3) — 876 GB usable, no extra disks needed +- [x] NVIDIA driver via role `nvidia_gpu` (595 open kernel modules) diff --git a/server/phy-z-srv-gpu01/notes/20260710-projektplan.md b/server/phy-z-srv-gpu01/notes/20260710-projektplan.md index d297e87..4b1dcf5 100644 --- a/server/phy-z-srv-gpu01/notes/20260710-projektplan.md +++ b/server/phy-z-srv-gpu01/notes/20260710-projektplan.md @@ -1,11 +1,26 @@ # Projektplan — Setup phy-z-srv-gpu01 (LLM-Server mit SMB-Wissensbasis) -Date: 2026-07-10 -Status: Plan — Server-Lieferung in einigen Monaten erwartet +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 3–5 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 | @@ -49,6 +64,14 @@ Deep Dive §4.7). Ausschlaggebend, Stand 2026-07-10 verifiziert: 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. @@ -70,28 +93,54 @@ GPU-Server nötig). Zu erheben: Ergebnis bestimmt: Korpus-Umfang (→ §1-Trigger), OCR-Pipeline ja/nein, Index-Größe (→ Storage-Check §2.3), Erst-Indexierungsdauer. -### 2.2 Mit dem Kunden zu klären (Checkliste) +**Ergebnis (Lauf 2026-07-14, Skript `scripts/share-analysis.ps1` auf `D:` des +Fileservers Z-FILESERVER):** -- [ ] AD/LDAP: Bind-Service-Account, Basis-DN, Gruppe „LLM-Nutzer" -- [ ] **SMB-Service-Account read-only** für die Mounts (kein Nutzer-Account) -- [ ] Welche Shares/Unterordner genau in den Index? Ausschlussliste (HR o. ä.) -- [ ] DNS-Name (z. B. `ki.phytron.local`) + TLS (internes CA-Zertifikat?) -- [ ] Internetzugang des Servers (Modell-/Container-Downloads bei Installation; - danach einschränkbar) -- [ ] Protokollierung/Datenschutz: werden Chats gespeichert? Betriebsrat/DSGVO - früh einbinden -- [ ] Update-/Wartungsfenster und wer den Betrieb nach Übergabe verantwortet -- [ ] „Andere Workloads" konkretisieren (Jira: Inhalte durchsuchen vs. - Aktionen ausführen?) → bestimmt MIG-Layout später +| Kennzahl | Wert | Konsequenz | +|---|---|---| +| Gesamt | **3.322.175 Dateien, 2.224 GB** | Scan lief über `D:` gesamt, nicht je Share → Obergrenze, enthält evtl. nicht freigegebene Daten | +| `office` (extrahierbar) | **1.258.642 Dateien, 587 GB** | **12× über der §1-Schwelle** → Eingrenzung zwingend (§2.6) | +| `other` (unklassifiziert) | 1.575.752 Dateien, 934 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 | **924.076**, davon ~**18 % ohne Textlayer** (Stichprobe 200) | ≈ **166.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.179 Dateien | Filterregeln | +| Pfade > 240 Zeichen | 25.169 | Extraktions-Toolchain gezielt testen | +| Nicht-ASCII-Dateinamen | 126.111 | UTF-8 durchgängig (CIFS-Mount-Optionen → Extraktion → Index) | -### 2.3 Storage-Check (⚠️ vor Bestellung ggf. nachsteuern) +**Noch auszuwerten** (CSVs liegen auf dem Fileserver, noch nicht gesichtet): +`D-toplevel-folders.csv` (Größe/Anzahl je Top-Level-Ordner — die Grundlage für +die Include-/Exclude-Liste) und `D-file-types.csv` (Auflösung der `other`-Kategorie). -2× 960 GB NVMe, vermutlich RAID1 → **~960 GB nutzbar** für OS + Docker + -Modelle (~100 GB) + Vektor-DB + Extraktions-Cache. Strategie: **kein lokaler -Spiegel der Shares** — nur Index (extrahierter Text + Embeddings, erfahrungsgemäß -wenige % des Rohvolumens). Nach der Share-Analyse gegenrechnen; wenn eng: -zwei weitere NVMe nachordern (Slots frei) — das ist **vor** Lieferung am -billigsten zu ändern. +### 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 150–250 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 vorbereiten (im Repo, testbar ohne GPU) @@ -116,6 +165,32 @@ Monaten neu sichten (§ Wiedereinstieg). Kleines deutsches Eval-Set (20–30 Fragen mit erwarteten Antworten aus dem Bestand) schon jetzt 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.000–100.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. + ## 3. Phasen ab Server-Lieferung | Phase | Inhalt | Ergebnis/Abnahme | @@ -133,9 +208,11 @@ sammeln — das ist später das Abnahmekriterium. | Risiko | Umgang | |---|---| -| Storage zu klein für Index (§2.3) | Share-Analyse → ggf. NVMe nachordern **vor** Lieferung | -| Viele gescannte PDFs → OCR-Aufwand & -Qualität | Analyse-Stichprobe; OCR-Phase einplanen; Erwartungen dämpfen | -| SM120/Treiber-Kinderkrankheiten (Deep Dive §1.6) | Treiber ≥ 580, NVIDIA-vLLM-Container, Burn-in in Phase 2, HPE-Support | +| ~~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 | @@ -144,12 +221,12 @@ sammeln — das ist später das Abnahmekriterium. ## 5. Wiedereinstiegs-Checkliste (wenn der Server da ist) -1. Dieses Dokument + Deep Dive lesen; offene Punkte aus §2.2 abhaken -2. Ergebnis der Share-Analyse vorliegen? (sonst zuerst!) -3. Neu sichten & **dann erst pinnen**: vLLM-Version (RTX-PRO-6000-Support), - Open WebUI + oikb Release Notes, NVIDIA-Treiber, Modell-Shortlist §2.5 -4. Storage-Entscheidung §2.3 umgesetzt? -5. Dann Phasenplan §3 von oben abarbeiten; jede Phase = Ansible-Commit + ggf. +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) diff --git a/server/phy-z-srv-gpu01/scripts/list-shares.ps1 b/server/phy-z-srv-gpu01/scripts/list-shares.ps1 new file mode 100644 index 0000000..8db39a1 --- /dev/null +++ b/server/phy-z-srv-gpu01/scripts/list-shares.ps1 @@ -0,0 +1,100 @@ +<# +.SYNOPSIS + Lists all SMB shares of a Windows file server incl. local path, size and + permissions — the basis for the include/exclude decision (projektplan §2.6). + +.DESCRIPTION + Run ON the file server (or against a remote one via -ComputerName). Reports + per share: name, local path, description, share-level permissions, NTFS + groups, plus optional size/file count. Admin shares (C$, ADMIN$, IPC$) are + skipped unless -IncludeAdminShares is given. + + Purely read-only. Requires local admin for Get-SmbShare (part of Windows + Server, no extra module needed). + +.EXAMPLE + .\list-shares.ps1 + +.EXAMPLE + # remote, and with size per share (slow on large shares) + .\list-shares.ps1 -ComputerName Z-FILESERVER -WithSize +#> +param( + [string]$ComputerName = $env:COMPUTERNAME, + [switch]$IncludeAdminShares, + [switch]$WithSize, + [string]$OutDir = (Join-Path (Get-Location) "share-list") +) + +$ErrorActionPreference = 'Continue' +New-Item -ItemType Directory -Path $OutDir -Force | Out-Null + +Write-Host "=== SMB shares on $ComputerName ===" -ForegroundColor Cyan + +$shares = Get-SmbShare -CimSession $ComputerName -ErrorAction Stop +if (-not $IncludeAdminShares) { + $shares = $shares | Where-Object { -not $_.Name.EndsWith('$') } +} + +$result = foreach ($sh in $shares) { + Write-Host (" {0,-25} {1}" -f $sh.Name, $sh.Path) + + # share-level permissions + $sharePerms = try { + (Get-SmbShareAccess -Name $sh.Name -CimSession $ComputerName -ErrorAction Stop | + ForEach-Object { "$($_.AccountName)=$($_.AccessRight)" }) -join '; ' + } catch { 'n/a' } + + # NTFS permissions (top level only) — who actually has access + $ntfsPerms = try { + $uncPath = "\\$ComputerName\$($sh.Name)" + ((Get-Acl -LiteralPath $uncPath -ErrorAction Stop).Access | + ForEach-Object { "$($_.IdentityReference)=$($_.FileSystemRights)" } | + Select-Object -Unique | Select-Object -First 10) -join '; ' + } catch { 'n/a' } + + $sizeGB = $null; $fileCount = $null + if ($WithSize) { + Write-Host " measuring size ..." -ForegroundColor DarkGray + try { + $m = Get-ChildItem -LiteralPath "\\$ComputerName\$($sh.Name)" -Recurse -File -Force -ErrorAction SilentlyContinue | + Measure-Object -Property Length -Sum + $sizeGB = [Math]::Round($m.Sum / 1GB, 2) + $fileCount = $m.Count + } catch { } + } + + [PSCustomObject]@{ + Name = $sh.Name + Path = $sh.Path + Description = $sh.Description + SharePermissions = $sharePerms + NtfsPermissions = $ntfsPerms + SizeGB = $sizeGB + FileCount = $fileCount + } +} + +$csv = Join-Path $OutDir "smb-shares.csv" +$result | Export-Csv $csv -NoTypeInformation -Encoding UTF8 +$result | Format-Table Name, Path, SizeGB, FileCount -AutoSize + +# DFS namespaces (if used) — often the path users actually see +Write-Host "`n=== DFS namespaces ===" -ForegroundColor Cyan +try { + $roots = Get-DfsnRoot -ErrorAction Stop + if ($roots) { + $dfs = foreach ($r in $roots) { + Get-DfsnFolder -Path "$($r.Path)\*" -ErrorAction SilentlyContinue | ForEach-Object { + $t = Get-DfsnFolderTarget -Path $_.Path -ErrorAction SilentlyContinue + [PSCustomObject]@{ DfsPath = $_.Path; Targets = ($t.TargetPath -join '; ') } + } + } + $dfs | Export-Csv (Join-Path $OutDir "dfs-namespaces.csv") -NoTypeInformation -Encoding UTF8 + $dfs | Format-Table -AutoSize + } else { Write-Host " none" } +} catch { + Write-Host " no DFS role / not available on this host" +} + +Write-Host "`nResults in: $OutDir" -ForegroundColor Green