Compare commits
5
Commits
9c38c60783
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93554f5f7a | ||
|
|
80a862efaf | ||
|
|
922352ae61 | ||
|
|
b6b242196d | ||
|
|
75296ce58c |
@@ -40,13 +40,13 @@ server/<hostname>/ # Host-specific docs, scripts, files
|
||||
|
||||
## Servers
|
||||
|
||||
| Hostname | IP | Status |
|
||||
| ---------------- | ------------- | ------------------------------- |
|
||||
| phy-z-srv-jira | 192.168.66.41 | configured |
|
||||
| phy-z-srv-cloud | 192.168.66.66 | configured (Nextcloud) |
|
||||
| phy-z-srv-git | 192.168.66.67 | configured |
|
||||
| phy-z-dmz-sftp01 | 192.168.99.68 | configured |
|
||||
| phy-z-srv-gpu01 | 192.168.66.69 | planned — setup is the next task|
|
||||
| Hostname | IP | Status |
|
||||
| ---------------- | ------------- | ---------------------------------- |
|
||||
| phy-z-srv-jira | 192.168.66.41 | configured |
|
||||
| phy-z-srv-cloud | 192.168.66.66 | configured (Nextcloud) |
|
||||
| phy-z-srv-git | 192.168.66.67 | configured |
|
||||
| phy-z-dmz-sftp01 | 192.168.99.68 | configured |
|
||||
| phy-srv-gpu01 | 192.168.66.69 | in setup — base + GPU driver done |
|
||||
|
||||
## Conventions
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Document all the steps done for the initial generic setup of the server.
|
||||
- [x] Document Server in IT-Glue configurations with all infos: IP address, SN, MAC, Hostname, Set as 'planned', ...
|
||||
- [x] Document iLO of the Server in IT-Glue configurations: IP address, SN, MAC, Hostname, ....
|
||||
- [x] Document iLO license in TI-Glue under 'Licensing'
|
||||
- [x] Label physical server: `phy-z-srv-gpu01`
|
||||
- [x] Label physical server: `phy-srv-gpu01`
|
||||
- [x] iLO setup: Hostname, snmp (v1 disable, configure v3), license was already inserted,
|
||||
- [x] RAID setup of server in BIOS
|
||||
- [ ] OS Install
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# TODO
|
||||
|
||||
## phy-z-srv-gpu01
|
||||
## phy-srv-gpu01
|
||||
|
||||
Per [projektplan](server/phy-z-srv-gpu01/notes/20260710-projektplan.md) (§ references below).
|
||||
Per [projektplan](server/phy-srv-gpu01/notes/20260710-projektplan.md) (§ references below).
|
||||
Server is delivered; base setup + NVIDIA driver are done (see `SETUP.md`).
|
||||
|
||||
### Blocking the knowledge base (customer conversation)
|
||||
@@ -22,21 +22,24 @@ Server is delivered; base setup + NVIDIA driver are done (see `SETUP.md`).
|
||||
|
||||
### Server / Ansible
|
||||
|
||||
- [ ] Add the three secrets to `group_vars/secrets.yml` (`just vault edit`):
|
||||
`vault_owui_secret_key`, `vault_owui_db_password`, `vault_ldap_bind_password`
|
||||
- [ ] Fill the real LDAP bind DN / base DN in `group_vars/phy_srv_gpu01.yml`
|
||||
- [ ] Pin `vllm_image` to a build verified on SM120, then `just compose phy_srv_gpu01`
|
||||
and start the stack manually (see `ansible/services/phy-srv-gpu01/README.md`)
|
||||
- [ ] 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.
|
||||
- [ ] oikb sync (share → knowledge base) once the corpus scope is decided
|
||||
- [ ] Smoke-test a vLLM container on the server (official images may not support SM120)
|
||||
— pin the working image, then build `llm_stack`. Start model: Qwen3-32B FP8
|
||||
- [ ] 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`
|
||||
- [ ] Finish base hardening in `SETUP.md` (ssh, updates) via `just run phy_srv_gpu01`
|
||||
|
||||
### Done
|
||||
|
||||
- [x] Renamed host/group/folder to `phy-srv-gpu01` everywhere
|
||||
- [x] LLM compose stack written (`ansible/services/phy-srv-gpu01/`) — not deployed
|
||||
- [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)
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
# overrides of group_vars/all.yml
|
||||
|
||||
hostname: phy-srv-gpu01
|
||||
|
||||
# geerlingguy.docker
|
||||
docker_users:
|
||||
- "{{ main_username }}"
|
||||
|
||||
# ironicbadger.docker_compose_generator
|
||||
# services/<docker_compose_hostname>/NN-<stack>/compose.yml next to run.yml
|
||||
appdata_path: "/home/{{ main_username }}/appdata"
|
||||
docker_compose_generator_output_path: "/home/{{ main_username }}/docker/"
|
||||
docker_compose_hostname: phy-srv-gpu01
|
||||
docker_compose_generator_uid: "{{ main_uid }}"
|
||||
docker_compose_generator_gid: "{{ main_gid }}"
|
||||
|
||||
# --- LLM stack -------------------------------------------------------------
|
||||
llm_dns_name: chat.phytron.local
|
||||
|
||||
# ⚠️ TODO: pin to a build verified on SM120 (Blackwell) before the first deploy
|
||||
vllm_image: vllm/vllm-openai:latest
|
||||
vllm_model: Qwen/Qwen3-32B-FP8
|
||||
vllm_served_model_name: qwen3-32b
|
||||
vllm_gpu_memory_utilization: "0.80"
|
||||
vllm_max_model_len: "32768"
|
||||
|
||||
embedding_model: BAAI/bge-m3
|
||||
embedding_model_name: bge-m3
|
||||
embeddings_gpu_memory_utilization: "0.10"
|
||||
|
||||
openwebui_image: ghcr.io/open-webui/open-webui:main
|
||||
pgvector_image: pgvector/pgvector:pg16
|
||||
|
||||
# --- AD/LDAP ---------------------------------------------------------------
|
||||
# ⚠️ TODO: real bind DN / base DN from the customer; group llm_users is agreed
|
||||
ldap_server_host: dc.phytron.local
|
||||
ldap_server_port: "389"
|
||||
ldap_bind_dn: "CN=svc-llm,OU=Service,DC=phytron,DC=local"
|
||||
ldap_search_base: "DC=phytron,DC=local"
|
||||
ldap_search_filter: "(&(objectClass=user)(memberOf=CN=llm_users,OU=Groups,DC=phytron,DC=local))"
|
||||
@@ -1,2 +0,0 @@
|
||||
---
|
||||
|
||||
+2
-2
@@ -10,5 +10,5 @@ phy-z-srv-git ansible_host=192.168.66.67
|
||||
[phy_z_dmz_sftp01]
|
||||
phy-z-dmz-sftp01 ansible_host=192.168.99.68
|
||||
|
||||
[phy_z_srv_gpu01]
|
||||
phy-z-srv-gpu01 ansible_host=192.168.66.69
|
||||
[phy_srv_gpu01]
|
||||
phy-srv-gpu01 ansible_host=192.168.66.69
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
---
|
||||
# docker_compose_generator MUST stay pinned — its galaxy tags mix formats
|
||||
# (1.0.x uses a `containers:` data structure, 2.x uses native compose files)
|
||||
# and an unpinned install can silently change the expected layout.
|
||||
roles:
|
||||
#- name: geerlingguy.pip
|
||||
- name: geerlingguy.docker
|
||||
@@ -6,3 +9,4 @@ roles:
|
||||
- name: geerlingguy.security
|
||||
- name: geerlingguy.ntp
|
||||
- name: ironicbadger.docker_compose_generator
|
||||
version: 2.0.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
# Automates server/phy-z-srv-gpu01/manuals/20260714-nvidia-driver-install.md
|
||||
# Automates server/phy-srv-gpu01/manuals/20260714-nvidia-driver-install.md
|
||||
# Requires Docker (geerlingguy.docker) for the container toolkit part.
|
||||
|
||||
- name: Check Secure Boot state
|
||||
|
||||
+6
-1
@@ -69,7 +69,7 @@
|
||||
tags: base
|
||||
|
||||
|
||||
- hosts: phy_z_srv_gpu01
|
||||
- hosts: phy_srv_gpu01
|
||||
become: yes
|
||||
vars_files:
|
||||
- "group_vars/secrets.yml"
|
||||
@@ -88,3 +88,8 @@
|
||||
tags: docker
|
||||
- role: nvidia_gpu
|
||||
tags: gpu
|
||||
# renders services/phy-srv-gpu01/*/compose.yml into ~/docker/compose.yaml.
|
||||
# It only WRITES the file — starting the stack stays a deliberate manual
|
||||
# step (see ansible/services/phy-srv-gpu01/README.md).
|
||||
- role: ironicbadger.docker_compose_generator
|
||||
tags: compose
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
services:
|
||||
vllm:
|
||||
image: "{{ vllm_image }}"
|
||||
container_name: vllm
|
||||
networks:
|
||||
- llmnet
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
# model cache on local disk — tens of GB per model
|
||||
- "{{ appdata_path }}/models/huggingface:/root/.cache/huggingface"
|
||||
environment:
|
||||
- NVIDIA_VISIBLE_DEVICES=all
|
||||
- NVIDIA_DRIVER_CAPABILITIES=compute,utility
|
||||
- "HUGGING_FACE_HUB_TOKEN={{ hf_token | default('') }}"
|
||||
command:
|
||||
- --model
|
||||
- "{{ vllm_model }}"
|
||||
- --served-model-name
|
||||
- "{{ vllm_served_model_name }}"
|
||||
# fixed VRAM share so the embedding server keeps its slice (§0: "it just works")
|
||||
- --gpu-memory-utilization
|
||||
- "{{ vllm_gpu_memory_utilization }}"
|
||||
- --max-model-len
|
||||
- "{{ vllm_max_model_len }}"
|
||||
# vLLM needs a large shared-memory segment; without this it dies on startup
|
||||
ipc: host
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- driver: nvidia
|
||||
count: 1
|
||||
capabilities: [gpu]
|
||||
runtime: nvidia
|
||||
restart: unless-stopped
|
||||
@@ -0,0 +1,37 @@
|
||||
services:
|
||||
# Embeddings run on a second vLLM instance rather than a separate toolchain
|
||||
# (e.g. text-embeddings-inference): whatever vLLM build works on SM120 then
|
||||
# covers embeddings too, instead of having to solve Blackwell support twice.
|
||||
embeddings:
|
||||
image: "{{ vllm_image }}"
|
||||
container_name: embeddings
|
||||
networks:
|
||||
- llmnet
|
||||
ports:
|
||||
- "8001:8000"
|
||||
volumes:
|
||||
- "{{ appdata_path }}/models/huggingface:/root/.cache/huggingface"
|
||||
environment:
|
||||
- NVIDIA_VISIBLE_DEVICES=all
|
||||
- NVIDIA_DRIVER_CAPABILITIES=compute,utility
|
||||
- "HUGGING_FACE_HUB_TOKEN={{ hf_token | default('') }}"
|
||||
command:
|
||||
- --model
|
||||
- "{{ embedding_model }}"
|
||||
- --served-model-name
|
||||
- "{{ embedding_model_name }}"
|
||||
- --task
|
||||
- embed
|
||||
# small fixed slice — the chat model gets the rest
|
||||
- --gpu-memory-utilization
|
||||
- "{{ embeddings_gpu_memory_utilization }}"
|
||||
ipc: host
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- driver: nvidia
|
||||
count: 1
|
||||
capabilities: [gpu]
|
||||
runtime: nvidia
|
||||
restart: unless-stopped
|
||||
@@ -0,0 +1,62 @@
|
||||
services:
|
||||
openwebui:
|
||||
image: "{{ openwebui_image }}"
|
||||
container_name: openwebui
|
||||
networks:
|
||||
- llmnet
|
||||
ports:
|
||||
# plain HTTP for now — no TLS yet (projektplan §2.2). Put a reverse proxy
|
||||
# in front (new stack 04-proxy) once an internal CA certificate exists.
|
||||
- "80:8080"
|
||||
depends_on:
|
||||
- vllm
|
||||
- owui-db
|
||||
volumes:
|
||||
- "{{ appdata_path }}/apps/open-webui:/app/backend/data"
|
||||
environment:
|
||||
- "WEBUI_URL=http://{{ llm_dns_name }}"
|
||||
- "WEBUI_SECRET_KEY={{ vault_owui_secret_key }}"
|
||||
|
||||
# --- inference: vLLM speaks the OpenAI API, Ollama stays off ---
|
||||
- OPENAI_API_BASE_URL=http://vllm:8000/v1
|
||||
- OPENAI_API_KEY=dummy
|
||||
- ENABLE_OLLAMA_API=false
|
||||
|
||||
# --- vector store: pgvector rather than the default chroma, which does
|
||||
# not scale to the corpus sizes discussed in §2.6 ---
|
||||
- VECTOR_DB=pgvector
|
||||
- "PGVECTOR_DB_URL=postgresql://openwebui:{{ vault_owui_db_password }}@owui-db:5432/openwebui"
|
||||
|
||||
# --- RAG embeddings from the second vLLM instance ---
|
||||
- RAG_EMBEDDING_ENGINE=openai
|
||||
- RAG_OPENAI_API_BASE_URL=http://embeddings:8000/v1
|
||||
- RAG_OPENAI_API_KEY=dummy
|
||||
- "RAG_EMBEDDING_MODEL={{ embedding_model_name }}"
|
||||
|
||||
# --- AD/LDAP login (group llm_users) ---
|
||||
# NOTE: verify these variable names against the Open WebUI docs for the
|
||||
# tag actually deployed — they have changed between releases.
|
||||
- ENABLE_LDAP=true
|
||||
- LDAP_SERVER_LABEL=Phytron AD
|
||||
- "LDAP_SERVER_HOST={{ ldap_server_host }}"
|
||||
- "LDAP_SERVER_PORT={{ ldap_server_port }}"
|
||||
- "LDAP_APP_DN={{ ldap_bind_dn }}"
|
||||
- "LDAP_APP_PASSWORD={{ vault_ldap_bind_password }}"
|
||||
- "LDAP_SEARCH_BASE={{ ldap_search_base }}"
|
||||
- "LDAP_SEARCH_FILTER={{ ldap_search_filter }}"
|
||||
- LDAP_ATTRIBUTE_FOR_USERNAME=sAMAccountName
|
||||
- LDAP_USE_TLS=false
|
||||
restart: unless-stopped
|
||||
|
||||
owui-db:
|
||||
image: "{{ pgvector_image }}"
|
||||
container_name: owui-db
|
||||
networks:
|
||||
- llmnet
|
||||
volumes:
|
||||
- "{{ appdata_path }}/databases/openwebui-pgdata:/var/lib/postgresql/data"
|
||||
environment:
|
||||
- POSTGRES_DB=openwebui
|
||||
- POSTGRES_USER=openwebui
|
||||
- "POSTGRES_PASSWORD={{ vault_owui_db_password }}"
|
||||
restart: unless-stopped
|
||||
@@ -0,0 +1,7 @@
|
||||
# network definition (leading comment required: the compose generator
|
||||
# indents the first line of each fragment; networks: must stay top-level)
|
||||
networks:
|
||||
llmnet:
|
||||
driver: bridge
|
||||
driver_opts:
|
||||
com.docker.network.bridge.name: br-llm
|
||||
@@ -0,0 +1,39 @@
|
||||
# Compose stacks for phy-srv-gpu01
|
||||
|
||||
Rendered by `ironicbadger.docker_compose_generator` (pinned to 2.0.1) into
|
||||
`{{ docker_compose_generator_output_path }}/compose.yaml` on the host.
|
||||
The fragments here are **Jinja2 templates**, not plain compose files — Ansible
|
||||
variables are substituted at render time.
|
||||
|
||||
| Stack | Contains |
|
||||
| --- | --- |
|
||||
| `01-vllm` | vLLM, serves the chat model on port 8000 (OpenAI API) |
|
||||
| `02-embeddings` | second vLLM instance, embedding model on port 8001 |
|
||||
| `03-openwebui` | Open WebUI (port 80) + pgvector Postgres |
|
||||
| `99-network` | the shared `llmnet` bridge — must sort last |
|
||||
|
||||
Ordering comes from the `NN-` prefix; `99-network` keeps `networks:` at the
|
||||
bottom of the generated file. The leading comment in `99-network/compose.yml`
|
||||
is load-bearing: the role indents only the first line of each fragment, and the
|
||||
comment absorbs that indentation so `networks:` stays top-level.
|
||||
|
||||
## Deploy
|
||||
|
||||
```sh
|
||||
just compose phy_srv_gpu01 # renders ~/docker/compose.yaml on the host
|
||||
```
|
||||
|
||||
The role only **writes** the file. Bringing the stack up is deliberate:
|
||||
|
||||
```sh
|
||||
ssh sbxadmin@192.168.66.69 'cd ~/docker && docker compose up -d'
|
||||
```
|
||||
|
||||
## Before the first run
|
||||
|
||||
- Secrets must exist in `group_vars/secrets.yml` (`just vault edit`):
|
||||
`vault_owui_secret_key`, `vault_owui_db_password`, `vault_ldap_bind_password`
|
||||
- LDAP parameters in `group_vars/phy_srv_gpu01.yml` need the real bind DN and
|
||||
base DN from the customer
|
||||
- `vllm_image` must point at a build that works on SM120 (Blackwell) — verify
|
||||
before deploying, the official image is not guaranteed to work
|
||||
@@ -1,11 +1,11 @@
|
||||
# phy-z-srv-gpu01
|
||||
# phy-srv-gpu01
|
||||
|
||||
GPU server for AI/ML workloads. Hardware is ordered/assessed; OS setup and configuration are the next step.
|
||||
|
||||
| | |
|
||||
| - | - |
|
||||
| IP | 192.168.66.69 (planned) |
|
||||
| Ansible group | `phy_z_srv_gpu01` |
|
||||
| Ansible group | `phy_srv_gpu01` |
|
||||
| Status | planned — not yet configured |
|
||||
|
||||
## Hardware
|
||||
+86
-26
@@ -1,4 +1,4 @@
|
||||
# Projektplan — Setup phy-z-srv-gpu01 (LLM-Server mit SMB-Wissensbasis)
|
||||
# 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
|
||||
@@ -93,24 +93,33 @@ 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.
|
||||
|
||||
**Ergebnis (Lauf 2026-07-14, Skript `scripts/share-analysis.ps1` auf `D:` des
|
||||
Fileservers Z-FILESERVER):**
|
||||
**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.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 |
|
||||
| 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 | **924.076**, davon ~**18 % ohne Textlayer** (Stichprobe 200) | ≈ **166.000 Scan-PDFs → OCR**; eigener Zeit-/GPU-Aufwand, konkurriert mit vLLM |
|
||||
| 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.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) |
|
||||
| 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) |
|
||||
|
||||
**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).
|
||||
**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
|
||||
|
||||
@@ -142,19 +151,27 @@ 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)
|
||||
### 2.4 Ansible — Stand 2026-09-03
|
||||
|
||||
- `run.yml`: Play für `phy_z_srv_gpu01` ergänzen
|
||||
- Rollen-Skelett unter `ansible/roles/` (Details erst bei Umsetzung):
|
||||
- `nvidia_gpu` — Treiber (≥ 580), Container Toolkit, optional MIG, DCGM
|
||||
- `cifs_mounts` — ro-Mounts, Credentials aus `group_vars/secrets.yml`
|
||||
- `llm_stack` — Docker Compose: vLLM (feste `--gpu-memory-utilization`),
|
||||
Embedding-Server, Open WebUI + pgvector, Reverse Proxy (TLS), oikb-Timer
|
||||
- Bestehendes nachnutzen: `geerlingguy.security` (Basis-Härtung),
|
||||
`geerlingguy.docker`
|
||||
- `group_vars/phy_z_srv_gpu01.yml`: nur Overrides (Modellname, VRAM-Quote,
|
||||
Share-Liste, LDAP-Parameter)
|
||||
- Alles außer GPU-Rolle ist vorab in einer Wegwerf-VM testbar (`just run`-Pfad)
|
||||
- [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
|
||||
|
||||
@@ -206,11 +223,54 @@ lautete sinngemäß „unsere Dokumente auslesen". Realistisch ist „ein kurati
|
||||
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 -WithSize` auf Z-FILESERVER: **30 Freigaben, alle
|
||||
unterhalb `D:`**, kein DFS, zusammen **2.128 GB / 3,11 Mio. Dateien**.
|
||||
Vollständige Zahlen je Freigabe: [`20260903-share-inventory.md`](20260903-share-inventory.md).
|
||||
|
||||
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` | **raus** | 446 GB bei nur 64k Dateien (Ø 7,2 MB) — Installer/ISOs, keine Dokumente |
|
||||
| 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 — nicht extrahierbar; Messung zeigt **0 Dateien**, beim Kunden verifizieren |
|
||||
| Marketing | `D:\Abteilungen\Marketing` | **raus (prüfen)** | 226 GB / 119k — überwiegend Bild-/Videomaterial |
|
||||
| EE | `D:\Abteilungen\EE` | **raus (prüfen)** | 423k Dateien à Ø 0,11 MB — Massen-Kleinstdateien, kein Lesestoff |
|
||||
| Datenbank | `D:\Datenbank` | **raus** | 371k Dateien à Ø 0,08 MB — 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 |
|
||||
|
||||
**Mengengerüst** (Details in der Inventar-Notiz): alle Freigaben 3,11 Mio. Dateien
|
||||
→ nach den offensichtlichen Ausschlüssen oben immer noch **2,08 Mio.** → ein
|
||||
„Kernbestand" aus den 14 kleinen Fachfreigaben (QM, Arbeitssicherheit, Competence
|
||||
Center, AA, AZ, BU, Kalkulationen, Lagerlogistik, SG, MF, MM, Immobilie,
|
||||
Controlling, EK) wären **108 GB / 198k Dateien**. Heißt: **Ausschlüsse auf
|
||||
Freigabe-Ebene allein genügen nicht** — innerhalb von Produktion, PL, EF, ME und
|
||||
Projekte muss auf Unterordner-Ebene weitergefiltert werden.
|
||||
|
||||
⚠️ **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_z_srv_gpu01` läuft grün |
|
||||
| **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 1–2) | 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 2–3) | Open WebUI + pgvector, LDAP-Login, Reverse Proxy + TLS, vorkonfigurierter „Phytron-Assistent" | Login mit AD-Konto, Chat läuft |
|
||||
@@ -0,0 +1,78 @@
|
||||
# Freigaben-Inventar Z-FILESERVER (2026-09-03)
|
||||
|
||||
Erhoben mit `scripts/list-shares.ps1 -WithSize` auf Z-FILESERVER.
|
||||
**30 Freigaben, alle unterhalb `D:`, kein DFS.**
|
||||
Summe: **2.128 GB, 3.112.777 Dateien.**
|
||||
|
||||
Zum Vergleich: der Scan über `D:` gesamt (§2.1 im Projektplan) ergab 2.231 GB /
|
||||
3.338.643 Dateien — die Differenz (~103 GB, ~226k Dateien) liegt auf `D:`, ist
|
||||
aber **nicht freigegeben**.
|
||||
|
||||
## Rohdaten
|
||||
|
||||
| Freigabe | Pfad | GB | Dateien | Ø Dateigröße |
|
||||
|---|---|---:|---:|---:|
|
||||
| EDV | `D:\Abteilungen\EDV` | 445,67 | 63.746 | 7,16 MB |
|
||||
| Produktion | `D:\Produktion` | 308,52 | 562.853 | 0,56 MB |
|
||||
| Benutzer | `D:\Benutzer` | 303,00 | 327.676 | 0,95 MB |
|
||||
| PL | `D:\Abteilungen\PL` | 228,79 | 547.503 | 0,43 MB |
|
||||
| Marketing | `D:\Abteilungen\Marketing` | 225,98 | 119.055 | 1,94 MB |
|
||||
| EF | `D:\Abteilungen\EF` | 131,45 | 110.854 | 1,21 MB |
|
||||
| ME | `D:\Abteilungen\ME` | 101,71 | 120.411 | 0,86 MB |
|
||||
| Tempwork | `D:\Tempwork` | 93,59 | 106.824 | 0,90 MB |
|
||||
| Projekte | `D:\Projekte` | 50,84 | 80.827 | 0,64 MB |
|
||||
| EE | `D:\Abteilungen\EE` | 46,15 | 422.572 | 0,11 MB |
|
||||
| QM | `D:\Abteilungen\QM` | 33,68 | 85.940 | 0,40 MB |
|
||||
| VT | `D:\Abteilungen\VT` | 30,40 | 41.034 | 0,76 MB |
|
||||
| Datenbank | `D:\Datenbank` | 27,74 | 370.842 | 0,08 MB |
|
||||
| MM | `D:\Abteilungen\MM` | 17,48 | 30.941 | 0,58 MB |
|
||||
| GF | `D:\Abteilungen\GF` | 15,31 | 14.322 | 1,09 MB |
|
||||
| Immobilie | `D:\Immobilie` | 12,33 | 10.700 | 1,18 MB |
|
||||
| Competence Center | `D:\Abteilungen\Competence Center` | 10,63 | 4.756 | 2,29 MB |
|
||||
| Personal | `D:\Abteilungen\Personal` | 10,50 | 25.440 | 0,42 MB |
|
||||
| MF | `D:\Abteilungen\MF` | 8,48 | 27.468 | 0,32 MB |
|
||||
| Controlling | `D:\Controlling` | 8,15 | 2.952 | 2,83 MB |
|
||||
| Arbeitssicherheit | `D:\Arbeitssicherheit` | 5,78 | 7.416 | 0,80 MB |
|
||||
| BU | `D:\Abteilungen\BU` | 4,79 | 6.242 | 0,79 MB |
|
||||
| AA | `D:\Abteilungen\AA` | 2,56 | 10.206 | 0,26 MB |
|
||||
| AZ | `D:\Abteilungen\AZ` | 1,86 | 3.973 | 0,48 MB |
|
||||
| SG | `D:\Abteilungen\SG` | 1,51 | 3.603 | 0,43 MB |
|
||||
| Kalkulationen | `D:\Abteilungen\Kalkulationen` | 0,59 | 3.325 | 0,18 MB |
|
||||
| Lagerlogistik | `D:\Abteilungen\Lagerlogistik` | 0,57 | 663 | 0,88 MB |
|
||||
| EK | `D:\Abteilungen\EK` | 0,02 | 314 | 0,07 MB |
|
||||
| BR | `D:\Abteilungen\BR` | 0,01 | 319 | 0,04 MB |
|
||||
| Zeichnungen | `D:\Abteilungen\Zeichnungen` | 0,00 | 0 | — |
|
||||
|
||||
## Auffälligkeiten
|
||||
|
||||
- **`Zeichnungen` ist leer (0 Dateien).** Entweder tatsächlich leer, oder das
|
||||
ausführende Konto hatte keinen Lesezugriff. **Beim Kunden klären** — falls dort
|
||||
doch Zeichnungen liegen, ändert das nichts am Ausschluss (CAD ist nicht
|
||||
extrahierbar), wohl aber die Aussage „wir haben alles gesehen".
|
||||
- **`EDV` ist kein Dokumentenbestand.** 445 GB bei nur 64k Dateien = Ø 7,2 MB je
|
||||
Datei — das sind Installer/ISOs/Images, keine Fließtexte. (Damit ist meine
|
||||
frühere Einschätzung „EDV rein" hinfällig.)
|
||||
- **`Datenbank` und `EE` sind Massen-Kleinstdateien** (Ø 0,08 bzw. 0,11 MB) —
|
||||
typisch für Datenbank- bzw. Systemdateien, kein Lesestoff.
|
||||
- **Vier Freigaben dominieren die Dateianzahl:** Produktion (563k), PL (548k),
|
||||
EE (423k), Benutzer (328k) — zusammen ~1,86 Mio. der 3,11 Mio. Dateien.
|
||||
|
||||
## Mengengerüst für die Eingrenzung
|
||||
|
||||
| Szenario | GB | Dateien |
|
||||
|---|---:|---:|
|
||||
| Alle Freigaben | 2.128 | 3.112.777 |
|
||||
| Ohne Benutzer, Personal, GF, BR, Datenbank, Tempwork, Zeichnungen, EDV, Marketing | 1.006 | 2.084.553 |
|
||||
| „Kernbestand": QM, Arbeitssicherheit, Competence Center, AA, AZ, BU, Kalkulationen, Lagerlogistik, SG, MF, MM, Immobilie, Controlling, EK | **108** | **198.499** |
|
||||
|
||||
Auch nach den offensichtlichen Ausschlüssen bleiben >2 Mio. Dateien — die
|
||||
Eingrenzung muss also **innerhalb** der großen Freigaben weitergehen
|
||||
(Unterordner), nicht nur auf Freigabe-Ebene. Der Kernbestand mit ~198k Dateien
|
||||
(davon nur ein Teil extrahierbare Office-Dokumente) liegt dagegen in der
|
||||
Größenordnung, die im Projektplan §2.6 als handhabbar beschrieben ist.
|
||||
|
||||
## Nächster Schritt
|
||||
|
||||
`share-analysis.ps1 -Paths "D:" -FolderDepth 2` liefert die Dateityp-Verteilung
|
||||
je Freigabe — erst damit ist bekannt, wie viele der Dateien je Freigabe
|
||||
überhaupt extrahierbar (`office`) sind. Läuft zum Zeitpunkt dieser Notiz noch.
|
||||
+36
-9
@@ -16,8 +16,20 @@
|
||||
.\list-shares.ps1
|
||||
|
||||
.EXAMPLE
|
||||
# remote, and with size per share (slow on large shares)
|
||||
.\list-shares.ps1 -ComputerName Z-FILESERVER -WithSize
|
||||
# with size per share — run this ON the file server, it then walks the local
|
||||
# paths instead of the UNC paths (far faster). Still minutes to hours on
|
||||
# millions of files; progress is printed every 50k files.
|
||||
.\list-shares.ps1 -WithSize
|
||||
|
||||
.NOTES
|
||||
Faster alternative when all shares live under one drive (as on Z-FILESERVER,
|
||||
where everything sits under D:): run share-analysis.ps1 once with a matching
|
||||
folder depth instead of measuring every share separately, e.g.
|
||||
|
||||
.\share-analysis.ps1 -Paths "D:" -FolderDepth 2
|
||||
|
||||
That produces size/count per D:\Abteilungen\<Share> in a single pass over
|
||||
the disk, which is what the share list here maps onto.
|
||||
#>
|
||||
param(
|
||||
[string]$ComputerName = $env:COMPUTERNAME,
|
||||
@@ -55,13 +67,28 @@ $result = foreach ($sh in $shares) {
|
||||
|
||||
$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 { }
|
||||
# Walk the LOCAL path when we are on the server itself — going through
|
||||
# the UNC path (\\server\share) routes every single file through the SMB
|
||||
# stack and is slower by orders of magnitude on large shares.
|
||||
$scanPath = if ($ComputerName -eq $env:COMPUTERNAME -and $sh.Path) {
|
||||
$sh.Path
|
||||
} else {
|
||||
"\\$ComputerName\$($sh.Name)"
|
||||
}
|
||||
|
||||
$sw = [System.Diagnostics.Stopwatch]::StartNew()
|
||||
$n = 0L; $bytes = 0L
|
||||
Get-ChildItem -LiteralPath $scanPath -Recurse -File -Force -ErrorAction SilentlyContinue |
|
||||
ForEach-Object {
|
||||
$n++; $bytes += $_.Length
|
||||
if ($n % 50000 -eq 0) {
|
||||
Write-Host (" ... {0:N0} files, {1:N1} GB ({2:N0}s)" -f $n, ($bytes / 1GB), $sw.Elapsed.TotalSeconds) -ForegroundColor DarkGray
|
||||
}
|
||||
}
|
||||
$sw.Stop()
|
||||
$sizeGB = [Math]::Round($bytes / 1GB, 2)
|
||||
$fileCount = $n
|
||||
Write-Host (" {0:N0} files, {1:N1} GB in {2:N0}s" -f $n, ($bytes / 1GB), $sw.Elapsed.TotalSeconds) -ForegroundColor DarkGray
|
||||
}
|
||||
|
||||
[PSCustomObject]@{
|
||||
+23
-3
@@ -26,7 +26,11 @@ param(
|
||||
[string]$OutDir = (Join-Path (Get-Location) ("share-analysis-" + (Get-Date -Format "yyyyMMdd-HHmmss"))),
|
||||
|
||||
# PDFs sampled per share for the text-layer check
|
||||
[int]$PdfSampleSize = 200
|
||||
[int]$PdfSampleSize = 200,
|
||||
|
||||
# How many folder levels to aggregate in the *-toplevel-folders.csv.
|
||||
# 1 = D:\Abteilungen, 2 = D:\Abteilungen\AA (matches the share layout here).
|
||||
[int]$FolderDepth = 1
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Continue'
|
||||
@@ -80,7 +84,17 @@ function Test-PdfTextLayer {
|
||||
# --- per-share pass ----------------------------------------------------------
|
||||
|
||||
foreach ($root in $Paths) {
|
||||
# Label used for the output filenames. Must not contain characters that are
|
||||
# illegal in a filename — e.g. -Paths "D:" would otherwise produce
|
||||
# "D:-file-types.csv" and Export-Csv fails with "path format not supported".
|
||||
$shareName = ($root.TrimEnd('\') -split '[\\/]')[-1]
|
||||
if (-not $shareName) { $shareName = 'root' }
|
||||
foreach ($c in [System.IO.Path]::GetInvalidFileNameChars()) {
|
||||
$shareName = $shareName.Replace($c, '_')
|
||||
}
|
||||
$shareName = $shareName.TrimEnd('_', '.', ' ')
|
||||
if (-not $shareName) { $shareName = 'root' }
|
||||
|
||||
Write-Host "=== Analyzing '$root' ..." -ForegroundColor Cyan
|
||||
|
||||
if (-not (Test-Path -LiteralPath $root)) {
|
||||
@@ -118,9 +132,15 @@ foreach ($root in $Paths) {
|
||||
$extStats[$ext].Count++
|
||||
$extStats[$ext].Bytes += $_.Length
|
||||
|
||||
# top-level folder relative to the share root
|
||||
# folder relative to the share root, aggregated at -FolderDepth levels
|
||||
$rel = $_.FullName.Substring($rootLen).TrimStart('\')
|
||||
$top = if ($rel.Contains('\')) { $rel.Split('\')[0] } else { '(root)' }
|
||||
$parts = $rel.Split('\')
|
||||
if ($parts.Count -le 1) {
|
||||
$top = '(root)'
|
||||
} else {
|
||||
$n = [Math]::Min($FolderDepth, $parts.Count - 1)
|
||||
$top = ($parts[0..($n - 1)] -join '\')
|
||||
}
|
||||
if (-not $topStats.ContainsKey($top)) { $topStats[$top] = @{ Count = 0L; Bytes = 0L } }
|
||||
$topStats[$top].Count++
|
||||
$topStats[$top].Bytes += $_.Length
|
||||
Reference in New Issue
Block a user