Add: DNS über Pi-Hole LXC. Härtung von CT-105 Wireguard.
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
# LLM-Server `ghostnet-ki`
|
||||
|
||||
Dieser Gerätebereich dokumentiert den physischen Server `10.0.0.90`. Er
|
||||
gruppiert die gemeinsame Hostbasis, den eigentlichen LLM-Stack und den davon
|
||||
fachlich getrennten CPU-Encoding-Workload. Alle drei Bereiche teilen Hardware,
|
||||
Betriebssystem und Ansible-Control-Projekt.
|
||||
|
||||
| Bereich | Inhalt |
|
||||
| --- | --- |
|
||||
| [Host](host/GNAI-002-INFRA_BaseSystem.md) | Hardware, Debian-Basis und GPU-Ausgangslage |
|
||||
| [Zugänge](host/GNAI-003-INFRA_Users.md) | Benutzer, Netzwerk, Resolver und SSH |
|
||||
| [AI](ai/README.md) | Architektur, Ansible-Deployment, Modellbewertung und `llama.cpp`-Serving |
|
||||
| [Encoding](encoding/GNENC-000-DOC_Index.md) | CPU-Encoding-Architektur und offener Implementierungsstand |
|
||||
|
||||
Übergreifende Rollen stehen im
|
||||
[Zugriffskonzept](../GNAI-005-SEC_Access-Control.md), der Gesamtstand im
|
||||
[zentralen Dokumentationsindex](../../GNAI-000-DOC_Index.md).
|
||||
@@ -0,0 +1,370 @@
|
||||
# Technical Architecture Specification
|
||||
|
||||
**Dokumenten-ID:** GNAI-004-INFRA
|
||||
**Titel:** Homelab / LLM-Infrastruktur – Zielarchitektur
|
||||
**Status:** Zielarchitektur; bestätigter Teilstand und offene Umsetzung separat ausgewiesen
|
||||
**Vorgänger / Referenzen:** [Basisinstallation](../host/GNAI-002-INFRA_BaseSystem.md), [Benutzer/SSH](../host/GNAI-003-INFRA_Users.md), [Zugriffskonzept](../../GNAI-005-SEC_Access-Control.md), [Reverse Proxy](../../proxmox-server/lxc/ct-103-reverse-proxy/GNAI-006-SEC_Reverse-Proxy.md), [WireGuard](../../proxmox-server/lxc/ct-105-wireguard/GNAI-007-SEC_VPN.md)
|
||||
|
||||
---
|
||||
|
||||
## Änderungsverlauf
|
||||
|
||||
| Datum | Version | Autor | Beschreibung |
|
||||
| --- | --- | --- | --- |
|
||||
| 2026-08-28 | 1.0 | spektr | Erstellung der Zielarchitektur |
|
||||
| 2026-09-15 | 1.1 | spektr| Adressplan, Rollenmodell, OIDC-Ist und offene Sicherheitsbefunde ergänzt. |
|
||||
| 2026-09-16 | 1.2 | spektr | Bestätigten Fail2Ban-Pin und noch offene Funktionsabnahme ergänzt. |
|
||||
| 2026-09-18 | 1.3 | spektr | Tatsächlichen KI-Server-Hostnamen übernommen; frischen Basisstand und späteren Ansible-Sprint vom Zielbild getrennt. |
|
||||
| 2026-09-21 | 1.4 | spektr / Codex | belegten Plattformstand aktualisiert und LiteLLM als zu bestätigenden Kandidaten einer allgemeinen API-Policy-Schicht eingeordnet |
|
||||
|
||||
---
|
||||
|
||||
## 1. Zweck und Geltungsbereich
|
||||
|
||||
Dieses Dokument beschreibt die festgelegte Zielarchitektur für die sichere Bereitstellung des LLM-Servers und der öffentlich erreichbaren Webservices innerhalb der Ghostnet-Infrastruktur.
|
||||
|
||||
Es handelt sich ausdrücklich um eine **technische Spezifikation und kein Installationshandbuch**. Konkrete Implementierungsanweisungen, Konfigurationsdateien und Abnahmetests werden in nachgelagerten SOPs dokumentiert.
|
||||
|
||||
Die Architektur baut auf dem in **GNAI-002-INFRA** beschriebenen Debian-basierten LLM-Server und der in **GNAI-003-INFRA** beschriebenen SSH-/Benutzer-Härtung auf.
|
||||
|
||||
Der KI-Server begann am 18.09.2026 als frisches Basissystem. Inzwischen sind
|
||||
Deployment-Zugang, Host-Baseline, Podman, getrennte Workload-Konten und der
|
||||
rootless ROCm-Gerätenachweis mit Ansible umgesetzt. Dauerhaftes
|
||||
`llama.cpp`-Serving und weitere Anwendungen sind noch nicht installiert.
|
||||
LiteLLM, OpenWebUI und GPT Researcher sind **Zielbild-Kandidaten**, aber ohne
|
||||
Bestätigung in der priorisierten Roadmap keine Zusage für die nächste
|
||||
Ausbaustufe.
|
||||
|
||||
---
|
||||
|
||||
## 2. Architekturprinzipien
|
||||
|
||||
Die Infrastruktur folgt diesen Grundsätzen:
|
||||
|
||||
1. **Minimale Internet-Exposure:** Aus dem Internet werden ausschließlich die tatsächlich benötigten öffentlichen Dienste freigegeben.
|
||||
2. **Dedizierter Reverse Proxy:** Sämtliche öffentlichen HTTP(S)-Dienste werden über einen zentralen Nginx Proxy Manager (NPM) veröffentlicht.
|
||||
3. **Managementnetz getrennt von Applikationszugriff:** Privilegierter Netzwerkzugriff erfolgt über WireGuard.
|
||||
4. **Anwendungsidentität über Keycloak:** Webanwendungen verwenden OIDC zur zentralen Authentifizierung.
|
||||
5. **LLM-API als dedizierte Applikationsschnittstelle:** Direkter Zugriff auf
|
||||
`llama.cpp` aus dem Internet ist nicht vorgesehen. Eine öffentliche
|
||||
Schnittstelle benötigt eine getrennte Policy-Schicht; LiteLLM ist dafür der
|
||||
bisherige Kandidat, aber noch nicht als erste Ausbaustufe bestätigt.
|
||||
6. **Defense in Depth:** Reverse Proxy, Fail2Ban, Firewall-Regeln, MFA, Patchmanagement, Monitoring und Backups ergänzen sich.
|
||||
7. **Least Privilege:** Server Admins und LLM Admins erhalten passende Serverrechte; Familie ist ausdrücklich für das gesamte Heimnetz freigegeben. Netzwerkrechte werden pro Peer serverseitig durchgesetzt.
|
||||
8. **Isolation:** Öffentlich exponierte Infrastruktur und einzelne Backend-Dienste bleiben logisch bzw. durch eigene LXCs getrennt.
|
||||
|
||||
---
|
||||
|
||||
## 3. Netzwerk- und Hoststruktur
|
||||
|
||||
### 3.1 Bestehende Proxmox-Struktur
|
||||
|
||||
Der Proxmox-Server bildet die Virtualisierungs- und Isolationsschicht.
|
||||
|
||||
| Komponente | Adresse | Rolle |
|
||||
| --- | --- | --- |
|
||||
| Proxmox VE | `10.0.0.2` | Virtualisierungs-/Managementhost |
|
||||
| DNS LXC | `10.0.0.14` | Pi-hole, lokaler Resolver und Split-DNS |
|
||||
| Reverse Proxy LXC | `10.0.0.10` | einziger öffentlicher HTTP(S)-Entry-Point |
|
||||
| WireGuard LXC | `10.0.0.15` | VPN-/Managementzugang |
|
||||
| LLM Server | `10.0.0.90`, Hostname `ghostnet-ki` | KI-Inferenz und LLM-Dienste |
|
||||
|
||||
Die Service-LXCs (z. B. Nextcloud, Gitea, Vaultwarden, Keycloak etc.) verbleiben jeweils in eigenen LXCs und werden nicht in den Reverse-Proxy-LXC zusammengelegt.
|
||||
|
||||
### Adressplanung und aktuelles Serviceinventar
|
||||
|
||||
Heimnetz `10.0.0.0/24`, Fritzbox/Gateway `.1`, lokaler DNS `.14`. Statische
|
||||
Server-/LXC-Adressen sind bis `.99` vorgesehen; DHCP vergibt `.100–.200` an
|
||||
Clients und verteilt Pi-hole als IPv4-DNS. Die `.90` ist dem physischen
|
||||
KI-Server vorbehalten. Das ist eine Belegungsordnung innerhalb desselben
|
||||
Netzes, keine VLAN-/Firewall-Isolation.
|
||||
|
||||
| Host / Service | Quellenstand | Status der Angabe |
|
||||
| --- | --- | --- |
|
||||
| Proxmox | `10.0.0.2` | Betreiber bestätigt |
|
||||
| Keycloak | `10.0.0.4:8080` | Betreiber bestätigt NPM-Backend |
|
||||
| Pi-hole | `10.0.0.14:53` | am 01.10.2026 in CT 100 abgenommen |
|
||||
| NPM / wg-easy | `10.0.0.10` / `10.0.0.15` | Betreiber bestätigt |
|
||||
| Jellyfin | `10.0.0.11:8096` | GNAI-006-Inventar, vor Freigabe abgleichen |
|
||||
| Nextcloud | `10.0.0.12:8080` | GNAI-006-Inventar, vor Freigabe abgleichen |
|
||||
| Gitea | `10.0.0.17:3000` | GNAI-006-Inventar, vor Freigabe abgleichen |
|
||||
| Japanese Web App | `10.0.0.23:80` | GNAI-006-Inventar, vor Freigabe abgleichen |
|
||||
| Vaultwarden | `10.0.0.47:8080` | GNAI-006-Inventar; OIDC-Nutzung vom Betreiber bestätigt |
|
||||
| KI-Server | `10.0.0.90` | Betreiber bestätigt |
|
||||
|
||||
Neue LXCs im Inventar ergänzen. Der ursprüngliche Bereich `.4–.40` war geschätzt und ist keine gültige Grenze. Lokales Gästenetz, mögliche separate Servernetze, Routing und VLAN-Unterstützung gehören in einen späteren Netzwerk-Sprint; es gibt hierfür noch keine beschlossenen Netzadressen. Automatische Broadcast-/Multicast-Geräteerkennung über WireGuard wird nicht vorausgesetzt.
|
||||
|
||||
### 3.2 Perimeter
|
||||
|
||||
Die Fritzbox 7530 bildet den Internet-Perimeter.
|
||||
|
||||
Aus dem Internet sollen grundsätzlich nur die für den öffentlichen Betrieb erforderlichen Weiterleitungen existieren:
|
||||
|
||||
- TCP 80 → Reverse Proxy LXC `10.0.0.10`
|
||||
- TCP 443 → Reverse Proxy LXC `10.0.0.10`
|
||||
- UDP 51820 → WireGuard LXC `10.0.0.15`
|
||||
|
||||
Managementdienste wie Proxmox Web UI oder SSH werden **nicht direkt aus dem Internet veröffentlicht**.
|
||||
|
||||
IPv4- und IPv6-Exposure sind gleichwertig zu betrachten und müssen bei der Implementierung separat verifiziert werden.
|
||||
|
||||
---
|
||||
|
||||
## 4. Reverse-Proxy-LXC
|
||||
|
||||
Der Reverse-Proxy-LXC (`10.0.0.10`) ist eine dedizierte Infrastrukturzone für öffentlich exponierte HTTP(S)-Komponenten.
|
||||
|
||||
### 4.1 Containerisierung
|
||||
|
||||
Innerhalb des LXCs wird Podman verwendet.
|
||||
|
||||
Vorgesehene Container:
|
||||
|
||||
- **Nginx Proxy Manager**
|
||||
- **Fail2Ban**
|
||||
|
||||
NPM und Fail2Ban befinden sich absichtlich im selben LXC. Dadurch kann Fail2Ban die Logdaten von NPM auswerten und – unter minimal erforderlichen Capabilities – die Netzwerk-/Firewallebene des eigenen LXCs absichern, ohne Zugriff auf die Firewall eines anderen LXCs zu benötigen.
|
||||
|
||||
### 4.2 Verantwortlichkeiten
|
||||
|
||||
**NPM**
|
||||
|
||||
- TLS-Terminierung
|
||||
- Reverse Proxy
|
||||
- Routing zu internen Services
|
||||
- HTTP(S)-Security-Header und weitere Proxy-Policies
|
||||
- optionales Rate Limiting, soweit sinnvoll
|
||||
|
||||
**Fail2Ban**
|
||||
|
||||
- Erkennung wiederholter bzw. eindeutiger Missbrauchsmuster
|
||||
- temporäre Sperrung auffälliger Quelladressen
|
||||
- Auswertung gezielter NPM-/Applikationslogs
|
||||
|
||||
Fail2Ban ist eine **zusätzliche Abuse-Protection-Schicht**, kein Ersatz für Patchmanagement, sichere Authentifizierung oder Netzwerksegmentierung.
|
||||
|
||||
---
|
||||
|
||||
## 5. Öffentliche Webservices
|
||||
|
||||
Alle öffentlich vorgesehenen Webservices werden hinter NPM veröffentlicht.
|
||||
|
||||
Beispiele:
|
||||
|
||||
### Services auf separaten Proxmox-LXCs
|
||||
|
||||
- Nextcloud
|
||||
- Gitea
|
||||
- Vaultwarden
|
||||
- Keycloak
|
||||
|
||||
### Mögliche Services auf dem LLM-Server
|
||||
|
||||
- OpenWebUI
|
||||
- GPT Researcher
|
||||
- LiteLLM API
|
||||
|
||||
Diese Liste beschreibt Architektur-Kandidaten, keine gemeinsam freigegebene
|
||||
Implementierungsreihenfolge. Die aktive Reihenfolge steht in GNAI-001.
|
||||
|
||||
Direkter Internetzugriff auf die internen Backend-Ports der Services ist nicht vorgesehen.
|
||||
|
||||
---
|
||||
|
||||
## 6. LLM-Server-Architektur
|
||||
|
||||
Der LLM-Server basiert auf der in **GNAI-002-INFRA** dokumentierten Debian-Basis mit AMD-Multi-GPU-Konfiguration und der in **GNAI-003-INFRA** vorgesehenen SSH-Key-basierten Absicherung.
|
||||
|
||||
### 6.1 LLM-Inferenz
|
||||
|
||||
`llama.cpp` soll die eigentliche lokale Modell-Inferenz bereitstellen.
|
||||
|
||||
Grundsatz:
|
||||
|
||||
> `llama.cpp` wird nicht direkt aus dem Internet exponiert.
|
||||
|
||||
Der Zugriff erfolgt intern über die dafür vorgesehene API-Schicht.
|
||||
|
||||
### 6.2 API- und Policy-Schicht
|
||||
|
||||
Für eine externe API ist eine standardisierte API- und Policy-Schicht vor
|
||||
`llama.cpp` vorgesehen. LiteLLM ist der bisherige Kandidat. Seine Einführung
|
||||
wird vor der Veröffentlichung gegen die tatsächlich benötigten individuellen
|
||||
Keys, Quoten, Routing- und Beobachtbarkeitsfunktionen geprüft.
|
||||
|
||||
Verantwortlichkeiten:
|
||||
|
||||
- öffentliche API-Schnittstelle
|
||||
- API-Key-Authentifizierung
|
||||
- Routing zu laufenden Modellen
|
||||
- ggf. Modell-/Benutzer-Policies
|
||||
- Rate-/Quota-Steuerung
|
||||
- Trennung der öffentlichen API von der eigentlichen Inferenz-Engine
|
||||
|
||||
Zielpfad:
|
||||
|
||||
Im Zielbild verbindet sich ein externer Client per HTTPS mit NPM. NPM leitet
|
||||
an die beschlossene Policy-Schicht weiter; diese nutzt `llama.cpp` über das
|
||||
interne Container-/Hostnetz. Die Inferenz läuft auf den GPUs. Dieser Pfad ist
|
||||
noch nicht umgesetzt.
|
||||
|
||||
---
|
||||
|
||||
## 7. WireGuard-Managementnetz
|
||||
|
||||
WireGuard ist die dedizierte Zugangsschicht für privilegierte Netzwerkzugriffe.
|
||||
|
||||
### 7.1 Implementierung
|
||||
|
||||
- eigener WireGuard-LXC auf Proxmox
|
||||
- `wg-easy` als Podman-Container
|
||||
- WireGuard-Peer-Authentifizierung ausschließlich über kryptografische Schlüssel
|
||||
- kein OIDC für die eigentliche WireGuard-Peer-Authentifizierung
|
||||
|
||||
### 7.2 Rollen und VPN-Betrieb
|
||||
|
||||
| Rolle | Zielrechte |
|
||||
| --- | --- |
|
||||
| Familie (Brüder) | gesamtes Heimnetz einschließlich PCs/Clients; Serververwaltung nach Dienstanmeldung |
|
||||
| Server Admin | Proxmox, Service-LXCs und KI-Server; NPM und wg-easy eingeschlossen |
|
||||
| LLM Admin | ausschließlich KI-Server `10.0.0.90` |
|
||||
|
||||
Profile werden pro Gerät vom Administrator erstellt und verteilt. Benutzer benötigen für den Profilbezug keine GUI-Anmeldung. Serverseitige **Firewall Allowed IPs** in wg-easy sollen die Zielrechte durchsetzen; clientseitige **Allowed IPs** steuern Split-Tunneling. Per-Client Firewall ist aktuell deaktiviert, Rollenisolierung nicht abgenommen. Gateway-GUI/SSH benötigen zusätzlich passende INPUT-Regeln. Die alte Full-Admin-Bezeichnung wird nicht automatisch einer der neuen Rollen gleichgesetzt.
|
||||
|
||||
Das Gateway nutzt MASQUERADE für VPN→LAN; die Fritzbox besitzt keine Rückroute zum VPN-Netz. LAN-Dienste sehen dadurch die Quelladresse des Gateways. Die serverseitige Peer-Zielkontrolle muss vor NAT greifen. Installation, Hooks und Abnahme stehen in GNAI-007.
|
||||
|
||||
---
|
||||
|
||||
## 8. Keycloak / OIDC
|
||||
|
||||
Keycloak ist die zentrale Identitäts- und Authentifizierungsplattform für die Webanwendungen.
|
||||
|
||||
Vorgesehenes Modell:
|
||||
|
||||
Der Browser erreicht den Webdienst über HTTPS und NPM. Der Webdienst bindet Keycloak für den OIDC-Anmeldeprozess ein; Anwendung und Identitätsquelle behalten getrennte Aufgaben.
|
||||
|
||||
Webservices sollen OIDC nutzen, soweit die jeweilige Anwendung daran angebunden wird. Laut Betreiber ist derzeit nur Vaultwarden produktiv an Keycloak/OIDC angebunden.
|
||||
|
||||
Die Keycloak-Administrationsoberfläche und administrative Managementschnittstellen sollen nicht öffentlich zugänglich sein und werden über das Managementnetz bereitgestellt.
|
||||
|
||||
Öffentlich erforderlich bleiben ausschließlich die für OIDC tatsächlich benötigten Endpunkte des jeweiligen Realms.
|
||||
|
||||
---
|
||||
|
||||
## 9. SSH
|
||||
|
||||
SSH auf dem LLM-Server wird ausschließlich schlüsselbasiert betrieben.
|
||||
|
||||
Aus **GNAI-003-INFRA** übernimmt die Zielkonfiguration insbesondere:
|
||||
|
||||
- `PermitRootLogin no`
|
||||
- `PasswordAuthentication no`
|
||||
- `KbdInteractiveAuthentication no`
|
||||
- `PubkeyAuthentication yes`
|
||||
|
||||
Der LLM-Server-SSH-Port wird nicht aus dem Internet veröffentlicht.
|
||||
|
||||
Zulässige Herkunft:
|
||||
|
||||
- lokales vertrauenswürdiges Netzwerk
|
||||
- WireGuard-Managementnetz
|
||||
|
||||
---
|
||||
|
||||
## 10. Sicherheitszonen und Vertrauensmodell
|
||||
|
||||
Die Infrastruktur unterscheidet mindestens drei Vertrauensklassen:
|
||||
|
||||
### Public Application Access
|
||||
|
||||
Zugang über:
|
||||
|
||||
HTTPS über NPM.
|
||||
|
||||
Authentifizierung erfolgt je nach Anwendung über:
|
||||
|
||||
- OIDC / Keycloak
|
||||
- API-Key bei der LLM-API
|
||||
|
||||
### Management Access
|
||||
|
||||
Zugang über:
|
||||
|
||||
WireGuard zum freigegebenen Zielsystem.
|
||||
|
||||
Zusätzliche Dienstauthentifizierung bleibt erhalten, z. B. SSH Public Keys oder Proxmox-Authentifizierung.
|
||||
|
||||
### Backend Access
|
||||
|
||||
Nicht öffentlich.
|
||||
|
||||
Beispiele:
|
||||
|
||||
- `llama.cpp`
|
||||
- interne Datenbankports
|
||||
- interne Container-/Serviceports
|
||||
- sonstige administrative Backend-Schnittstellen
|
||||
|
||||
---
|
||||
|
||||
## 11. Geplante Defense-in-Depth-Schichten
|
||||
|
||||
Die Zielarchitektur sieht folgende komplementäre Schutzschichten vor:
|
||||
|
||||
1. Fritzbox als Perimeter
|
||||
2. keine unnötigen Portweiterleitungen
|
||||
3. Proxmox-/LXC-Netzwerk- und Firewallregeln
|
||||
4. dedizierter Reverse Proxy
|
||||
5. Fail2Ban für erkannte Missbrauchsmuster
|
||||
6. TLS/HTTPS
|
||||
7. zentrale Authentifizierung mit Keycloak/OIDC
|
||||
8. MFA/WebAuthn für privilegierte Benutzer, wo unterstützt
|
||||
9. API-Keys bzw. OAuth-/Anwendungscredentials für Maschinenzugriffe
|
||||
10. SSH ausschließlich über Schlüssel
|
||||
11. regelmäßige Updates / Patchmanagement
|
||||
12. Backup- und Restore-Strategie
|
||||
13. externes Monitoring und regelmäßige Prüfung der öffentlichen Exposure
|
||||
|
||||
---
|
||||
|
||||
## 12. Nicht Teil dieser Spezifikation
|
||||
|
||||
Folgende konkrete Werte und Verfahren werden in den Fachdokumenten geführt. Einige sind bereits festgelegt, andere dort ausdrücklich als offen markiert:
|
||||
|
||||
- konkrete WireGuard-Subnetze und Peer-Adressen
|
||||
- konkrete Firewall-Regelsätze
|
||||
- genaue `wg-easy`-Version und Containerparameter
|
||||
- genaue NPM-Container-/Storage-Konfiguration
|
||||
- konkrete Fail2Ban-Jails und Filter
|
||||
- konkrete Modell-, Credential- und Quota-Policies der späteren API-Schicht
|
||||
- konkrete Keycloak-Realm-/Client-Struktur
|
||||
- konkrete DNS-/Domainnamen
|
||||
- konkrete Backup- und Monitoring-Implementierung
|
||||
|
||||
---
|
||||
|
||||
## 13. Architektur-Abnahmekriterien
|
||||
|
||||
Die Architektur gilt als korrekt umgesetzt, wenn mindestens folgende Eigenschaften nachweisbar sind:
|
||||
|
||||
- Nur vorgesehene öffentliche Ports sind aus dem Internet erreichbar.
|
||||
- Proxmox ist nicht direkt aus dem Internet erreichbar.
|
||||
- SSH des LLM-Servers ist nicht direkt aus dem Internet erreichbar.
|
||||
- `llama.cpp` ist nicht direkt aus dem Internet erreichbar.
|
||||
- Öffentliche Webservices terminieren an NPM.
|
||||
- Fail2Ban befindet sich zusammen mit NPM im Reverse-Proxy-LXC.
|
||||
- Familie-, Server-Admin- und LLM-Admin-Peers besitzen die beschlossenen, serverseitig durchgesetzten Zugriffsprofile.
|
||||
- Webanwendungen verwenden Keycloak/OIDC, sofern dies für den jeweiligen
|
||||
Nutzerpfad beschlossen und von der Anwendung belastbar unterstützt wird;
|
||||
Maschinen-APIs behalten eigene Credentials und Policies.
|
||||
- Die LLM-API wird über eine ausdrücklich beschlossene Policy-Schicht
|
||||
bereitgestellt und über individuelle Credentials geschützt; bei Auswahl von
|
||||
LiteLLM werden dessen Key-, Quota- und Routingfunktionen gesondert abgenommen.
|
||||
- Administrative Weboberflächen von Keycloak sind nicht öffentlich.
|
||||
- Service-LXCs bleiben voneinander isoliert und erlauben nur erforderliche Kommunikationsbeziehungen.
|
||||
|
||||
---
|
||||
|
||||
## 14. Umsetzungsstand
|
||||
|
||||
Die Architektur-Abnahmekriterien in Abschnitt 13 sind Sollkriterien und keine bestätigte Abnahme. Der aktuelle Projektstand und die priorisierten Befunde stehen im [Dokumentationsindex](../../../GNAI-000-DOC_Index.md); technische Nachweise und offene Prüfungen werden in GNAI-006 und GNAI-007 geführt. Backendisolation und lokale Netzwerksegmentierung sind ebenfalls noch nachzuweisen.
|
||||
@@ -0,0 +1,852 @@
|
||||
# Arbeitsentwurf
|
||||
|
||||
**Dokumenten-ID:** GNAI-008-PLAN
|
||||
**Titel:** Ansible-Deployment für den KI-Server – Planungsstand
|
||||
**Stand:** 2026-09-21
|
||||
**Status:** Nicht finaler Arbeitsentwurf zur Persistierung des aktuellen Planungsstands
|
||||
**Referenzen:** [Projektstand](../../../GNAI-000-DOC_Index.md), [Zielarchitektur](GNAI-004-INFRA_Architecture.md), [Modellbewertung](GNAI-009-PLAN_Llama-Model-Evaluation.md), [llama.cpp-Sollkonfiguration](GNAI-010-TECH_Llama-Serving.md), [Zugriffskonzept](../../GNAI-005-SEC_Access-Control.md)
|
||||
|
||||
> **Abgrenzung:** Dieses Dokument hält die Ergebnisse der am 19.09.2026
|
||||
> begonnenen und bis zum 21.09.2026 fortgeschriebenen Planungssession fest. Es ist
|
||||
> weder eine freigegebene technische Spezifikation noch
|
||||
> eine Installationsanleitung oder ein Nachweis über bereits umgesetzte
|
||||
> Komponenten. Entscheidungen, die noch geprüft oder bestätigt werden müssen,
|
||||
> sind ausdrücklich als vorläufig oder offen gekennzeichnet.
|
||||
|
||||
---
|
||||
|
||||
## Änderungsverlauf
|
||||
|
||||
| Datum | Version | Autor | Beschreibung |
|
||||
| --- | --- | --- | --- |
|
||||
| 2026-09-19 | 0.1 | spektr / Codex | Planungsstand zu Ansible, Gitea Actions, Runner, Freigaben und künftiger Dokumentation aus der Session festgehalten |
|
||||
| 2026-09-19 | 0.2 | spektr / Codex | vorläufige Ein-Repository-Strategie sowie logisch getrennte Production- und Testing-Umgebungen auf dem KI-Server ergänzt |
|
||||
| 2026-09-19 | 0.3 | spektr / Codex | exklusiven zeitlich gestaffelten Betrieb von Production und Testing ohne physische Ressourcenaufteilung als Planungsentscheidung übernommen |
|
||||
| 2026-09-19 | 0.4 | spektr / Codex | erstes Ansible-Control-Projekt und erfolgreich ausgeführten lesenden Live-Preflight als belegten Implementierungsstand ergänzt |
|
||||
| 2026-09-19 | 0.5 | spektr / Codex | technischen Schlüssel sowie Bootstrap- und Prüfplaybook für `ansible-deploy` vorbereitet; Ausführung bleibt interaktiv offen |
|
||||
| 2026-09-19 | 0.6 | spektr / Codex | `ansible-deploy` auf dem KI-Server eingerichtet und SSH-/sudo-Zugriff unabhängig erfolgreich verifiziert |
|
||||
| 2026-09-19 | 0.7 | spektr / Codex | minimale gemeinsame Host-Baseline mit Schutzprüfungen und getrennten Verzeichniswurzeln implementiert, angewendet und auf Idempotenz geprüft |
|
||||
| 2026-09-20 | 0.8 | spektr / Codex | gemeinsame Podman-/Quadlet-Runtime implementiert, installiert und ohne Images oder Container verifiziert |
|
||||
| 2026-09-20 | 0.9 | spektr / Codex | getrennte rootless Konten für AI Production, AI Testing und CPU-Encoding eingerichtet; Parallelitäts- und Repository-Grenzen ergänzt |
|
||||
| 2026-09-20 | 0.10 | spektr / Codex | ROCm als Backend für zwei RX 6900 XT, initialen `llama.cpp`-Modellstand und Layer-Split mit gleicher GPU-Gewichtung festgehalten |
|
||||
| 2026-09-20 | 0.11 | spektr / Codex | AMD-Gerätezugriff für beide rootless KI-Konten implementiert, angewendet und idempotent geprüft |
|
||||
| 2026-09-20 | 0.12 | spektr / Codex | gepinnten rootless ROCm-Probelauf für Testing implementiert; zwei `gfx1030`-Agents und zwei `llama.cpp`-ROCm-Geräte nachgewiesen |
|
||||
| 2026-09-21 | 0.13 | spektr / Codex | Swift-Qwen3.8 Q6_K mit 128k und `xhigh` als Testing-Kandidat übernommen; Modellbewertung und technische Sollkonfiguration ausgelagert |
|
||||
| 2026-09-21 | 0.14 | spektr / Codex | Unsloth UD-Q6_K als zweites installiertes 128k-Profil und A/B-Kandidat festgelegt |
|
||||
| 2026-09-21 | 0.15 | spektr / Codex | je Modell ein festes, gegenseitig ausgeschlossenes Quadlet vorgesehen und dynamischen llama.cpp-Router zurückgestellt |
|
||||
| 2026-09-21 | 0.16 | spektr / Codex | überholten Phasen- und Dokumentenbacklog konsolidiert, Umsetzungsreihenfolge an GNAI-001 delegiert und privilegierten Runner nach Recovery verschoben |
|
||||
|
||||
---
|
||||
|
||||
## 1. Zweck
|
||||
|
||||
Der KI-Server `ghostnet-ki` soll reproduzierbar mit Ansible aufgebaut und
|
||||
aktualisiert werden. Mehrere berechtigte Personen sollen Änderungen gemeinsam
|
||||
über Gitea entwickeln, prüfen, freigeben und nachvollziehbar ausrollen können.
|
||||
|
||||
Gesucht wird ein Verfahren, das:
|
||||
|
||||
- den vollständigen Sollzustand des Servers versioniert,
|
||||
- wiederholbare und möglichst idempotente Deployments ermöglicht,
|
||||
- Änderungen vor der Anwendung prüft,
|
||||
- einen nachvollziehbaren Freigabeweg für mehrere Mitwirkende bietet,
|
||||
- Geheimnisse und privilegierte Zugänge vom Quelltext trennt,
|
||||
- Fehler und Rollbacks betrieblich beherrschbar macht und
|
||||
- für einen einzelnen KI-Server ohne unnötig große Managementplattform
|
||||
betrieben werden kann.
|
||||
|
||||
Die Planung umfasst sowohl die spätere technische Implementierung als auch die
|
||||
Dokumentation für Administratoren, Entwickler und Deployment-Berechtigte.
|
||||
|
||||
---
|
||||
|
||||
## 2. Ausgangslage
|
||||
|
||||
Nach dem dokumentierten Stand besitzt der KI-Server die gemeinsame
|
||||
Ansible-/Podman-/ROCm-Grundlage unter `10.0.0.90`. Dauerhaftes `llama.cpp`-
|
||||
Serving und die Zielbild-Kandidaten LiteLLM, OpenWebUI und weitere Anwendungen
|
||||
sind noch nicht installiert. Gitea ist im bestehenden
|
||||
Serviceinventar unter `10.0.0.17:3000` geführt; Version, Actions-Konfiguration
|
||||
und Registry-Funktionen müssen vor der Implementierung live geprüft werden.
|
||||
|
||||
Der bestehende Account `ghost-agent` ist für lesende Prüfungen vorgesehen und
|
||||
besitzt nach dem letzten Abgleich keine `sudo`-Berechtigung. Er soll nach dem
|
||||
aktuellen Plan nicht zum privilegierten Deployment-Account erweitert werden.
|
||||
|
||||
Das Repository enthält inzwischen ein minimales Ansible-Control-Projekt mit
|
||||
Testing-Inventar, einem ausschließlich lesenden Preflight sowie Bootstrap- und
|
||||
Prüfplaybooks für `ansible-deploy`. Der privilegierte Deployment-Account ist
|
||||
eingerichtet und verifiziert. Schreibende Rollen verwalten die gemeinsamen
|
||||
Konfigurations- und Datenwurzeln sowie Podman und Quadlet. Anwendungsrollen und
|
||||
ein Production-Inventar existieren noch nicht. Der Preflight selbst nahm keine
|
||||
Änderungen vor; die bislang angewendeten Rollen bleiben auf die nachfolgend
|
||||
dokumentierten Zugangs-, Verzeichnis- und Runtime-Zustände begrenzt.
|
||||
|
||||
Ein fachlich vom LLM-Stack getrennter CPU-Encoding-Workload wird auf demselben
|
||||
Host vorgesehen. Sein Ansible-Sollzustand bleibt wegen des gemeinsamen Hosts im
|
||||
Infrastructure-Repository; Architektur und spätere Nutzer-SOPs werden unter
|
||||
[`docs/platform/llm-server/encoding/`](../encoding/GNENC-000-DOC_Index.md) getrennt geführt.
|
||||
|
||||
---
|
||||
|
||||
## 3. Vorläufiges Zielbild
|
||||
|
||||
Ansible bleibt die einzige technische Quelle für den gewünschten Zustand des
|
||||
KI-Servers. Gitea verwaltet Quelltext, Reviews, Freigaben und Jobprotokolle.
|
||||
Getrennte Runner- beziehungsweise Berechtigungsstufen führen CI-Prüfungen,
|
||||
Testing-Deployments und Production-Deployments aus. Die genaue Anzahl der
|
||||
Runner wird nach Prüfung der Gitea-Berechtigungsgrenzen festgelegt.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
DEV[Feature-Branch] --> PR[Pull Request]
|
||||
PR --> CI[statische CI ohne Serverzugang]
|
||||
CI --> TESTOK[freigegebener Testing-Lauf]
|
||||
TESTOK --> TESTRUNNER[Testing-Berechtigung]
|
||||
TESTRUNNER --> TEST[Testing auf ghostnet-ki]
|
||||
TEST --> REVIEW[Review und Merge nach main]
|
||||
REVIEW --> RELEASE[bewusste Production-Freigabe]
|
||||
RELEASE --> PRODRUNNER[Production-Berechtigung]
|
||||
PRODRUNNER --> PROD[Production auf ghostnet-ki]
|
||||
PROD --> VERIFY[Health- und Funktionstests]
|
||||
|
||||
UPSTREAM[versionierte Upstream-Artefakte] --> UPDATE[Versions-Pin im Infrastruktur-Repository]
|
||||
UPDATE --> PR
|
||||
```
|
||||
|
||||
Runner sollen keine eingehenden Verbindungen aus dem Internet benötigen. Sie
|
||||
holen Jobs bei Gitea ab und benötigen ausgehend nur die für Gitea, Paket- oder
|
||||
Imagequellen und den jeweiligen SSH-Zugriff zum KI-Server erforderlichen
|
||||
Verbindungen. CI-Jobs erhalten keinen SSH-Zugriff zum KI-Server.
|
||||
|
||||
---
|
||||
|
||||
## 4. Ergebnisse der bisherigen Planung
|
||||
|
||||
Die folgende Tabelle unterscheidet bewusst zwischen einer gewählten
|
||||
Planungsrichtung und noch nicht bestätigten Implementierungsdetails.
|
||||
|
||||
| Thema | Aktueller Planungsstand | Verbindlichkeit / Nachweis |
|
||||
| --- | --- | --- |
|
||||
| Konfigurationsmanagement | Ansible beschreibt Basissystem, Laufzeitumgebung, Dienste und Konfiguration | Control-Projekt, Preflight, `host_baseline`, `podman_runtime`, `workload_accounts`, `amd_gpu_access` und der ROCm-Container-Probelauf vorhanden und angewendet; Anwendungsrollen fehlen noch |
|
||||
| Quell- und Freigabeverfahren | Änderungen über Gitea-Branches und Pull Requests; kein regulärer Direkt-Push auf `main` | vorgesehen; Branch-Schutz noch nicht geprüft oder eingerichtet |
|
||||
| CI | Pull Requests erhalten Lint-, YAML- und Ansible-Syntaxprüfungen ohne produktive Zugangsdaten | vorgesehen; Workflow noch nicht vorhanden |
|
||||
| Produktionsausführung | Gitea Actions startet Ansible über eine dedizierte Production-Berechtigungsstufe | bevorzugte Lösung; Gitea-Version, Runner-Trennung und Actions-Funktion noch zu prüfen |
|
||||
| Runner-Platzierung | eigener kleiner LXC, getrennt von Gitea und KI-Server | bevorzugt; Host, Adresse und Ressourcen noch offen |
|
||||
| Job-Isolation | containerisierte Jobs; für den Gitea Runner ist nach aktueller Herstellerdokumentation Docker der unterstützte Standard | bevorzugt; lokale Freigabe und LXC-Eignung noch offen |
|
||||
| Parallelität | Produktionsrunner führt höchstens einen Job gleichzeitig aus | vorgesehen; Runner-Konfiguration noch nicht vorhanden |
|
||||
| Deployment-Freigabe | nach erfolgreichem Review bewusste Freigabe über geschützten `deploy-*`-Tag | bevorzugte Startvariante; Namensschema und berechtigte Teams noch offen |
|
||||
| Zielzugang | eigener technischer SSH-Account `ansible-deploy` mit separatem, quellnetzbeschränktem Schlüssel und validiertem `NOPASSWD: ALL` | auf dem KI-Server eingerichtet und unabhängig verifiziert; spätere Runner-Übernahme und Rotation offen |
|
||||
| Geheimnisse | verschlüsselte Ansible-Variablen plus getrennt gespeichertes Vault-Passwort und Runner-Zugangsdaten | Richtung angenommen; konkreter Secret-Workflow noch offen |
|
||||
| Artefaktversionen | Pakete, Collections und Container-Images werden auf konkrete Versionen, nach Möglichkeit Digests, festgelegt | vorgesehen |
|
||||
| Repository-Zuschnitt | `infrastructure` bleibt alleinige Quelle für alle Ansible-Zustände des Hosts; KI- und Encoding-Dokumentation werden getrennt geführt; weitere Quellcode-Repositories erst bei eigenständig entwickelten Anwendungen | als aktuelle Planungsentscheidung angenommen |
|
||||
| Anwendungsupdates | Upstream-Artefakte werden im Infrastruktur-Repository auf eine geprüfte Version festgelegt; eigene Build-Repositories sind vorerst nicht Bestandteil des Projekts | vorgesehen; Registry- und Buildverfahren bei späterem Eigenentwicklungsbedarf neu bewerten |
|
||||
| Automatische Updates | kein unbeaufsichtigtes Produktionsupdate über `latest` oder Watchtower | vorläufige Sicherheitsentscheidung |
|
||||
| Laufzeitumgebungen | Production und Testing werden als getrennte Stacks auf demselben physischen KI-Server betrieben | als aktuelle Planungsentscheidung angenommen |
|
||||
| Production | stabiler, extern nutzbarer Stand mit bewusst freigegebenen Versionen | Zielbild; Dienste noch nicht eingerichtet |
|
||||
| Testing | interner, kurzlebiger oder zurücksetzbarer Stand für kontrollierte Tests vor dem Merge nach `main`; nutzt während eines Wartungsfensters beide GPUs exklusiv gegenüber Production | Zielbild; Dienste, Zugriffsweg und Umschaltverfahren noch nicht eingerichtet |
|
||||
| Ressourcenbetrieb | AI Production und AI Testing laufen nicht gleichzeitig; der getrennte CPU-Encoding-Workload darf nachts parallel zur jeweils aktiven KI-Umgebung laufen | als aktuelle Planungsentscheidung angenommen; Grenzwerte und Lasttest offen |
|
||||
| Workload-Konten | `gnai-production`, `gnai-testing` und `gn-encoding` verwenden getrennte rootless Podman-Stores und systemd-User-Manager | eingerichtet und mit `changed=0` verifiziert |
|
||||
| Betriebsnachweis | erfolgreicher Lauf dokumentiert Git-Commit, Zielhost, Zeitpunkt, Prüfungen und Ergebnis | vorgesehen |
|
||||
| Rollback | Konfigurationsänderung über Revert und erneute Freigabe; persistente Daten zusätzlich über getestete Backups wiederherstellen | Grundprinzip; konkrete SOP noch zu erstellen |
|
||||
|
||||
Eine spätere Umstellung auf ein automatisches Deployment nach jedem Merge in
|
||||
`main` bleibt möglich. Zu Beginn soll die Freigabe jedoch bewusst und
|
||||
nachvollziehbar erfolgen, während die technische Ausführung vollständig
|
||||
automatisiert wird.
|
||||
|
||||
---
|
||||
|
||||
## 5. Verworfene oder zurückgestellte Varianten
|
||||
|
||||
### 5.1 Ausschließliche Ausführung vom Admin-PC
|
||||
|
||||
Lokale Ansible-Ausführung bleibt für Entwicklung, Diagnose und Notfälle
|
||||
sinnvoll. Sie soll nicht der reguläre Produktionsweg werden, weil Ausführung,
|
||||
Werkzeugversionen und Protokolle sonst von einem einzelnen Arbeitsplatz
|
||||
abhängen.
|
||||
|
||||
### 5.2 `ansible-pull` auf dem KI-Server
|
||||
|
||||
Der Zielserver könnte das Repository periodisch selbst abrufen und lokal
|
||||
anwenden. Für den einzelnen zentral erreichbaren Server bietet das derzeit
|
||||
weniger klare Freigaben und eine ungünstigere Trennung zwischen Zielsystem und
|
||||
Deployment-Steuerung. Die Variante bleibt zurückgestellt.
|
||||
|
||||
### 5.3 AWX
|
||||
|
||||
AWX bietet eine eigene Oberfläche, Rollen, Zeitpläne und Inventarverwaltung.
|
||||
Der zusätzliche Plattformbetrieb erscheint für den aktuellen Umfang zu groß.
|
||||
AWX kann erneut bewertet werden, wenn mehrere Zielsysteme, feinere delegierte
|
||||
Rechte oder umfangreiche Jobpläne verwaltet werden müssen.
|
||||
|
||||
---
|
||||
|
||||
## 6. Repository-Verantwortung
|
||||
|
||||
Vorerst wird ausschließlich das bestehende Repository `infrastructure`
|
||||
verwendet. Es soll den vollständigen Sollzustand des KI-Servers, beide
|
||||
Laufzeitumgebungen und die zugehörigen Workflows besitzen. Damit verändern
|
||||
nicht mehrere unabhängige Deployment-Repositories denselben Host.
|
||||
|
||||
Vorgesehene grobe Struktur:
|
||||
|
||||
```text
|
||||
ansible/
|
||||
├── ansible.cfg
|
||||
├── collections/
|
||||
│ └── requirements.yml
|
||||
├── inventories/
|
||||
│ ├── production/
|
||||
│ └── testing/
|
||||
├── playbooks/
|
||||
│ ├── host.yml
|
||||
│ ├── environment.yml
|
||||
│ └── verify.yml
|
||||
└── roles/
|
||||
├── host_baseline/
|
||||
├── environment_stack/
|
||||
└── weitere_fachrollen/
|
||||
|
||||
.gitea/workflows/
|
||||
├── validate.yml
|
||||
├── deploy-testing.yml
|
||||
└── deploy-production.yml
|
||||
```
|
||||
|
||||
Diese Struktur ist ein Ausgangspunkt und noch keine Festlegung aller Rollen.
|
||||
`host.yml` und `environment.yml` sollen gemeinsam den unterstützten
|
||||
Gesamtzustand herstellen. Ansible-Tags können Diagnose und gezielte Wartung
|
||||
unterstützen, dürfen aber nicht zu ungetesteten alternativen Installationswegen
|
||||
führen.
|
||||
|
||||
Unveränderte Drittanbieterkomponenten wie `llama.cpp`, LiteLLM oder OpenWebUI
|
||||
erhalten derzeit kein eigenes Repository. Ihre freigegebenen Versionen oder
|
||||
Image-Digests werden im Infrastruktur-Repository geführt. Ein weiteres
|
||||
Repository wird erst angelegt, wenn tatsächlich eigener, unabhängig
|
||||
versionierter Quellcode für eine Anwendung, einen MCP Router oder MCP Server
|
||||
entwickelt wird. Diese spätere Erweiterung ändert nichts daran, dass das
|
||||
Infrastruktur-Repository allein festlegt, welche Artefaktversion in welcher
|
||||
Umgebung läuft.
|
||||
|
||||
---
|
||||
|
||||
## 7. Production und Testing auf einem Host
|
||||
|
||||
### 7.1 Geltungsbereich der Trennung
|
||||
|
||||
Production und Testing laufen zunächst auf demselben physischen KI-Server.
|
||||
Damit können Anwendungscontainer, Konfigurationen und Daten sauber logisch
|
||||
getrennt werden. Die Hardware kann wegen des hohen Leistungsbedarfs nicht
|
||||
dauerhaft zwischen beiden KI-Umgebungen aufgeteilt werden. Stattdessen erhält
|
||||
die jeweils aktive KI-Umgebung beide GPUs exklusiv gegenüber der anderen
|
||||
KI-Umgebung. Ein fachlich getrennter CPU-Encoding-Workload darf außerhalb der
|
||||
Stoßzeiten parallel laufen. Es entstehen dennoch keine getrennten
|
||||
Fehlerdomänen:
|
||||
|
||||
- Kernel, AMD-Treiber beziehungsweise ROCm, Podman und systemd werden gemeinsam
|
||||
verwendet;
|
||||
- ein Hostausfall betrifft beide Umgebungen;
|
||||
- ein fehlerhafter Testing-Stand kann den gemeinsamen Host beeinträchtigen und
|
||||
dadurch den Wiederanlauf von Production verzögern;
|
||||
- Kernel-, Treiber- und Host-Runtime-Updates können nicht realitätsnah in
|
||||
Testing geprüft werden, ohne den gemeinsamen Host zu verändern.
|
||||
|
||||
Testing ist deshalb für neue Anwendungsstände, Konfigurationen, MCP-Komponenten
|
||||
und Integrationen vorgesehen. Für risikoarme Vorabtests von Kernel-, Treiber-
|
||||
oder Podman-Upgrades wäre später ein zweiter Host oder eine ausreichend
|
||||
vergleichbare separate Maschine erforderlich.
|
||||
|
||||
Die von externen Nutzern erwartete Stabilität von Production gilt außerhalb
|
||||
angekündigter Test- und Wartungsfenster. Solange nur dieser eine ausgelastete
|
||||
Host verfügbar ist, verursachen realistische Tests mit voller Hardware eine
|
||||
geplante Unterbrechung von Production.
|
||||
|
||||
### 7.2 Isolationsziele
|
||||
|
||||
| Bereich | Production | Testing |
|
||||
| --- | --- | --- |
|
||||
| Erreichbarkeit | für freigegebene Dienste über NPM; Backend- und Managementports bleiben intern | nur aus freigegebenem LAN/VPN; keine öffentliche NPM-Route |
|
||||
| Container- und Unit-Namen | eigener eindeutiger Präfix | eigener eindeutiger Präfix |
|
||||
| Podman-Netz | eigenes internes Netz | eigenes internes Netz ohne Verbindung zum Production-Netz |
|
||||
| Konfiguration | eigener Verzeichnisbaum | eigener Verzeichnisbaum |
|
||||
| Persistente Daten | eigene Volumes mit Backup- und Restore-Verfahren | eigene zurücksetzbare Volumes; keine Production-Datenkopie ohne Bereinigung und Freigabe |
|
||||
| Secrets und API-Keys | ausschließlich Production-Credentials | eigene eingeschränkte Test-Credentials |
|
||||
| Ports und Hostnamen | stabile freigegebene Endpunkte | getrennte interne Ports und noch festzulegende Testhostnamen |
|
||||
| Ressourcen | erhält im Normalbetrieb beide GPUs und das festzulegende KI-Ressourcenbudget | erhält im Testfenster beide GPUs und das festzulegende KI-Ressourcenbudget |
|
||||
| GPU | beide GPUs, solange Production aktiv ist | beide GPUs, nachdem Production vollständig beendet wurde |
|
||||
| Ausführungszustand | standardmäßig aktiv | standardmäßig gestoppt |
|
||||
| Verfügbarkeit | stabiler Betriebsstand außerhalb angekündigter Wartungsfenster | best effort; wird nach jedem Testlauf wieder vollständig gestoppt |
|
||||
|
||||
Eine gleichzeitige Inferenzlast beider KI-Umgebungen ist nicht vorgesehen. Vor dem
|
||||
Start von Testing muss nachweisbar sein, dass Production beendet wurde und
|
||||
keine zugehörigen Prozesse mehr CPU-, RAM- oder GPU-Ressourcen halten. Vor dem
|
||||
Wiederanlauf von Production muss Testing entsprechend vollständig beendet und
|
||||
sein temporärer Zustand bereinigt sein. Speicherplatz- und Prozessgrenzen
|
||||
bleiben auch bei exklusiver GPU-Nutzung sinnvoll, damit ein Test nicht den
|
||||
gemeinsamen Datenträger füllt oder den Host dauerhaft beeinträchtigt.
|
||||
|
||||
Der CPU-basierte Encoding-Workload ist nicht Teil dieser KI-Umschaltung. Er darf
|
||||
nachts parallel zu Production oder Testing laufen, erhält keinen GPU-Zugriff
|
||||
und muss über CPU-, Speicher- und I/O-Grenzen die Reaktionsfähigkeit der
|
||||
KI-Dienste schützen. Die Betreiberfestlegung „CPU-basiert“ und die geplante
|
||||
Parallelität sind noch durch einen repräsentativen gemeinsamen Lasttest zu
|
||||
bestätigen. Zeitfenster und Grenzwerte werden ausschließlich im getrennten
|
||||
[Encoding-Plan](../encoding/GNENC-001-PLAN_Architecture.md) spezifiziert.
|
||||
|
||||
Große schreibgeschützte Modelldateien können später aus einem gemeinsamen,
|
||||
nur lesbar eingebundenen Modellspeicher bereitgestellt werden. Datenbanken,
|
||||
Uploads, Caches mit Schreibzugriff, Benutzerkonten und Anwendungsschlüssel
|
||||
bleiben vollständig getrennt. Ob ein gemeinsamer Modellspeicher technisch und
|
||||
betrieblich sinnvoll ist, wird erst mit dem Modell- und Backupkonzept
|
||||
entschieden.
|
||||
|
||||
### 7.3 Exklusives Umschaltverfahren
|
||||
|
||||
Ein Testing-Deployment ist zugleich ein geplantes Wartungsfenster für
|
||||
Production. Der vorläufige Ablauf lautet:
|
||||
|
||||
1. konkreten PR-Commit und Testumfang festlegen;
|
||||
2. Testing-Lauf durch eine berechtigte Person freigeben und Wartungsfenster
|
||||
dokumentieren;
|
||||
3. letzten funktionsfähigen Production-Stand und erforderlichen Backupstatus
|
||||
feststellen;
|
||||
4. neue externe Anfragen nach Möglichkeit kontrolliert abweisen und Production
|
||||
geordnet beenden;
|
||||
5. prüfen, dass alle Production-Units, -Container und GPU-Prozesse beendet
|
||||
sind;
|
||||
6. einen exklusiven Lock für den KI-Umgebungswechsel setzen;
|
||||
7. Testing für den festgelegten Commit ausrollen und mit der vollständigen
|
||||
Hardware starten;
|
||||
8. Health Checks und fachliche Tests ausführen und Ergebnisse dem Pull Request
|
||||
zuordnen;
|
||||
9. Testing unabhängig vom Testergebnis vollständig stoppen;
|
||||
10. prüfen, dass alle Testing-Units, -Container und GPU-Prozesse beendet sind;
|
||||
11. Production unverändert auf dem zuletzt freigegebenen Stand starten;
|
||||
12. Production-Health- und Inferenztests ausführen und erst danach das
|
||||
Wartungsfenster beenden.
|
||||
|
||||
Der Wiederanlauf von Production darf nicht ausschließlich von einem letzten
|
||||
Schritt im Gitea-Job abhängen. Runner-Abbruch, Netzwerkverlust oder ein
|
||||
fehlgeschlagener Test müssen durch einen hostseitigen Recovery-Mechanismus und
|
||||
eine dokumentierte manuelle SOP beherrscht werden. Die konkrete Umsetzung kann
|
||||
beispielsweise einen systemd-gesteuerten Umschaltdienst, einen exklusiven Lock,
|
||||
Timeouts und einen definierten Rückfall auf Production kombinieren; sie wird
|
||||
erst in der technischen Spezifikation festgelegt.
|
||||
|
||||
Der Testing-Deployment-Account soll Production nicht frei verwalten können. Er
|
||||
darf lediglich eine eng definierte hostseitige Umschaltoperation starten und
|
||||
deren Status abfragen. Der Umschaltdienst besitzt die notwendigen Rechte zum
|
||||
geordneten Stoppen und Wiederanlaufen der beiden Stacks, stellt dem Testing-Job
|
||||
aber weder Production-Secrets noch allgemeine Production-Verwaltungsrechte zur
|
||||
Verfügung.
|
||||
|
||||
Production und Testing dürfen niemals gleichzeitig als aktiv markiert sein.
|
||||
Jeder Start prüft den Gegenstatus und bricht bei einem widersprüchlichen oder
|
||||
unklaren Zustand ab. Der Encoding-Workload bleibt von diesem KI-Lock unabhängig;
|
||||
ein allgemeines Hostwartungsfenster kann weiterhin alle Workloads stoppen.
|
||||
|
||||
### 7.4 Vorgesehene Ansible-Abbildung
|
||||
|
||||
Gemeinsame Hostaufgaben und umgebungsspezifische Anwendungsaufgaben werden
|
||||
getrennt:
|
||||
|
||||
```text
|
||||
ansible/
|
||||
├── playbooks/
|
||||
│ ├── host.yml
|
||||
│ ├── ai.yml
|
||||
│ ├── video-encoding.yml
|
||||
│ └── site.yml
|
||||
└── roles/
|
||||
├── host_baseline/
|
||||
├── podman_runtime/
|
||||
├── workload_accounts/
|
||||
├── ai_stack/
|
||||
├── ai_environment_switch/
|
||||
└── video_encoding/
|
||||
```
|
||||
|
||||
`host.yml` verwaltet gemeinsame Pakete, Treiber, Benutzer und
|
||||
Runtime-Komponenten einmalig. `ai.yml` verwaltet ausschließlich den LLM-Stack
|
||||
und genau eine explizit gewählte KI-Umgebung. `video-encoding.yml` verwaltet nur
|
||||
Encoding-Pfade, Images, Quadlets, Timer und Prüfungen. `site.yml` kann den
|
||||
unterstützten Gesamtzustand in definierter Reihenfolge herstellen. Hoständerungen
|
||||
dürfen nicht beiläufig durch einen Workload-Lauf erfolgen.
|
||||
|
||||
Für den ersten umgesetzten Baseline-Stand gelten getrennte Konfigurationsbäume
|
||||
unter `/etc/ghostnet-ai/{production,testing}` und getrennte persistente
|
||||
Datenbäume unter `/var/lib/ghostnet-ai/{production,testing}`. Der Pfad
|
||||
`/var/lib/ghostnet-ai/shared` ist für später ausdrücklich freigegebene
|
||||
gemeinsame Daten reserviert. Konkrete Unterverzeichnisse, Eigentümer sowie
|
||||
Unit- und Container-Namen werden mit den jeweiligen Fachrollen festgelegt. Die
|
||||
Konten `gnai-production` und `gnai-testing` besitzen inzwischen ihre
|
||||
Datenwurzeln und getrennten rootless Podman-Stores; Konfiguration bleibt
|
||||
rootverwaltet und für die jeweilige Gruppe lesbar. Der Encoding-Workload
|
||||
verwendet entsprechend `/etc/ghostnet-encoding` und
|
||||
`/var/lib/ghostnet-encoding`.
|
||||
|
||||
### 7.5 ROCm- und `llama.cpp`-Ausgangskonfiguration
|
||||
|
||||
Der Betreiber hat zwei Sapphire Nitro+ Radeon RX 6900 XT mit jeweils 16 GiB
|
||||
VRAM als Zielhardware bestätigt. Für diesen homogenen AMD-Aufbau wird ROCm als
|
||||
Backend verwendet. Vulkan bleibt eine mögliche spätere Alternative, falls der
|
||||
Server um GPUs mit unterschiedlichen Backend-Anforderungen erweitert wird.
|
||||
|
||||
Als Ausgangspunkt dient eine bereits auf einer RX 7900 XTX verwendete
|
||||
Podman-Compose-Konfiguration. Die Serverumsetzung wird daraus mit folgenden
|
||||
Abweichungen als rootless Quadlet erzeugt:
|
||||
|
||||
- das Image `ghcr.io/ggml-org/llama.cpp:server-rocm` ist für den ersten
|
||||
Testing-Stand als Build `b11058`, Revision
|
||||
`f072b103714dfa1eee531f80b24512faf38e3dd2` und amd64-Manifest
|
||||
`sha256:b429b8be10d3d0353a09c98f4ab840fe516f779858a28f9fd4e5bdf10bc63cb4`
|
||||
festgelegt;
|
||||
- `/dev/kfd` und die tatsächlich benötigten `/dev/dri`-Geräte werden explizit
|
||||
eingebunden; `GroupAdd=keep-groups` erhält die nötigen ergänzenden
|
||||
Geräte-Gruppen im rootless Container;
|
||||
- das gemeinsame Modellverzeichnis wird nur lesbar unter `/models`
|
||||
eingebunden;
|
||||
- `llama-server` erhält keinen Host-Port und ist ausschließlich für die später
|
||||
beschlossene API-Policy-Schicht im umgebungseigenen Podman-Netz erreichbar;
|
||||
- systemd übernimmt Start, Neustart, Abhängigkeiten und Health-Überwachung;
|
||||
- Modell- und Laufzeitwerte werden als Ansible-Variablen in die
|
||||
Quadlet-Kommandozeile gerendert. Zusätzliche Container-Environment-Variablen
|
||||
werden nur verwendet, wenn das Backend sie technisch benötigt;
|
||||
- `HSA_OVERRIDE_GFX_VERSION=11.0.0` aus der RX-7900-XTX-Konfiguration wird
|
||||
nicht übernommen. Die RX 6900 XT verwendet nativ `gfx1030`, das im
|
||||
vorgesehenen ROCm-Image enthalten sein muss. Ein Override wäre nur nach
|
||||
einem fehlgeschlagenen Geräte-/Backendtest zulässig und müsste für RDNA2
|
||||
gesondert begründet werden;
|
||||
- `label=disable` wird nur übernommen, falls eine tatsächlich aktive
|
||||
SELinux-Konfiguration dies erfordert. Für den dokumentierten Debian-Host
|
||||
wird es nicht vorsorglich gesetzt.
|
||||
|
||||
Die ausführliche Kandidatenbewertung steht in
|
||||
[GNAI-009-PLAN](GNAI-009-PLAN_Llama-Model-Evaluation.md), die technische
|
||||
Sollkonfiguration einschließlich revisionsgebundener Downloadquelle und
|
||||
SHA-256 in [GNAI-010-TECH](GNAI-010-TECH_Llama-Serving.md). Der bevorzugte
|
||||
erste Testing-Kandidat ist:
|
||||
|
||||
| Parameter | Ausgangswert |
|
||||
| --- | --- |
|
||||
| Modelldatei | `Swift-Qwen3.8-27B-Q6_K.gguf` |
|
||||
| API-Alias | `swift-qwen3.8-27b-q6k` |
|
||||
| Kontext | 131.072 Token; Inbetriebnahme zunächst mit 65.536 |
|
||||
| parallele Slots | 1 |
|
||||
| maximale Ausgabe | zunächst 32.768 Token; gesonderter 65.536-Token-Test |
|
||||
| GPU-Layer | alle |
|
||||
| Flash Attention | aktiviert |
|
||||
| K-/V-KV-Cache | jeweils `q8_0` |
|
||||
| Reasoning Effort | `xhigh`, ohne kurzen Reasoning-Cap |
|
||||
| MTP | zunächst deaktiviert; spätere A/B-Messung mit `n-max=2` |
|
||||
| Vision-Projektor | zunächst nicht geladen |
|
||||
|
||||
Samplingwerte und Chat-Template-Parameter gehören zum Modellprofil und nicht
|
||||
zu allgemeinen Rollendefaults. Dadurch können andere Modelle später eigene
|
||||
geprüfte Profile verwenden, ohne die gemeinsame `llama_cpp`-Rolle zu ändern.
|
||||
Zusätzlich wird `Qwen3.8-27B-UD-Q6_K.gguf` aus dem revisionsgebundenen
|
||||
Unsloth-Repository installiert. Es läuft ebenfalls mit 131.072 Token Kontext.
|
||||
Ein kontrollierter A/B-Test verwendet für beide Kandidaten `xhigh`; ein
|
||||
zweiter Vergleich stellt Swift `xhigh` dem vorgesehenen Unsloth-`medium`-
|
||||
Praxisprofil gegenüber. Beide Dateien liegen auf dem Host, aber immer nur ein
|
||||
Modell wird in den VRAM geladen. Jedes Modell erhält ein eigenes festes
|
||||
Testing-Quadlet; der Wechsel stoppt die aktive Unit vollständig, bevor die
|
||||
andere startet. Der im gepinnten Build vorhandene dynamische `llama.cpp`-
|
||||
Router-Modus bleibt für die erste Ausbaustufe bewusst zurückgestellt.
|
||||
|
||||
Für die beiden gleich großen GPUs wird zunächst
|
||||
`--split-mode layer --tensor-split 1,1` festgelegt. Der Layer-Split minimiert
|
||||
GPU-übergreifende Kommunikation und passt damit zur dokumentierten
|
||||
PCIe-Topologie, bei der die zweite Karte über einen langsameren, am PCH
|
||||
angebundenen Slot läuft. `row` wird wegen seiner Ablösung im aktuellen
|
||||
`llama.cpp` nicht als neue Betriebsvariante vorgesehen. Der experimentelle
|
||||
`tensor`-Split bleibt ein späterer Benchmarkkandidat; er benötigt deutlich
|
||||
mehr Kommunikation zwischen den Karten und ist mit dem geplanten
|
||||
quantisierten V-KV-Cache derzeit nicht kompatibel.
|
||||
|
||||
Der rootless Container-Probelauf hat inzwischen zwei `gfx1030`-Agents sowie
|
||||
`ROCm0` und `ROCm1` mit jeweils rund 16 GiB VRAM bestätigt. Vor dem dauerhaften
|
||||
Start muss ein begrenzter Modelltest noch die Device-Reihenfolge und die
|
||||
tatsächliche 1:1-Verteilung in den `llama.cpp`-Startlogs bestätigen. Zusätzlich
|
||||
werden Prompt-Verarbeitung, Token-Generierung, VRAM-Nutzung und Stabilität bei
|
||||
131.072 Token Kontext gemessen. Der 1:1-Wert ist erst nach diesem Test ein
|
||||
abgenommener Betriebsparameter.
|
||||
|
||||
---
|
||||
|
||||
## 8. Vorgesehener Workflow
|
||||
|
||||
### 8.1 Änderung und Prüfung
|
||||
|
||||
1. Repository klonen und dokumentierte lokale Ansible-Umgebung einrichten.
|
||||
2. Feature-Branch anlegen.
|
||||
3. Rollen, Variablen, Templates und zugehörige Dokumentation ändern.
|
||||
4. lokale Lint-, Syntax- und nach Möglichkeit Check-Mode-Prüfungen ausführen.
|
||||
5. Pull Request erstellen.
|
||||
6. automatisierte CI-Prüfungen ohne produktive SSH- oder Vault-Zugangsdaten
|
||||
ausführen.
|
||||
7. erste menschliche Prüfung und einen Testing-Lauf für den konkreten Commit
|
||||
freigeben;
|
||||
8. Testing-Ergebnis im Pull Request dokumentieren;
|
||||
9. fachliches Abschlussreview und Freigabe durch eine zweite berechtigte
|
||||
Person und
|
||||
10. nach erfolgreichem Statuscheck in den geschützten `main`-Branch mergen.
|
||||
|
||||
### 8.2 Testing-Deployment vor dem Merge
|
||||
|
||||
Ein Pull Request führt zunächst nur statische CI-Prüfungen in einer isolierten
|
||||
Jobumgebung ohne SSH-, Vault- oder Serverzugang aus. Nach einer ersten
|
||||
menschlichen Prüfung kann eine berechtigte Person einen kontrollierten
|
||||
Testing-Lauf für den konkreten Commit auslösen.
|
||||
|
||||
Der Testing-Lauf:
|
||||
|
||||
1. verwendet ausschließlich den Testing-Deployment-Account und
|
||||
Testing-Secrets;
|
||||
2. erhält erst nach einem freigegebenen Wartungsfenster Zugriff auf den
|
||||
Umschaltvorgang;
|
||||
3. fordert über die fest definierte hostseitige Umschaltoperation den
|
||||
kontrollierten Production-Stopp an und prüft den vollständigen Stillstand;
|
||||
4. darf im aktiven Testabschnitt nur Testing-Pfade, -Units, -Container und
|
||||
-Netze verwalten;
|
||||
5. zeichnet den getesteten Commit unverwechselbar auf;
|
||||
6. führt technische Health Checks und die für die Änderung notwendigen
|
||||
Funktionstests aus;
|
||||
7. veröffentlicht keine Testdienste im Internet;
|
||||
8. kann die Testing-Daten vor oder nach dem Lauf auf einen bekannten Stand
|
||||
zurücksetzen und
|
||||
9. muss Testing stoppen sowie den letzten freigegebenen Production-Stand
|
||||
wieder starten und prüfen, auch wenn der eigentliche Test fehlschlägt.
|
||||
|
||||
Beliebiger Pull-Request-Code darf nicht automatisch mit privilegierten
|
||||
Serverzugängen ausgeführt werden. Runner-Labels allein gelten nicht als
|
||||
ausreichende Berechtigungsgrenze. Vor der Implementierung ist zu prüfen, wie
|
||||
Gitea in der eingesetzten Version Secrets, interne Branches, Forks und manuell
|
||||
freigegebene Jobs behandelt. Production-Credentials werden keinem
|
||||
Pull-Request-Workflow bereitgestellt.
|
||||
|
||||
### 8.3 Production-Deployment
|
||||
|
||||
1. berechtigte Person erzeugt einen geschützten Deployment-Tag auf einem
|
||||
geprüften Commit;
|
||||
2. der repository-spezifische Runner übernimmt den Job mit einer Parallelität
|
||||
von eins;
|
||||
3. der Job prüft erneut Syntax und Abhängigkeiten;
|
||||
4. optionaler Check-Modus zeigt erwartete Änderungen, soweit die verwendeten
|
||||
Module ihn zuverlässig unterstützen;
|
||||
5. `environment.yml` wendet den Production-Sollzustand auf `ghostnet-ki` an;
|
||||
6. `verify.yml` prüft Dienste, interne Endpunkte, Containerzustand und später
|
||||
einen kleinen Inferenztest;
|
||||
7. der Lauf hält Commit, Imageversionen oder Digests sowie Prüfergebnis fest;
|
||||
8. erst nach allen Prüfungen gilt das Deployment als erfolgreich.
|
||||
|
||||
Ein Production-Deployment verwendet ausschließlich einen Commit aus dem
|
||||
geschützten `main`-Branch und eine eigene bewusste Freigabe. Ein erfolgreicher
|
||||
Testing-Lauf ist Voraussetzung für fachlich relevante Änderungen, ersetzt aber
|
||||
weder Review noch Production-Backup und Freigabe.
|
||||
|
||||
Check Mode ist nur eine zusätzliche Kontrolle. Module und Shell-Kommandos, die
|
||||
keine vollständige Simulation unterstützen, müssen durch gezielte Tests und
|
||||
idempotente Implementierung abgesichert werden.
|
||||
|
||||
---
|
||||
|
||||
## 9. Zugangsdaten und Sicherheitsgrenzen
|
||||
|
||||
Der Produktionsrunner verarbeitet privilegierte Zugangsdaten und muss daher
|
||||
wie ein Administrationssystem behandelt werden. Vorgesehen sind:
|
||||
|
||||
- repository-spezifische Runner-Registrierung und eindeutiges Runner-Label,
|
||||
- keine Ausführung fremder oder öffentlicher Repositories,
|
||||
- ein technischer Zielaccount getrennt von persönlichen Konten und
|
||||
`ghost-agent`,
|
||||
- getrennte technische Berechtigungen und Schlüssel für Production und
|
||||
Testing, soweit dies mit der gewählten Podman-/systemd-Verwaltung belastbar
|
||||
durchsetzbar ist,
|
||||
- ein eigener SSH-Schlüssel ausschließlich für das Deployment,
|
||||
- geprüfter und fest hinterlegter SSH-Host-Key,
|
||||
- keine Passwort- oder Root-Anmeldung per SSH,
|
||||
- möglichst eng begrenzter Netzwerkzugang des Runner-LXC,
|
||||
- keine Secrets in Repository, Jobausgaben oder diff-fähigen Templates,
|
||||
- `no_log` und deaktivierte Diffs für geheimnishaltige Tasks,
|
||||
- getrennte Aufbewahrung des Vault-Passworts im Runner und im administrativen
|
||||
Passwortmanager sowie
|
||||
- dokumentierte Rotation und Sperrung von Runner-, SSH- und Vault-Zugangsdaten.
|
||||
|
||||
Der Testing-Zugang darf keinen Zugriff auf Production-Secrets oder
|
||||
Production-Daten erhalten. Die Produktionsberechtigung darf nicht allein durch
|
||||
die Auswahl eines frei benennbaren Runner-Labels erreichbar sein. Das genaue
|
||||
Verfahren wird nach Prüfung der eingesetzten Gitea-Version festgelegt. Die
|
||||
notwendige zeitliche Umschaltung wird über eine eng definierte hostseitige
|
||||
Operation vermittelt und ist keine allgemeine Production-Berechtigung des
|
||||
Testing-Accounts.
|
||||
|
||||
Der Deployment-Account besitzt derzeit bewusst `NOPASSWD: ALL`, weil
|
||||
Paketverwaltung, systemd, Treiber und rootful Containerverwaltung weitreichende
|
||||
Rechte erfordern. Eine formal kleine Befehlsliste wäre nur sinnvoll, wenn sie
|
||||
den tatsächlichen Ansible-Betrieb nicht durch unsichere Umgehungen oder fragile
|
||||
Sonderfälle ersetzt. Schlüssel, Account und berechtigte Workflows sind deshalb
|
||||
als root-äquivalente Sicherheitsgrenze zu behandeln. Die spätere Trennung von
|
||||
Production- und Testing-Berechtigungen bleibt davon unabhängig zu spezifizieren.
|
||||
|
||||
---
|
||||
|
||||
## 10. Zu erstellende technische Dokumentation und SOPs
|
||||
|
||||
Die frühere Liste aus neun Einzeldokumenten war für den aktuellen Umfang zu
|
||||
feingranular und hätte Rollen-READMEs, Betriebsabläufe und Sicherheitsregeln
|
||||
mehrfach beschrieben. Dokumentation wird deshalb entlang realer Zielgruppen
|
||||
gebündelt und erst gemeinsam mit der zugehörigen Implementierung erstellt:
|
||||
|
||||
| Artefakt | Inhalt |
|
||||
| --- | --- |
|
||||
| Deployment-Architektur | Runner- und Secret-Grenzen, Inventar, Freigaben, Production-/Testing-Isolation und Abnahmekriterien |
|
||||
| Contributor- und Deployment-SOP | lokales Environment, Änderung, Review, statische CI, bewusste Freigabe und Deploymentnachweis |
|
||||
| Betriebs- und Recovery-SOP | Testing-Fenster, Environment-Wechsel, Rollback, Backup/Restore und Störungsbehandlung |
|
||||
| Rollen-READMEs | konkrete Variablen, Voraussetzungen, Aufrufe, erwartete Ergebnisse und technische Fehlerfälle |
|
||||
|
||||
Ein eigenes Dokument entsteht nur bei eigenständiger Zielgruppe oder
|
||||
Vertrauensgrenze. Eine Zielbeschreibung allein gilt nicht als Nachweis einer
|
||||
erfolgreichen Einrichtung.
|
||||
|
||||
---
|
||||
|
||||
## 11. Umsetzungsreihenfolge
|
||||
|
||||
Der frühere Phasenplan vermischte bereits erledigte Grundlagen mit offener
|
||||
Arbeit und stellte die Gitea-Integration pauschal vor den ersten
|
||||
Anwendungsnachweis. Er wird durch den statusbasierten
|
||||
[High-Level-Implementierungsplan](../../../GNAI-001-PLAN_Project-Roadmap.md) ersetzt.
|
||||
|
||||
Für diesen Ansible-Teilplan gelten daraus drei Leitplanken:
|
||||
|
||||
1. Der erste interne `llama.cpp`-Vertical-Slice wird lokal kontrolliert und
|
||||
ohne öffentliche Route ausgerollt.
|
||||
2. Statische CI erhält keine SSH- oder Deployment-Secrets.
|
||||
3. Ein privilegierter Runner folgt erst nach stabilem Rollen-, Rollback- und
|
||||
Recovery-Verfahren.
|
||||
|
||||
---
|
||||
|
||||
## 12. Verbleibende Deployment-Entscheidungen
|
||||
|
||||
Produkt- und Prioritätsentscheidungen stehen zentral in GNAI-001. Für die
|
||||
spätere Delivery-Kette bleiben hier nur folgende technische Fragen:
|
||||
|
||||
1. Welche Gitea-/Actions-/Runner-Version und welche Schutzfunktionen sind live
|
||||
verfügbar?
|
||||
2. Wo läuft der repository-spezifische Runner, und welche Job-Isolation wird
|
||||
nachweisbar unterstützt?
|
||||
3. Welche Personen dürfen reviewen, mergen und Testing beziehungsweise
|
||||
Production freigeben?
|
||||
4. Welcher Secret-Store, welche Schlüsselrotation und welche getrennten
|
||||
Testing-/Production-Rechte werden verwendet?
|
||||
5. Welcher unveränderliche Commit und welcher manuelle Freigabeschritt lösen
|
||||
Production aus?
|
||||
6. Welcher hostseitige Lock-, Timeout- und Recovery-Mechanismus stellt
|
||||
Production nach einem Abbruch wieder her?
|
||||
|
||||
Diese Fragen werden vor Einführung eines privilegierten Runners entschieden,
|
||||
nicht als Voraussetzung für den internen Testing-Vertical-Slice.
|
||||
|
||||
---
|
||||
|
||||
## 13. Referenzstand der Planung
|
||||
|
||||
Für die weitere Ausarbeitung wurden am 19.09.2026 insbesondere folgende
|
||||
Herstellerdokumentationen herangezogen:
|
||||
|
||||
- [Gitea Actions – Überblick](https://docs.gitea.com/usage/actions/)
|
||||
- [Gitea Runner](https://docs.gitea.com/runner/)
|
||||
- [Gitea Actions – Secrets](https://docs.gitea.com/usage/actions/secrets/)
|
||||
- [Gitea Actions – Job-Token-Berechtigungen](https://docs.gitea.com/usage/actions/token-permissions/)
|
||||
- [Gitea – geschützte Tags](https://docs.gitea.com/usage/protected-tags/)
|
||||
- [Ansible Playbooks](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_intro.html)
|
||||
- [Ansible Check- und Diff-Modus](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_checkmode.html)
|
||||
- [Ansible Vault](https://docs.ansible.com/projects/ansible/latest/vault_guide/vault.html)
|
||||
- [Debian 13 – Paket `podman`](https://packages.debian.org/trixie/podman)
|
||||
- [Podman – Quadlet und systemd](https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html)
|
||||
- [llama.cpp – Multi-GPU](https://github.com/ggml-org/llama.cpp/blob/master/docs/multi-gpu.md)
|
||||
- [llama.cpp – ROCm-Container](https://github.com/ggml-org/llama.cpp/blob/master/.devops/rocm.Dockerfile)
|
||||
|
||||
Vor der Implementierung sind versionsspezifische Aussagen gegen die dann
|
||||
tatsächlich eingesetzten Gitea-, Runner- und Ansible-Versionen zu prüfen.
|
||||
|
||||
---
|
||||
|
||||
## 14. Belegter Ansible-Implementierungsstand
|
||||
|
||||
Am 19.09.2026 wurde `ansible-core` 2.21.4 in einer lokalen, von Git
|
||||
ausgeschlossenen virtuellen Umgebung installiert. Das neue Control-Projekt
|
||||
enthält:
|
||||
|
||||
- `ansible.cfg` mit festem Testing-Inventar, Host-Key-Prüfung und lokalem
|
||||
Ansible-Arbeitsverzeichnis;
|
||||
- ein Testing-Inventar für `ghostnet-ki` unter `10.0.0.90` mit dem
|
||||
unprivilegierten Konto `ghost-agent`;
|
||||
- ein Preflight-Playbook ohne `become` und ohne konfigurationsändernde Tasks;
|
||||
- eine kurze Anleitung für Control-Node-Setup, SSH-Agent, Syntaxprüfung und
|
||||
Live-Lauf.
|
||||
|
||||
Inventardarstellung und Syntaxprüfung waren erfolgreich. Der Live-Lauf
|
||||
bestätigte:
|
||||
|
||||
| Merkmal | Ergebnis |
|
||||
| --- | --- |
|
||||
| Hostname | `ghostnet-ki` |
|
||||
| Betriebssystem | Debian 13.6 |
|
||||
| Standard-IPv4 | `10.0.0.90` |
|
||||
| CPU | 14 vCPUs |
|
||||
| RAM | 64.123 MiB |
|
||||
| Remote-Python | 3.13.5 |
|
||||
| GPUs | zwei Navi-21-PCI-Geräte `1002:73bf` |
|
||||
| Play Recap | `ok=5 changed=0 unreachable=0 failed=0` |
|
||||
|
||||
Dieser Lauf ist ein Zugriffs- und Identitätsnachweis. Er bestätigt weder ROCm
|
||||
noch Inferenzfunktion, privilegierte Deploymentrechte, Containerbetrieb oder
|
||||
die geplante Umschaltung zwischen Production und Testing.
|
||||
|
||||
Für den folgenden Bootstrap wurden ein eigener Ed25519-Schlüssel, ein
|
||||
Account-/sudoers-Playbook und ein getrenntes Prüfplaybook erstellt. Der
|
||||
öffentliche Schlüssel ist mit Fingerprint
|
||||
`SHA256:IKEInbIo8VVI1USg5+a3ypGK8H8FweJiQu+v7KvhQDU` versioniert; der private
|
||||
Schlüssel liegt mit Modus `0600` außerhalb des Repositories. Syntaxprüfung und
|
||||
Bootstrap-Inventar waren erfolgreich.
|
||||
|
||||
Der Bootstrap wurde anschließend über das persönliche Administratorkonto
|
||||
ausgeführt. Benutzer, Home-/SSH-Verzeichnisse, eingeschränkter Public Key und
|
||||
die mit `visudo` geprüfte sudoers-Datei wurden erfolgreich angelegt. Die letzte
|
||||
Assertion des ersten Laufs schlug ausschließlich fehl, weil die nicht vollständig
|
||||
quotierte Bedingung mit `NOPASSWD: ALL` als YAML-Mapping statt als String
|
||||
interpretiert wurde. Die Bedingung wurde korrigiert. Das getrennte Prüfplaybook
|
||||
bestätigte danach die Anmeldung als `ansible-deploy`, normale Modulausführung
|
||||
und nicht-interaktive Eskalation auf UID 0 mit
|
||||
`ok=5 changed=0 unreachable=0 failed=0`. Ein vollständiger zweiter
|
||||
Bootstrap-Lauf zur praktischen Idempotenzprüfung wurde noch nicht ausgeführt.
|
||||
|
||||
Als erste gemeinsame Hostrolle wurde anschließend `host_baseline` mit eigenem
|
||||
Host-Inventar und `playbooks/host.yml` implementiert. Vor Änderungen prüft sie
|
||||
Hostname, Debian-Hauptversion, Standard-IPv4, die Erkennung des
|
||||
Root-Dateisystems und mindestens 100 GiB freien Speicher. Sie verwaltet derzeit
|
||||
ausschließlich folgende root-eigenen Verzeichniswurzeln:
|
||||
|
||||
```text
|
||||
/etc/ghostnet-ai/production
|
||||
/etc/ghostnet-ai/testing
|
||||
/var/lib/ghostnet-ai/production
|
||||
/var/lib/ghostnet-ai/testing
|
||||
/var/lib/ghostnet-ai/shared
|
||||
```
|
||||
|
||||
Inventar- und Syntaxprüfung waren erfolgreich. Der Check-Modus zeigte nur die
|
||||
sieben erwarteten Verzeichnisse einschließlich ihrer Eltern als Änderungen und
|
||||
meldete 1.677,7 GiB freien Platz auf `/`. Der erste reale Lauf endete mit
|
||||
`ok=6 changed=1 unreachable=0 failed=0`. Der unmittelbar folgende identische
|
||||
Lauf endete mit `ok=6 changed=0 unreachable=0 failed=0` und belegt die
|
||||
Idempotenz des aktuellen Rollenumfangs. Es wurden keine Pakete installiert und
|
||||
keine Dienste, Container oder GPU-Komponenten verändert.
|
||||
|
||||
Am 20.09.2026 wurde `podman_runtime` in das gemeinsame Host-Playbook
|
||||
aufgenommen. Die Rolle installiert `podman`, `aardvark-dns`, `passt`,
|
||||
`slirp4netns` und `uidmap` ohne empfohlene Build- und Checkpoint-Pakete. Sie
|
||||
verlangt Debian 13, systemd und cgroup v2, verwaltet
|
||||
`/etc/containers/systemd`, prüft mindestens Podman-Hauptversion 5 sowie den
|
||||
Overlay-Storage und führt den Quadlet-Generator ohne Workload im Dry-Run aus.
|
||||
Der API-Socket und der automatische Image-Update-Timer bleiben explizit
|
||||
deaktiviert und gestoppt.
|
||||
|
||||
Der Check-Modus sagte 38 neue Pakete und das Quadlet-Verzeichnis voraus. Im
|
||||
ersten realen Lauf wurden die Pakete installiert; die anschließende
|
||||
Generatorprüfung schlug fehl, weil der Generator in Debian als Symlink
|
||||
bereitgestellt wird und die `stat`-Prüfung das Ziel zunächst nicht auflöste.
|
||||
Nach der Korrektur auf `follow: true` bestanden sämtliche Laufzeitprüfungen.
|
||||
Der finale Gesamtlauf endete mit
|
||||
`ok=20 changed=0 unreachable=0 failed=0`. Verifiziert wurden Podman 5.4.2,
|
||||
cgroup v2, der Storage-Treiber `overlay` und der ausführbare Quadlet-Generator.
|
||||
Eine getrennte Prüfung bestätigte außerdem leere Image- und Containerlisten
|
||||
sowie `podman.socket` und `podman-auto-update.timer` jeweils als `disabled` und
|
||||
`inactive`.
|
||||
|
||||
Die Rolle `workload_accounts` wurde anschließend in `host.yml` aufgenommen. Sie
|
||||
verwaltet feste IDs, gesperrte Passwörter, `/usr/sbin/nologin`, getrennte
|
||||
SubUID-/SubGID-Bereiche, Daten- und Konfigurationsrechte, rootverwaltete
|
||||
Quadlet-Verzeichnisse und Linger für `gnai-production` (UID/GID 980),
|
||||
`gnai-testing` (981) und `gn-encoding` (982). Die Datenwurzeln gehören dem
|
||||
jeweiligen Konto; Konfiguration gehört root und ist für die jeweilige Gruppe
|
||||
lesbar.
|
||||
|
||||
Beim ersten realen Lauf scheiterte nur die Abschlussprüfung, weil die
|
||||
sudoers-Regel von `ansible-deploy` den direkten Ansible-Wechsel zu den
|
||||
Servicekonten nicht erlaubt. Die Prüfung wurde auf einen rootseitigen Aufruf
|
||||
über `runuser` umgestellt, ohne die sudoers-Regel zu erweitern. Ein weiterer
|
||||
Lauf deckte eine konkurrierende Eigentümerverwaltung durch `host_baseline` auf;
|
||||
die umgebungsspezifischen Pfade wurden daraufhin vollständig an
|
||||
`workload_accounts` übertragen. Der korrigierte Gesamtlauf endete mit
|
||||
`ok=36 changed=0 unreachable=0 failed=0` und bestätigte drei aktive
|
||||
systemd-User-Manager sowie drei getrennte rootless Podman Graph Roots. Es
|
||||
wurden keine Images oder Workload-Container angelegt.
|
||||
|
||||
Anschließend wurde `amd_gpu_access` in das gemeinsame Host-Playbook
|
||||
aufgenommen. Die Rolle verlangt genau zwei Geräte `1002:73bf` mit `amdgpu`,
|
||||
ordnet über sysfs ausschließlich deren Render-Nodes zu, prüft `/dev/kfd` und
|
||||
leitet die minimal erforderlichen Gerätegruppen aus dem Istzustand ab. Auf dem
|
||||
Host sind dies `/dev/kfd`, `/dev/dri/renderD128` und
|
||||
`/dev/dri/renderD129`, jeweils mit Gruppe `render`.
|
||||
|
||||
Der Check-Modus prognostizierte nur die ergänzende Gruppenmitgliedschaft für
|
||||
`gnai-production` und `gnai-testing`. Der reale Lauf bestätigte danach für
|
||||
beide Konten Lese- und Schreibzugriff auf alle drei Geräte. Ein erster
|
||||
Wiederholungslauf deckte auf, dass `workload_accounts` die Gruppe zunächst
|
||||
entfernte und `amd_gpu_access` sie wieder hinzufügte. `render` wurde deshalb in
|
||||
den expliziten Sollzustand der beiden KI-Konten übernommen; das Encoding-Konto
|
||||
bleibt ohne GPU-Gruppe. Der korrigierte Gesamtlauf endete mit
|
||||
`ok=55 changed=0 unreachable=0 failed=0`. Weder ein ROCm-Image noch ein
|
||||
Container oder eine Inferenz wurden dabei ausgeführt.
|
||||
|
||||
Danach wurde `verify-rocm-container.yml` mit der eigenständigen Rolle
|
||||
`rocm_container_probe` ergänzt. Das Playbook wiederholt zunächst den
|
||||
Host-Gerätenachweis und arbeitet anschließend ausschließlich im rootless
|
||||
Podman-Store von `gnai-testing`. Es verwendet das offizielle
|
||||
`llama.cpp`-ROCm-Image als unveränderliches amd64-Manifest
|
||||
`sha256:b429b8be10d3d0353a09c98f4ab840fe516f779858a28f9fd4e5bdf10bc63cb4`
|
||||
und prüft dessen OCI-Labels auf Build `b11058` und Revision
|
||||
`f072b103714dfa1eee531f80b24512faf38e3dd2`.
|
||||
|
||||
Die ephemeren Probecontainer erhielten nur `/dev/kfd`, die beiden zuvor
|
||||
ermittelten Render-Nodes und `keep-groups`; ein
|
||||
`HSA_OVERRIDE_GFX_VERSION` wurde nicht gesetzt. `rocminfo` meldete genau zwei
|
||||
GPU-Agents mit `gfx1030`. `llama-server --list-devices` meldete getrennt
|
||||
`ROCm0` und `ROCm1`, beide als AMD Radeon RX 6900 XT mit jeweils rund 16 GiB
|
||||
VRAM. Der Abschlusslauf endete mit
|
||||
`ok=33 changed=0 unreachable=0 failed=0`. Die Probecontainer wurden durch
|
||||
`--rm` entfernt; das gepinnte Image bleibt für den folgenden Testing-Aufbau im
|
||||
Store. Modellladen, Layer-Verteilung und Inferenz wurden noch nicht geprüft.
|
||||
|
||||
---
|
||||
|
||||
## 15. Abschlussgrenze dieses Entwurfs
|
||||
|
||||
Mit Version 0.15 sind der aktuelle Planungsstand, der ausdrücklich begrenzte
|
||||
Ansible-Preflight, der verifizierte Deployment-Account und die angewendete
|
||||
minimale Host-Baseline, die Podman-/Quadlet-Grundruntime und die getrennten
|
||||
Workload-Konten, deren begrenzter AMD-Gerätezugriff sowie der rootless
|
||||
ROCm-/`llama.cpp`-Gerätenachweis für Testing persistiert.
|
||||
Insbesondere wurden noch nicht:
|
||||
|
||||
- ein Gitea Runner angelegt,
|
||||
- Gitea Actions oder Schutzregeln konfiguriert,
|
||||
- eine dauerhafte `llama.cpp`-Anwendungsrolle, ein Production-Inventar oder ein
|
||||
Vault erstellt,
|
||||
- eine Production- oder Testing-Umgebung eingerichtet oder voneinander
|
||||
abgenommen,
|
||||
- ein dauerhafter GPU-Container, ein Modell oder eine Anwendung auf dem
|
||||
KI-Server eingerichtet,
|
||||
- Backups oder Rollbacks praktisch getestet oder
|
||||
- die geplante Deploymentkette technisch abgenommen.
|
||||
|
||||
Der nächste KI-spezifische Schritt ist die `llama.cpp`-Rolle für Testing. Die
|
||||
bereichsübergreifende Priorität und die Abhängigkeit von den offenen
|
||||
Sicherheitsbefunden legt GNAI-001 fest. LiteLLM ist nicht länger automatisch
|
||||
der Folgeschritt, sondern wird vor einer Veröffentlichung anhand der benötigten
|
||||
API-Policies entschieden.
|
||||
@@ -0,0 +1,345 @@
|
||||
# Arbeitsentwurf
|
||||
|
||||
**Dokumenten-ID:** GNAI-009-PLAN
|
||||
**Titel:** Modell- und Laufzeitbewertung für `llama.cpp`
|
||||
**Stand:** 2026-09-21
|
||||
**Status:** Nicht finaler Evaluierungsplan; keine Production-Freigabe
|
||||
**Referenzen:** [Ansible-Deployment](GNAI-008-PLAN_Ansible-Deployment.md), [technische Sollkonfiguration](GNAI-010-TECH_Llama-Serving.md), [Zielarchitektur](GNAI-004-INFRA_Architecture.md)
|
||||
|
||||
> Dieses Dokument persistiert die Modellrecherche und legt fest, wie daraus
|
||||
> eine belastbare Entscheidung für den KI-Server entsteht. Veröffentlichte
|
||||
> Benchmarks, Community-Berichte und lokale Abnahmetests werden getrennt
|
||||
> bewertet. Ein hier genannter Kandidat ist erst nach erfolgreicher lokaler
|
||||
> Abnahme für Production freigegeben.
|
||||
|
||||
---
|
||||
|
||||
## Änderungsverlauf
|
||||
|
||||
| Datum | Version | Autor | Beschreibung |
|
||||
| --- | --- | --- | --- |
|
||||
| 2026-09-21 | 0.4 | spektr / Codex | Metrikübergabe an die später gewählte API-Policy-Schicht statt vorab an LiteLLM gebunden |
|
||||
| 2026-09-21 | 0.3 | spektr / Codex | feste, gegenseitig ausgeschlossene Quadlet-Unit je Modell als Umschaltverfahren festgelegt |
|
||||
| 2026-09-21 | 0.2 | spektr / Codex | Unsloth Qwen3.8 UD-Q6_K als verbindlichen zweiten 128k-Kandidaten festgelegt und kontrollierten A/B-Test ergänzt |
|
||||
| 2026-09-21 | 0.1 | spektr / Codex | Swift-Qwen3.8, Q6_K, Reasoning, Kontext, MTP und lokales Vergleichsverfahren als ersten Evaluierungsstand aufgenommen |
|
||||
|
||||
---
|
||||
|
||||
## 1. Ziel
|
||||
|
||||
Gesucht wird ein Modellprofil für Coding-, Tool- und längere agentische
|
||||
Aufgaben auf zwei Radeon RX 6900 XT mit zusammen 32 GiB VRAM. Das Profil soll:
|
||||
|
||||
- komplexe Aufgaben mit ausreichend langem Reasoning bearbeiten können;
|
||||
- unnötige Reasoning-Schleifen reduzieren;
|
||||
- Tool Calls und strukturierte Ausgaben zuverlässig erzeugen;
|
||||
- mindestens 128 KiToken Kontext als Zielkonfiguration unterstützen;
|
||||
- vollständig über beide GPUs mit ROCm betrieben werden;
|
||||
- durch unveränderliche Modell- und Containerartefakte reproduzierbar sein;
|
||||
- Swift und Unsloth gleichzeitig im Modellbestand vorhalten, aber für Tests
|
||||
jeweils nur ein Modell in den VRAM laden;
|
||||
- ohne Modellwechsel zwischen Testing und Production übertragbar sein; und
|
||||
- später gegen neue Swift-, Base- oder Dynamic-Quantisierungen austauschbar
|
||||
bleiben.
|
||||
|
||||
Die Entscheidung wird anhand realer Aufgaben getroffen. Herstellerbenchmarks
|
||||
dienen als Kandidatenauswahl, ersetzen aber nicht die lokale Abnahme.
|
||||
|
||||
## 2. Bewerteter Quellenstand
|
||||
|
||||
### 2.1 Qwen3.8-27B
|
||||
|
||||
Die [Qwen-Modellkarte](https://huggingface.co/Qwen/Qwen3.8-27B) nennt für das
|
||||
27B-Modell 64 Blöcke, davon 16 Full-Attention-Blöcke, einen nativen Kontext von
|
||||
262.144 Token und ein trainiertes Multi-Token-Prediction-Head. Thinking ist
|
||||
standardmäßig aktiv. Qwen empfiehlt für Thinking `temperature=1.0`,
|
||||
`top_p=0.95`, `top_k=20`, `min_p=0.0`, `presence_penalty=0.0` und
|
||||
`repetition_penalty=1.0`.
|
||||
|
||||
Qwen unterstützt `xhigh`, `medium` und `low` als Reasoning-Stufen. Die
|
||||
Modellkarte warnt ausdrücklich, dass ein niedrigeres Reasoning-Niveau bei
|
||||
mehrstufigen Agentenaufgaben zu Fehlversuchen und Wiederholungen führen kann
|
||||
und dadurch nicht zwingend Zeit oder Token spart. Ein harter kurzer
|
||||
Reasoning-Cap wird deshalb nicht als Standardprofil vorgesehen.
|
||||
|
||||
### 2.2 Swift-Qwen3.8-27B
|
||||
|
||||
Der Herausgeber beschreibt
|
||||
[Swift-Qwen3.8-27B](https://huggingface.co/ukisai/Swift-Qwen3.8-27b) als
|
||||
reasoning-effiziente Ableitung des Qwen-Basismodells. Nach eigener Darstellung
|
||||
wurden mit Overthinking korrelierte Reasoning-Muster im Fine-Tuning
|
||||
benachteiligt und verlorene Qualität durch weitere Trainingsschritte
|
||||
kompensiert.
|
||||
|
||||
Die veröffentlichten BF16-Vergleiche wurden mit vLLM, 262.144 Token Kontext,
|
||||
`xhigh`, fünf Seeds und den Qwen-Samplingwerten durchgeführt. Für GPQA Diamond
|
||||
werden nahezu gleiche Scores bei 41 Prozent weniger mittleren beziehungsweise
|
||||
58,3 Prozent weniger medianen Thinking-Tokens ausgewiesen. Terminal-Bench 2.1
|
||||
liegt geringfügig unter Base, LiveCodeBench v6 darüber. AIME 2026 und HMMT
|
||||
zeigen hingegen einen messbaren Rückgang.
|
||||
|
||||
Diese Ergebnisse stammen vom Modellhersteller. Sie sind ein guter Grund für
|
||||
einen lokalen Test, aber noch kein unabhängiger Nachweis für unsere Aufgaben,
|
||||
für GGUF Q6_K, für llama.cpp oder für ROCm auf Navi 21. Insbesondere darf der
|
||||
LiveCodeBench-Unterschied nicht ohne Kontrolle der Output-Limits als allgemeine
|
||||
Qualitätssteigerung interpretiert werden.
|
||||
|
||||
### 2.3 GGUF-Quantisierungen
|
||||
|
||||
Das vom Swift-Herausgeber gepflegte
|
||||
[GGUF-Repository](https://huggingface.co/ukisai/Swift-Qwen3.8-27B-GGUF)
|
||||
enthält unter anderem folgende Ein-Datei-Quantisierungen:
|
||||
|
||||
| Quantisierung | veröffentlichte Größe | vorgesehene Rolle |
|
||||
| --- | ---: | --- |
|
||||
| Q4_K_M | 18,0 GB | speichersparender Vergleich und möglicher Fast-/Vision-Fallback |
|
||||
| Q5_K_M | 20,2 GB | möglicher Kompromiss für MTP oder zusätzliche Reserve |
|
||||
| Q6_K | 22,9 GB | bevorzugter Qualitätskandidat für Text-, Coding- und Tool-Aufgaben |
|
||||
| Q6_K_L | 25,2 GB | derzeit zu geringe Reserve für 128k und Laufzeitpuffer |
|
||||
| Q8_0 | 29,1 GB | für 32 GiB Gesamt-VRAM mit langem Kontext ungeeignet |
|
||||
|
||||
Der Herausgeber empfiehlt Q6_K oder höher für lange Agentenläufe und strikte
|
||||
Tool-Formate. Die GGUFs wurden laut Modellkarte mit llama.cpp `b10896` erzeugt;
|
||||
recurrent-sensitive Tensoren und das MTP-Head verwenden gezielt höhere
|
||||
Präzision. Die veröffentlichten quantisierten Aufgabenbenchmarks betreffen
|
||||
jedoch W4A16/AWQ und nicht direkt die Q6_K-GGUF. Die Eignung von Q6_K bleibt
|
||||
daher Gegenstand unseres Tests.
|
||||
|
||||
### 2.4 Unsloth Qwen3.8-27B UD-Q6_K
|
||||
|
||||
Das zweite Testartefakt stammt aus dem
|
||||
[Unsloth-GGUF-Repository](https://huggingface.co/unsloth/Qwen3.8-27B-GGUF).
|
||||
Ausgewählt ist `Qwen3.8-27B-UD-Q6_K.gguf` mit einer veröffentlichten Größe von
|
||||
22 GB. Das Repository steht laut Modellkarte unter Apache-2.0. Unsloth hat die
|
||||
Quantisierung mit einer Importance Matrix und dem eigenen Dynamic-Verfahren
|
||||
erzeugt.
|
||||
|
||||
Damit unterscheidet sich der Kandidat nicht nur durch das ursprüngliche
|
||||
Qwen-Verhalten von Swift, sondern auch durch das Quantisierungslayout. Der
|
||||
Vergleich misst deshalb die beiden konkret einsetzbaren Artefakte als Ganzes;
|
||||
er isoliert nicht wissenschaftlich den Effekt des Swift-Fine-Tunings. Diese
|
||||
Einschränkung wird bei der Auswertung ausdrücklich festgehalten.
|
||||
|
||||
### 2.5 MTP
|
||||
|
||||
Das MTP-Head ist in den Swift-GGUFs enthalten. Die Modellkarte nennt für
|
||||
llama.cpp `--spec-type draft-mtp --spec-draft-n-max 3`. Die aktuelle
|
||||
llama.cpp-Dokumentation führt MTP als unterstützte spekulative Methode.
|
||||
|
||||
MTP wird trotzdem nicht in das erste Abnahmeprofil aufgenommen. Es benötigt
|
||||
zusätzlichen Draft-Kontext und zusätzliche Laufzeitpuffer. Der verlinkte
|
||||
Community-Nachweis zeigt, dass MTP deutliche Gewinne liefern kann, verwendet
|
||||
aber Strix Halo, Vulkan, ein anderes Qwen-Modell und zeitweise einen Fork. Er
|
||||
ist kein Nachweis für zwei RX 6900 XT mit dem gepinnten ROCm-Image.
|
||||
|
||||
MTP wird erst nach einer stabilen Baseline in derselben Sitzung und mit
|
||||
identischen Aufgaben ein- und ausgeschaltet. Begonnen wird mit
|
||||
`--spec-draft-n-max 2`; `3` bleibt eine zweite Messstufe.
|
||||
|
||||
### 2.6 Swift 1.5 und Community-Quants
|
||||
|
||||
Ankündigungen zu Swift 1.5 und kombinierten Swift-/Unsloth-Dynamic-Quants
|
||||
werden als Beobachtung festgehalten. Am 21.09.2026 wurde daraus noch kein
|
||||
freigegebenes Projektartefakt ausgewählt. Weder die Ansible-Rolle noch das
|
||||
Production-Profil warten auf eine angekündigte Version.
|
||||
|
||||
Ein neuer Checkpoint wird als neues, unveränderliches Profil mit eigener
|
||||
Quelle, Revision, Prüfsumme und Lizenzprüfung aufgenommen. Er ersetzt niemals
|
||||
automatisch ein bereits abgenommenes Profil.
|
||||
|
||||
## 3. Vorläufige Kandidaten
|
||||
|
||||
### 3.1 Bevorzugter Kandidat
|
||||
|
||||
**Swift-Qwen3.8-27B Q6_K mit `xhigh`** wird als erster Testing-Kandidat
|
||||
festgelegt. Gründe sind die höhere Gewichtsqualität gegenüber Q4/Q5, die
|
||||
Herstellerempfehlung für lange Agentenläufe und die Zielsetzung von Swift,
|
||||
Reasoning-Effizienz ohne kurzen harten Token-Cap zu erreichen.
|
||||
|
||||
Das erste Profil verwendet:
|
||||
|
||||
| Parameter | Wert |
|
||||
| --- | --- |
|
||||
| Quantisierung | Q6_K |
|
||||
| Kontextziel | 131.072 Token |
|
||||
| K-/V-KV-Cache | `q8_0` / `q8_0` |
|
||||
| Reasoning | aktiv, `xhigh`, ohne Reasoning-Budget |
|
||||
| parallele Slots | 1 |
|
||||
| Flash Attention | aktiv |
|
||||
| GPU-Offload | alle Layer |
|
||||
| GPU-Aufteilung | `layer`, `1,1` |
|
||||
| MTP | zunächst deaktiviert |
|
||||
| Vision-Projektor | zunächst nicht geladen |
|
||||
| maximale Ausgabe | zunächst 32.768 Token; gesonderter 65.536-Token-Test |
|
||||
|
||||
128 KiToken sind das Zielprofil. Die Inbetriebnahme prüft dasselbe Modell
|
||||
zuerst mit 64 KiToken, damit Fehler bei Modellformat, ROCm oder GPU-Aufteilung
|
||||
von reinem Speicherdruck unterschieden werden können.
|
||||
|
||||
### 3.2 Verbindlicher Vergleichskandidat
|
||||
|
||||
**Unsloth Qwen3.8-27B UD-Q6_K** wird ebenfalls installiert und mit 131.072
|
||||
Token Kontext abgenommen. Das Artefakt ist verbindlich festgelegt:
|
||||
|
||||
| Feld | Wert |
|
||||
| --- | --- |
|
||||
| Repository | `unsloth/Qwen3.8-27B-GGUF` |
|
||||
| Revision | `4ca720788d1e01f1bff70c033e0d0028fd02e502` |
|
||||
| Datei | `Qwen3.8-27B-UD-Q6_K.gguf` |
|
||||
| veröffentlichte Größe | 22 GB |
|
||||
| SHA-256 | `c9c206812fbe4ac7b76a729e25928b63f2ae89d37f69da7a71c20aec763cd436` |
|
||||
| API-Alias | `unsloth-qwen3.8-27b-ud-q6k` |
|
||||
|
||||
Beide Dateien liegen gleichzeitig auf dem Host und jedes Modell besitzt eine
|
||||
eigene feste Quadlet-Unit. Für einen Messlauf ist immer nur die ausgewählte
|
||||
Unit aktiv; die Modelle werden nicht gleichzeitig in den VRAM geladen. Der
|
||||
Wechsel stoppt den bisherigen Container vollständig, bevor der andere startet.
|
||||
|
||||
Der erste kontrollierte A/B-Vergleich verwendet bei beiden Modellen `xhigh`,
|
||||
131.072 Token Kontext, Q8-K/V, einen Slot, dieselben Samplingwerte und kein
|
||||
MTP. Danach folgt der praxisnahe Profilvergleich Swift `xhigh` gegen Unsloth
|
||||
`medium`. Dadurch bleiben die Effekte der Reasoning-Einstellung sichtbar,
|
||||
statt sie unbemerkt dem Modellartefakt zuzurechnen.
|
||||
|
||||
### 3.3 Ressourcen-Fallback
|
||||
|
||||
**Swift Q5_K_M mit `xhigh`** ist der erste Fallback, falls Q6_K mit 128k,
|
||||
Laufzeitpuffern oder später MTP nicht stabil in 32 GiB VRAM betrieben werden
|
||||
kann. Ein Wechsel auf Q5 erfolgt nur aufgrund gemessener Speicher- oder
|
||||
Leistungsergebnisse, nicht vorsorglich.
|
||||
|
||||
## 4. Testmatrix
|
||||
|
||||
| Stufe | Modellprofil | Kontext | MTP | Zweck |
|
||||
| --- | --- | ---: | --- | --- |
|
||||
| A0 | Swift Q6_K, text-only | 65.536 | aus | Modellformat, vollständiger GPU-Offload, API und Grundstabilität |
|
||||
| A1 | Swift Q6_K, text-only | 131.072 | aus | bevorzugtes Zielprofil und VRAM-Abnahme |
|
||||
| A2 | Swift Q6_K mit mmproj | 131.072 | aus | optionaler Vision-Betrieb und zusätzlicher VRAM-Bedarf |
|
||||
| A3 | Swift Q6_K, text-only | 131.072 | `n-max=2` | MTP-A/B gegen A1 |
|
||||
| A4 | Swift Q6_K, text-only | 131.072 | `n-max=3` | nur nach erfolgreichem A3-Test |
|
||||
| B1 | Swift Q5_K_M, text-only | 131.072 | aus / 2 | Ressourcen- und Geschwindigkeitsvergleich |
|
||||
| U0 | Unsloth UD-Q6_K, text-only, `xhigh` | 65.536 | aus | Modellformat, GPU-Offload, API und Grundstabilität |
|
||||
| U1 | Unsloth UD-Q6_K, text-only, `xhigh` | 131.072 | aus | kontrolliertes Gegenstück zu A1 |
|
||||
| AB1 | Swift Q6_K gegen Unsloth UD-Q6_K, beide `xhigh` | 131.072 | aus | A/B mit identischen Laufzeitparametern |
|
||||
| AB2 | Swift Q6_K `xhigh` gegen Unsloth UD-Q6_K `medium` | 131.072 | aus | Vergleich der vorgesehenen Praxisprofile |
|
||||
|
||||
Vision und MTP sind eigenständige Varianten. Sie werden nicht gleichzeitig
|
||||
erstmals aktiviert, weil andernfalls Speicher- und Performanceeffekte nicht
|
||||
zugeordnet werden können.
|
||||
|
||||
## 5. Aufgaben und Messwerte
|
||||
|
||||
Die Vergleichssuite enthält 10 bis 20 repräsentative Aufgaben aus dem späteren
|
||||
Betrieb:
|
||||
|
||||
1. kleine, eindeutig prüfbare Codeänderung;
|
||||
2. repositoryweite Änderung mit mehreren Dateien;
|
||||
3. Fehlersuche anhand von Logs und Tests;
|
||||
4. Tool-Aufruf mit korrektem JSON-Schema;
|
||||
5. mehrstufige Aufgabe mit fehlgeschlagenem ersten Ansatz;
|
||||
6. lange Kontextaufgabe mit relevanten Informationen an verschiedenen Stellen;
|
||||
7. Refactoring mit unverändertem Verhalten;
|
||||
8. Dokumentations- und Konfigurationsänderung;
|
||||
9. mathematisch-logische Kontrollaufgabe; und
|
||||
10. Negativtest, bei dem das Modell fehlende Informationen erkennen soll.
|
||||
|
||||
Pro Aufgabe und Profil werden soweit praktikabel mindestens drei Läufe
|
||||
durchgeführt. Für Leistungsmessungen werden zusätzlich deterministische
|
||||
Prompts mit festem Seed verwendet. Reale Agentenaufgaben behalten die von Qwen
|
||||
empfohlenen Samplingwerte.
|
||||
|
||||
Für AB1 wird die Reihenfolge pro Aufgabe zwischen Swift und Unsloth gewechselt.
|
||||
Nach jedem Profilwechsel wird ein definierter Warm-up-Request ausgeführt; seine
|
||||
Zeit fließt nicht in den fachlichen Vergleich ein. Modellladezeit und Warm-up
|
||||
werden separat erfasst. Container-Digest, Prompt, Systemnachricht, Toolschema,
|
||||
Seed, Kontext, Sampling und Ausgabegrenze bleiben innerhalb einer Serie
|
||||
identisch. Die automatischen Prüfungen und die manuelle Bewertungsrubrik werden
|
||||
vor dem ersten gewerteten Lauf festgelegt.
|
||||
|
||||
Jede abgeschlossene Serie erhält im Repository einen datierten
|
||||
Evaluierungsdatensatz mit Profilmanifest, Aufgabenrevision, Rohmesswerten und
|
||||
Kurzbericht. Modell- oder Containerdateien sowie sensible Promptinhalte werden
|
||||
nicht in Git gespeichert. Der Bericht muss erkennen lassen, welche Läufe wegen
|
||||
technischer Fehler wiederholt oder aus der Wertung genommen wurden.
|
||||
|
||||
Zu erfassen sind:
|
||||
|
||||
- fachlicher Erfolg und bestandene Tests;
|
||||
- Zahl der Tool-Aufrufe, Fehlversuche und Wiederholungen;
|
||||
- Prompt-, Reasoning-, Antwort- und Gesamttoken;
|
||||
- Time to First Token, Prompt Processing und Generationstempo;
|
||||
- gesamte Laufzeit bis zum erfolgreichen Abschluss;
|
||||
- Spitzen- und stabiler VRAM-Verbrauch je GPU;
|
||||
- CPU- und RAM-Verbrauch;
|
||||
- GPU-Aufteilung und etwaige CPU-Offloads;
|
||||
- Abbruch, OOM, fehlerhafte Tool-Formate oder unvollständige Antworten.
|
||||
|
||||
## 6. Vorläufige Abnahmekriterien
|
||||
|
||||
Ein Kandidat wird zum bevorzugten Production-Profil, wenn:
|
||||
|
||||
- das 128k-Profil ohne OOM startet und einen einstündigen gemischten Test
|
||||
ohne Containerneustart oder GPU-Fehler besteht;
|
||||
- alle vorgesehenen Modell-Layer auf die GPUs ausgelagert werden;
|
||||
- beide GPUs genutzt werden und keine Karte ihren verfügbaren VRAM erschöpft;
|
||||
- API-Antwort, Reasoning-Trennung und Tool Calls dem erwarteten Schema
|
||||
entsprechen;
|
||||
- die Aufgabensuite höchstens einen Erfolg weniger als der
|
||||
jeweils andere Vergleichskandidat erzielt;
|
||||
- keine systematische Regression bei Mathematik- oder Tool-Aufgaben sichtbar
|
||||
wird; und
|
||||
- der Median der generierten Gesamttoken oder der Zeit bis zum erfolgreichen
|
||||
Abschluss gegenüber dem anderen Profil praktisch relevant sinkt.
|
||||
|
||||
Als Zielwert für die Effizienz gilt zunächst eine Reduktion von mindestens
|
||||
20 Prozent. Die vom Hersteller berichteten 30 bis 50 Prozent sind eine
|
||||
Hypothese, kein lokales Abnahmekriterium.
|
||||
|
||||
MTP wird nur übernommen, wenn es in einer A/B-Messung derselben Sitzung den
|
||||
Median der Generationsleistung oder End-to-End-Laufzeit um mindestens
|
||||
10 Prozent verbessert, ohne Stabilität, Tool-Format oder Abschlussquote zu
|
||||
verschlechtern. Einzelne schnelle Läufe reichen nicht als Nachweis.
|
||||
|
||||
## 7. Freigabe und Rollback
|
||||
|
||||
Jedes Modellprofil erhält einen stabilen Namen, eine unveränderliche Quelle,
|
||||
eine SHA-256-Prüfsumme und einen dokumentierten Lizenzstand. Testing und
|
||||
Production referenzieren dieselbe geprüfte Datei aus dem gemeinsamen
|
||||
schreibgeschützten Modellbestand.
|
||||
|
||||
Ein Upgrade erzeugt ein neues Profil. Das bisherige Modell bleibt bis zum
|
||||
abgeschlossenen Production-Test erhalten. Der Rollback ändert nur den
|
||||
versionierten Profilverweis und startet die betroffene Umgebung neu. Ein
|
||||
unbeaufsichtigter Download von `main`, `latest` oder einer beweglichen
|
||||
Quantisierungsbezeichnung ist ausgeschlossen.
|
||||
|
||||
Swift und Unsloth werden als getrennte Profile installiert. Das Ergebnis kann
|
||||
ein bevorzugtes Standardprofil und ein weiterhin verfügbares alternatives
|
||||
Profil sein; beide dürfen nur über den kontrollierten Umgebungswechsel
|
||||
aktiviert werden.
|
||||
|
||||
## 8. Offene Punkte
|
||||
|
||||
- Lizenz und tatsächlichen Verwendungsrahmen des Swift-Modells vor Production
|
||||
bestätigen;
|
||||
- maximal zulässige Antwortdauer und Ausgabegröße für externe Nutzer
|
||||
festlegen;
|
||||
- entscheiden, ob Vision Bestandteil des ersten Production-Profils wird;
|
||||
- Metrikerfassung für Reasoning- und Antworttoken mit der später gewählten
|
||||
API-Policy-Schicht abstimmen;
|
||||
- Aufgabenbestand und erwartete Ergebnisse der lokalen Suite versionieren; und
|
||||
- nach Veröffentlichung bewerten, ob Swift 1.5 oder eine geprüfte
|
||||
Swift-Dynamic-Quantisierung als neuer Kandidat aufgenommen wird.
|
||||
|
||||
## 9. Quellen
|
||||
|
||||
- [Qwen3.8-27B – Modellkarte](https://huggingface.co/Qwen/Qwen3.8-27B)
|
||||
- [Swift-Qwen3.8-27B – Modellkarte und Benchmarks](https://huggingface.co/ukisai/Swift-Qwen3.8-27b)
|
||||
- [Swift-Qwen3.8-27B-GGUF – Quantisierungen, KV-Angaben und MTP](https://huggingface.co/ukisai/Swift-Qwen3.8-27B-GGUF)
|
||||
- [Unsloth Qwen3.8-27B-GGUF – Modellkarte und Dateien](https://huggingface.co/unsloth/Qwen3.8-27B-GGUF)
|
||||
- [Unsloth UD-Q6_K – Datei und Prüfsumme](https://huggingface.co/unsloth/Qwen3.8-27B-GGUF/blob/4ca720788d1e01f1bff70c033e0d0028fd02e502/Qwen3.8-27B-UD-Q6_K.gguf)
|
||||
- [llama.cpp Server – aktuelle Parameterreferenz](https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md)
|
||||
- [llama.cpp MTP-/ROCm-Communitybericht](https://github.com/ggml-org/llama.cpp/discussions/27950)
|
||||
|
||||
Community-Berichte dienen ausschließlich der Hypothesenbildung. Freigaben
|
||||
beruhen auf reproduzierbaren Messungen des gepinnten eigenen Stacks.
|
||||
@@ -0,0 +1,563 @@
|
||||
# Technischer Arbeitsentwurf
|
||||
|
||||
**Dokumenten-ID:** GNAI-010-TECH
|
||||
**Titel:** `llama.cpp`-Serving mit ROCm und rootless Podman
|
||||
**Stand:** 2026-09-21
|
||||
**Status:** Technische Sollkonfiguration für Testing; noch nicht implementiert
|
||||
**Referenzen:** [Modellbewertung](GNAI-009-PLAN_Llama-Model-Evaluation.md), [Ansible-Deployment](GNAI-008-PLAN_Ansible-Deployment.md), [Zielarchitektur](GNAI-004-INFRA_Architecture.md), [Ansible-Anleitung](../../../../ansible/README.md)
|
||||
|
||||
> Dieses Dokument beschreibt den vorgesehenen technischen Sollzustand. Es ist
|
||||
> keine Betriebs-SOP und kein Nachweis, dass Modell, Quadlet oder Inferenz
|
||||
> bereits eingerichtet wurden. Abweichungen aus der Implementierung werden vor
|
||||
> einer Production-Freigabe hier nachgeführt.
|
||||
|
||||
---
|
||||
|
||||
## Änderungsverlauf
|
||||
|
||||
| Datum | Version | Autor | Beschreibung |
|
||||
| --- | --- | --- | --- |
|
||||
| 2026-09-21 | 0.4 | spektr / Codex | Serving von der noch zu entscheidenden externen API-Policy-Schicht entkoppelt |
|
||||
| 2026-09-21 | 0.3 | spektr / Codex | je Modell ein festes Quadlet vorgesehen; Swift und Unsloth durch kontrollierten Stop/Start gegenseitig ausgeschlossen; Router-Modus zurückgestellt |
|
||||
| 2026-09-21 | 0.2 | spektr / Codex | Unsloth UD-Q6_K als zweites installiertes Modellprofil und kontrollierten Profilwechsel für A/B-Tests ergänzt |
|
||||
| 2026-09-21 | 0.1 | spektr / Codex | erstes technisches Profil für Swift Q6_K, 128k, Q8-KV, rootless Quadlet und gestufte MTP-Abnahme erstellt |
|
||||
|
||||
---
|
||||
|
||||
## 1. Geltungsbereich
|
||||
|
||||
Die Spezifikation umfasst zwei feste `llama-server`-Quadlets in der
|
||||
Testing-Umgebung, von denen immer genau eines aktiv ist. Sie legt fest:
|
||||
|
||||
- welche Container- und Modellartefakte verwendet werden;
|
||||
- wie Modell und Konfiguration gespeichert werden;
|
||||
- welche Geräte und Netzwerke der Container erhält;
|
||||
- welche `llama-server`-Parameter als Ausgangsprofil gelten;
|
||||
- wie VRAM-Annahmen behandelt werden; und
|
||||
- welche technischen Prüfungen vor der Übernahme nach Production bestehen
|
||||
müssen.
|
||||
|
||||
API-Policy-Schicht, öffentliche Veröffentlichung, Nutzer-SOPs und die zeitliche
|
||||
Umschaltung zwischen Production und Testing erhalten eigene Rollen und
|
||||
Dokumente. Ob dafür LiteLLM eingesetzt wird, entscheidet GNAI-001 erst vor der
|
||||
Veröffentlichung. `llama-server` wird nicht direkt für LAN, VPN oder Internet
|
||||
veröffentlicht.
|
||||
|
||||
## 2. Bereits belegte Voraussetzungen
|
||||
|
||||
Am 20.09.2026 wurden auf `ghostnet-ki` folgende Grundlagen nachgewiesen:
|
||||
|
||||
- Debian 13.6 und Podman 5.4.2 mit cgroup v2 und `crun`;
|
||||
- rootless Konto `gnai-testing` mit UID/GID 981 und persistentem
|
||||
systemd-User-Manager;
|
||||
- `/dev/kfd`, `/dev/dri/renderD128` und `/dev/dri/renderD129`;
|
||||
- zwei RX 6900 XT als getrennte ROCm-Geräte und `gfx1030`-Agents; und
|
||||
- erfolgreicher Start des offiziellen `llama.cpp`-ROCm-Images ohne
|
||||
`HSA_OVERRIDE_GFX_VERSION`.
|
||||
|
||||
Modellladen, 128k-Kontext, Multi-GPU-Layer-Verteilung und Inferenz wurden noch
|
||||
nicht nachgewiesen.
|
||||
|
||||
## 3. Unveränderliche Artefakte
|
||||
|
||||
### 3.1 Container
|
||||
|
||||
| Feld | Wert |
|
||||
| --- | --- |
|
||||
| Upstream | `ghcr.io/ggml-org/llama.cpp:server-rocm` |
|
||||
| verwendete Referenz | `ghcr.io/ggml-org/llama.cpp@sha256:b429b8be10d3d0353a09c98f4ab840fe516f779858a28f9fd4e5bdf10bc63cb4` |
|
||||
| Architektur | amd64 |
|
||||
| Build | `b11058` |
|
||||
| Revision | `f072b103714dfa1eee531f80b24512faf38e3dd2` |
|
||||
| Status | ROCm-Geräteerkennung unter `gnai-testing` bestanden |
|
||||
|
||||
Der Dienst verwendet ausschließlich den Digest. Ein bewegliches Tag darf in
|
||||
Testing zur Recherche aufgelöst, aber nicht in ein freigegebenes Profil
|
||||
übernommen werden.
|
||||
|
||||
### 3.2 Swift-Modellprofil
|
||||
|
||||
| Feld | Wert |
|
||||
| --- | --- |
|
||||
| Repository | `ukisai/Swift-Qwen3.8-27B-GGUF` |
|
||||
| Repository-Revision | `eb0e3a7dc70643c2ab920f5133750d02c20849ff` |
|
||||
| Datei | `Swift-Qwen3.8-27B-Q6_K.gguf` |
|
||||
| veröffentlichte Größe | 22,9 GB |
|
||||
| SHA-256 | `7f4de8abd5446c08b0f975a1b38e43d02639f4ae9ecdd2ddfd5c5b0f612bda59` |
|
||||
| Download | `https://huggingface.co/ukisai/Swift-Qwen3.8-27B-GGUF/resolve/eb0e3a7dc70643c2ab920f5133750d02c20849ff/Swift-Qwen3.8-27B-Q6_K.gguf` |
|
||||
| API-Alias | `swift-qwen3.8-27b-q6k` |
|
||||
|
||||
Die Prüfsumme stammt aus der vom Herausgeber veröffentlichten
|
||||
[`SHA256SUMS`](https://huggingface.co/ukisai/Swift-Qwen3.8-27B-GGUF/blob/main/SHA256SUMS)-Datei.
|
||||
Ansible muss die SHA-256-Prüfsumme während des Downloads prüfen. Eine bereits
|
||||
vorhandene Datei mit abweichender Prüfsumme führt zum Abbruch; sie wird nicht
|
||||
stillschweigend überschrieben oder verwendet.
|
||||
|
||||
### 3.3 Unsloth-Vergleichsprofil
|
||||
|
||||
| Feld | Wert |
|
||||
| --- | --- |
|
||||
| Repository | `unsloth/Qwen3.8-27B-GGUF` |
|
||||
| Repository-Revision | `4ca720788d1e01f1bff70c033e0d0028fd02e502` |
|
||||
| Datei | `Qwen3.8-27B-UD-Q6_K.gguf` |
|
||||
| veröffentlichte Größe | 22 GB |
|
||||
| SHA-256 | `c9c206812fbe4ac7b76a729e25928b63f2ae89d37f69da7a71c20aec763cd436` |
|
||||
| Download | `https://huggingface.co/unsloth/Qwen3.8-27B-GGUF/resolve/4ca720788d1e01f1bff70c033e0d0028fd02e502/Qwen3.8-27B-UD-Q6_K.gguf` |
|
||||
| API-Alias | `unsloth-qwen3.8-27b-ud-q6k` |
|
||||
|
||||
Die SHA-256-Prüfsumme ist in den Xet-Dateimetadaten des Herausgebers
|
||||
veröffentlicht. Für Download, vorhandene Dateien und Fehlerfälle gelten
|
||||
dieselben Regeln wie beim Swift-Artefakt. Beide Modelldateien werden
|
||||
bereitgestellt, bevor die A/B-Serie beginnt.
|
||||
|
||||
### 3.4 Optionaler Vision-Projektor
|
||||
|
||||
Der erste Text-/Coding-Test lädt keinen Vision-Projektor. Dadurch bleibt rund
|
||||
ein weiteres GiB für KV-Cache und Laufzeitpuffer verfügbar und die
|
||||
Grundabnahme erhält weniger Variablen.
|
||||
|
||||
Für eine spätere Vision-Stufe ist vorgesehen:
|
||||
|
||||
| Feld | Wert |
|
||||
| --- | --- |
|
||||
| Datei | `mmproj-Swift-Qwen3.8-27B-F16.gguf` |
|
||||
| veröffentlichte Größe | 928 MB |
|
||||
| SHA-256 | `daa1116c9422fa390cc8688495da0e91781f92841dfc3b31a378ff252571745a` |
|
||||
| Download | `https://huggingface.co/ukisai/Swift-Qwen3.8-27B-GGUF/resolve/eb0e3a7dc70643c2ab920f5133750d02c20849ff/mmproj-Swift-Qwen3.8-27B-F16.gguf` |
|
||||
|
||||
Der Projektor wird erst heruntergeladen und mit `--mmproj` eingebunden, wenn
|
||||
die Vision-Teststufe ausdrücklich aktiviert wird.
|
||||
|
||||
### 3.5 Lizenz und Herkunft
|
||||
|
||||
Das Qwen-Basismodell und das Unsloth-GGUF-Repository stehen laut ihren
|
||||
Modellkarten unter Apache-2.0. Die Swift-Änderungen werden laut
|
||||
Modellkarte unter der Swift Open License 1.0 angeboten. Die Modellkarte nennt
|
||||
eine kostenlose persönliche, Forschungs-, Bildungs-, Evaluierungs- und
|
||||
kommerzielle Nutzung bis zu einer dort definierten Umsatzgrenze; oberhalb
|
||||
davon gelten zusätzliche Lizenzbedingungen.
|
||||
|
||||
Vor einer externen oder kommerziellen Production-Nutzung wird der dann
|
||||
aktuelle Lizenztext gegen den konkreten Betreiber- und Nutzungsrahmen geprüft.
|
||||
Repository-Revision, Modellkarte, Prüfsummen und Lizenzhinweis werden als
|
||||
Herkunftsnachweis zum Profil dokumentiert.
|
||||
|
||||
## 4. Verzeichnis- und Rechtekonzept
|
||||
|
||||
Modelle sind große, unveränderliche und nicht geheime Artefakte. Production
|
||||
und Testing sollen dieselbe geprüfte Datei lesen, ohne sie in getrennten
|
||||
Podman-Stores zu duplizieren.
|
||||
|
||||
Vorgesehene Hostpfade:
|
||||
|
||||
```text
|
||||
/var/lib/ghostnet-ai/shared/models/
|
||||
├── swift-qwen3.8-27b/
|
||||
│ ├── Swift-Qwen3.8-27B-Q6_K.gguf
|
||||
│ ├── mmproj-Swift-Qwen3.8-27B-F16.gguf # erst in der Vision-Stufe
|
||||
│ └── SOURCE.yml
|
||||
└── unsloth-qwen3.8-27b/
|
||||
├── Qwen3.8-27B-UD-Q6_K.gguf
|
||||
└── SOURCE.yml
|
||||
```
|
||||
|
||||
| Objekt | Eigentümer | Modus | Begründung |
|
||||
| --- | --- | --- | --- |
|
||||
| Modellverzeichnisse | `root:root` | `0755` | durch Deployment verwaltet, für beide Umgebungen lesbar |
|
||||
| GGUF-Dateien | `root:root` | `0644` | unveränderlicher, nicht geheimer gemeinsamer Bestand |
|
||||
| `SOURCE.yml` | `root:root` | `0644` | nachvollziehbarer Herkunftsnachweis |
|
||||
|
||||
Der Container bindet nur das zum aktiven Profil gehörende Modellverzeichnis
|
||||
als `/models:ro` ein. Er erhält keinen Schreibzugriff auf den gemeinsamen Bestand.
|
||||
Downloads erfolgen atomar in ein temporäres Ziel und werden erst nach
|
||||
erfolgreicher Prüfsumme an den endgültigen Pfad verschoben.
|
||||
|
||||
Vor dem Download beider Modelle prüft Ansible mindestens 70 GiB freien Platz
|
||||
auf dem Zieldateisystem. Das deckt rund 45 GB endgültigen Modellbestand, die
|
||||
größte temporäre Downloaddatei und einen kleinen Betriebspuffer ab. Bereits
|
||||
korrekt vorhandene Artefakte werden bei der Berechnung nicht erneut als
|
||||
Downloadbedarf angesetzt.
|
||||
|
||||
## 5. Rootless Quadlet
|
||||
|
||||
### 5.1 Ablage und Identität
|
||||
|
||||
Ansible rendert für jedes Modell ein eigenes, rootverwaltetes Testing-Quadlet:
|
||||
|
||||
```text
|
||||
/etc/containers/systemd/users/981/
|
||||
├── gnai-testing-llama-swift.container
|
||||
└── gnai-testing-llama-unsloth.container
|
||||
```
|
||||
|
||||
Sie werden vom persistenten systemd-User-Manager von `gnai-testing`
|
||||
ausgeführt. Die Units heißen `gnai-testing-llama-swift.service` und
|
||||
`gnai-testing-llama-unsloth.service`. Beide verwenden dasselbe gepinnte Image,
|
||||
aber einen eigenen Containernamen, Modellpfad, API-Alias und Parametersatz.
|
||||
Podman speichert das identische Image nur einmal im rootless Image-Store.
|
||||
|
||||
### 5.2 Containeroptionen
|
||||
|
||||
Der `[Container]`-Abschnitt muss mindestens folgende Eigenschaften abbilden:
|
||||
|
||||
| Quadlet-Eigenschaft | Sollwert |
|
||||
| --- | --- |
|
||||
| `Image=` | gepinnter Digest aus Abschnitt 3.1 |
|
||||
| `Pull=` | `never`; Images werden vor dem Dienststart kontrolliert importiert |
|
||||
| `AddDevice=` | `/dev/kfd`, `/dev/dri/renderD128`, `/dev/dri/renderD129` |
|
||||
| `GroupAdd=` | `keep-groups` |
|
||||
| `Volume=` | Modellverzeichnis nach `/models:ro` |
|
||||
| `Network=` | dediziertes Testing-Netz für `llama.cpp` und eine spätere API-Policy-Schicht |
|
||||
| `NetworkAlias=` | `llama-cpp`; wegen gegenseitigem Ausschluss immer nur einmal aktiv |
|
||||
| `PublishPort=` | nicht gesetzt |
|
||||
| `ReadOnly=` | nach erfolgreichem Starttest `true` |
|
||||
| `SecurityLabelDisable=` | nicht gesetzt, solange SELinux auf dem Host nicht aktiv ist |
|
||||
|
||||
Podman dokumentiert `AddDevice=` und `GroupAdd=keep-groups` direkt als
|
||||
Quadlet-Optionen. Freie `PodmanArgs=` werden für diese Funktionen nicht
|
||||
benötigt. Die Render-Nodes werden weiterhin aus der Hardwareerkennung der
|
||||
Ansible-Rolle übernommen und nicht unabhängig als zweite Hardwarequelle
|
||||
konfiguriert.
|
||||
|
||||
### 5.3 systemd-Verhalten und gegenseitiger Ausschluss
|
||||
|
||||
Vorgesehene Servicewerte:
|
||||
|
||||
```ini
|
||||
[Service]
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
TimeoutStartSec=900
|
||||
```
|
||||
|
||||
Der erhöhte Start-Timeout berücksichtigt Modellinitialisierung und einen
|
||||
möglichen kontrollierten Erstimport. Das Image und das Modell sollen dennoch
|
||||
vor dem Dienststart bereitstehen. Ein normaler administrativer Stop darf nicht
|
||||
durch eine `always`-Policy sofort rückgängig gemacht werden.
|
||||
|
||||
Das Testing-Quadlet erhält keinen `[Install]`-Abschnitt und wird nicht für
|
||||
`default.target` aktiviert. Es startet ausschließlich für ein bewusst
|
||||
geöffnetes Testfenster. Die spätere Production-Unit kann nach bestandener
|
||||
Umschalt- und Neustartabnahme gezielt aktiviert werden.
|
||||
|
||||
Die Units erhalten folgende negative Abhängigkeit und eine eindeutige
|
||||
Reihenfolge:
|
||||
|
||||
```ini
|
||||
# Swift-Unit
|
||||
[Unit]
|
||||
Conflicts=gnai-testing-llama-unsloth.service
|
||||
Before=gnai-testing-llama-unsloth.service
|
||||
|
||||
# Unsloth-Unit
|
||||
[Unit]
|
||||
Conflicts=gnai-testing-llama-swift.service
|
||||
```
|
||||
|
||||
`Conflicts=` sorgt dafür, dass der Start einer Unit die andere stoppt. Das
|
||||
einmalig gesetzte `Before=` ergänzt die nötige Ordnungsabhängigkeit; systemd
|
||||
führt dadurch den Stop vollständig vor dem neuen Start aus. Der reguläre
|
||||
Profilwechsel bleibt zusätzlich eine explizite, sequenzielle Operation:
|
||||
|
||||
1. aktuell aktive Modell-Unit stoppen;
|
||||
2. auf vollständiges Ende des Containers warten;
|
||||
3. prüfen, dass kein `llama-server` mehr die GPUs belegt;
|
||||
4. gewünschte Modell-Unit starten; und
|
||||
5. Alias, Health, Modellrevision und GPU-Belegung prüfen.
|
||||
|
||||
Das Ansible-Playbook und die spätere Betriebs-SOP kapseln diese Reihenfolge.
|
||||
Ein direkter Start der jeweils anderen Unit löst denselben geordneten Wechsel
|
||||
aus. Der gleichzeitige Start beider Units in einer Transaktion gilt als Fehler
|
||||
und ist kein unterstützter Betriebsweg.
|
||||
|
||||
### 5.4 Entscheidung gegen den Router-Modus in der ersten Ausbaustufe
|
||||
|
||||
Der gepinnte `llama.cpp`-Build unterstützt bereits einen Router-Modus mit
|
||||
`--models-preset`, `--models-max`, automatischem Laden sowie den Endpunkten
|
||||
`/models/load` und `/models/unload`. Technisch wäre damit ein einzelner
|
||||
Router-Container für beide Modelle möglich.
|
||||
|
||||
Für die erste Abnahme bleibt jedes Quadlet im einfacheren Single-Model-Modus.
|
||||
Damit sind Startparameter, Logs, Health, VRAM-Verbrauch und Fehlerzustand einem
|
||||
Modell eindeutig zugeordnet. Außerdem benötigt der interne Dienst keine
|
||||
dynamische Modellverwaltungs-API. Nach stabiler Abnahme kann der Router-Modus
|
||||
separat bewertet werden; er ist keine Voraussetzung für eine externe
|
||||
API-Policy-Schicht.
|
||||
|
||||
## 6. `llama-server`-Ausgangsprofil
|
||||
|
||||
Das erste 128k-Zielprofil entspricht konzeptionell folgender Kommandozeile:
|
||||
|
||||
```text
|
||||
--host 0.0.0.0
|
||||
--port 8080
|
||||
--model /models/Swift-Qwen3.8-27B-Q6_K.gguf
|
||||
--alias swift-qwen3.8-27b-q6k
|
||||
--parallel 1
|
||||
--ctx-size 131072
|
||||
--n-predict 32768
|
||||
--n-gpu-layers all
|
||||
--split-mode layer
|
||||
--tensor-split 1,1
|
||||
--flash-attn on
|
||||
--cache-type-k q8_0
|
||||
--cache-type-v q8_0
|
||||
--jinja
|
||||
--reasoning on
|
||||
--reasoning-effort xhigh
|
||||
--reasoning-budget -1
|
||||
--temp 1.0
|
||||
--top-p 0.95
|
||||
--top-k 20
|
||||
--min-p 0.0
|
||||
--presence-penalty 0.0
|
||||
--repeat-penalty 1.0
|
||||
--no-webui
|
||||
--metrics
|
||||
```
|
||||
|
||||
Die Parameter werden als strukturierte Ansible-Variablen verwaltet und von
|
||||
einem Template in `Exec=` gerendert. Eine freie, manuell gepflegte
|
||||
Environment-Datei ist nicht die Konfigurationsquelle.
|
||||
|
||||
Vor der Implementierung wird `llama-server --help` im exakt gepinnten Image
|
||||
gegen alle Optionen geprüft. Falls dieser Build `all` für
|
||||
`--n-gpu-layers` nicht akzeptiert, wird der bereits bewährte numerische Wert
|
||||
`999` verwendet und die Abweichung dokumentiert.
|
||||
|
||||
Das Unsloth-Profil verwendet dieselbe Kommandozeile mit diesen gezielten
|
||||
Abweichungen:
|
||||
|
||||
| Parameter | kontrollierter A/B-Test | Praxisvergleich |
|
||||
| --- | --- | --- |
|
||||
| `--model` | `/models/Qwen3.8-27B-UD-Q6_K.gguf` | identisch |
|
||||
| `--alias` | `unsloth-qwen3.8-27b-ud-q6k` | identisch |
|
||||
| `--reasoning-effort` | `xhigh` | `medium` |
|
||||
|
||||
Alle nicht aufgeführten Parameter bleiben gegenüber dem Swift-Profil
|
||||
unverändert.
|
||||
|
||||
### 6.1 Reasoning
|
||||
|
||||
`xhigh` ist der Ausgangswert für Swift. `--reasoning-budget -1` vermeidet einen
|
||||
kurzen serverseitigen Thinking-Cap. `--n-predict 32768` begrenzt dagegen die
|
||||
gesamte Ausgabe eines einzelnen Requests und schützt den zunächst manuell
|
||||
betriebenen Testing-Dienst vor unbegrenzten Läufen.
|
||||
|
||||
Die Evaluierung enthält zusätzlich einen kontrollierten 65.536-Token-Lauf.
|
||||
Vor Production wird entschieden, ob die allgemeine Obergrenze erhöht oder
|
||||
später durch die API-Policy-Schicht pro Nutzer und Modell gesteuert wird. Ein Default von
|
||||
2.048 oder 4.096 Token ist für dieses Profil nicht vorgesehen.
|
||||
|
||||
Die API muss Reasoning getrennt von der finalen Antwort liefern. Zunächst wird
|
||||
das automatische `--reasoning-format` des gepinnten Builds geprüft. Ein fester
|
||||
Wert wird erst gesetzt, wenn die Kombination aus `llama.cpp`, der gewählten
|
||||
API-Policy-Schicht und den
|
||||
verwendeten Clients mit einem reproduzierbaren Request abgenommen wurde.
|
||||
|
||||
Für Unsloth werden zwei Reasoning-Varianten gerendert. `xhigh` dient dem
|
||||
kontrollierten A/B-Test mit Swift. `medium` bildet das geplante Praxisprofil
|
||||
ab. Alle übrigen Laufzeitparameter bleiben innerhalb der jeweiligen
|
||||
Vergleichsserie gleich.
|
||||
|
||||
### 6.2 Sampling
|
||||
|
||||
Die Werte entsprechen der Qwen-Modellkarte für Thinking. Sie bleiben Teil des
|
||||
Modellprofils. Ein späteres Instruct-/Non-Thinking-Profil erhält eigene Werte
|
||||
und überschreibt diese nicht stillschweigend.
|
||||
|
||||
### 6.3 Multi-GPU
|
||||
|
||||
`layer` und `1,1` sind die Ausgangskonfiguration für die zwei gleich großen
|
||||
Karten. Der zweite PCIe-Slot ist langsamer angebunden, weshalb experimentelle
|
||||
parallele Split-Verfahren nicht vor der stabilen Layer-Baseline aktiviert
|
||||
werden.
|
||||
|
||||
Die Startlogs müssen für beide Karten den belegten VRAM und die tatsächliche
|
||||
Layer-Verteilung ausweisen. `1,1` ist eine Gewichtung, keine Garantie für exakt
|
||||
gleiche Speicherbelegung. Ein abweichender Split wird ausschließlich anhand
|
||||
gemessener freier Reserven und Durchsatzwerte eingeführt.
|
||||
|
||||
### 6.4 Vision
|
||||
|
||||
Der Haupttest verwendet `--model` mit einer lokalen Datei und übergibt kein
|
||||
`--mmproj`. Erst die Vision-Stufe bindet den Projektor ein. Dadurch werden
|
||||
Text-/Tool-Qualität, Modellformat und 128k-Speicherbedarf unabhängig vom
|
||||
Vision-Encoder geprüft.
|
||||
|
||||
### 6.5 MTP
|
||||
|
||||
MTP bleibt im Ausgangsprofil aus. Die erste Variante ergänzt:
|
||||
|
||||
```text
|
||||
--spec-type draft-mtp
|
||||
--spec-draft-n-max 2
|
||||
```
|
||||
|
||||
Erst nach bestandener A/B-Messung folgt optional `n-max=3`. Draft-Kontext,
|
||||
VRAM-Zuwachs, Akzeptanzrate und tatsächlicher End-to-End-Gewinn werden
|
||||
protokolliert. MTP wird nicht zusammen mit Vision erstmals aktiviert.
|
||||
|
||||
## 7. VRAM-Modell
|
||||
|
||||
Die Hardware stellt nominell 2 × 16 GiB VRAM bereit. Die Summe allein genügt
|
||||
nicht als Fit-Nachweis, weil llama.cpp Gewichte, KV-Cache, Rechenpuffer und
|
||||
gegebenenfalls den Vision- oder Draft-Kontext nicht zwingend gleichmäßig auf
|
||||
beide Karten verteilt.
|
||||
|
||||
### 7.1 Gewichte
|
||||
|
||||
Die veröffentlichte Swift-Q6_K-Dateigröße von 22,9 GB entspricht grob
|
||||
21,3 GiB. Unsloth UD-Q6_K ist mit veröffentlichten 22 GB ähnlich groß. Die
|
||||
tatsächliche GPU-Belegung kann durch Metadaten, Ausrichtung, mmap und
|
||||
Backend-Puffer abweichen und wird aus den Startlogs übernommen.
|
||||
|
||||
### 7.2 KV-Cache
|
||||
|
||||
Die Swift-Modellkarte berechnet für FP16-K/V 64 KiB pro Token, weil nur 16 der
|
||||
64 Blöcke Full Attention verwenden:
|
||||
|
||||
| Kontext | FP16-K/V laut Modellkarte | Q8-K/V, rechnerische Näherung |
|
||||
| ---: | ---: | ---: |
|
||||
| 65.536 | 4 GiB | etwa 2 GiB |
|
||||
| 131.072 | 8 GiB | etwa 4 GiB |
|
||||
| 196.608 | 12 GiB | etwa 6 GiB |
|
||||
| 262.144 | 16 GiB | etwa 8 GiB |
|
||||
|
||||
Die Q8-Spalte ist eine Halbierungsnäherung und kein gemessener Wert. Block- und
|
||||
Backend-Overhead sowie die Verteilung je GPU werden beim Modellstart erfasst.
|
||||
|
||||
### 7.3 Grobe Zielrechnung
|
||||
|
||||
Für das textbasierte 128k-Profil ergibt sich vor der Messung:
|
||||
|
||||
```text
|
||||
Q6_K-Gewichte etwa 21,3 GiB
|
||||
Q8-K/V bei 128 KiToken etwa 4,0 GiB
|
||||
ROCm-/Graph-/Compute-Puffer noch zu messen
|
||||
──────────────────────────────────────────
|
||||
Zwischensumme vor Puffern etwa 25,3 GiB
|
||||
```
|
||||
|
||||
Mit Vision kommen rund 0,9 GB Dateigröße sowie zusätzliche Laufzeitpuffer
|
||||
hinzu. MTP kann ebenfalls weiteren Draft-Kontext und Puffer belegen. Deshalb
|
||||
werden beide Funktionen getrennt zugeschaltet und Q5_K_M bleibt als
|
||||
Ressourcen-Fallback vorgesehen.
|
||||
|
||||
## 8. Netzwerk und API-Grenze
|
||||
|
||||
`llama-server` hört im Container auf TCP 8080. Es gibt kein `PublishPort=` und
|
||||
keine Host-Portweiterleitung. Eine später beschlossene API-Policy-Schicht wird
|
||||
in dasselbe umgebungsspezifische rootless Podman-Netz aufgenommen und erreicht
|
||||
den Dienst über `http://llama-cpp:8080`.
|
||||
|
||||
Direkte Funktionsprüfungen erfolgen aus dem Container-Netz oder über einen
|
||||
kurzlebigen administrativen Prüfcontainer. Externe Nutzer sprechen später nur
|
||||
die freigegebene API-Policy-Schicht beziehungsweise die dafür vorgesehene
|
||||
veröffentlichte Anwendung an.
|
||||
|
||||
Der integrierte Webserver wird mit `--no-webui` betrieben. Metriken und Health
|
||||
bleiben ausschließlich im internen Container-Netz. Vor der Health-Quadlet-
|
||||
Konfiguration wird geprüft, welches HTTP-Werkzeug das gepinnte Image enthält;
|
||||
die Abnahme darf nicht von einem im Image fehlenden `curl` abhängen.
|
||||
|
||||
## 9. Ansible-Rollenentwurf
|
||||
|
||||
Die spätere Rolle `llama_cpp` soll folgende Zuständigkeiten besitzen:
|
||||
|
||||
1. Variablen und Artefaktmanifest vollständig validieren.
|
||||
2. Modellverzeichnisse mit festgelegten Rechten anlegen.
|
||||
3. beide Modelle über revisionsgebundene URLs und SHA-256 atomar
|
||||
bereitstellen.
|
||||
4. Gepinntes Containerimage im rootless Store der Zielumgebung sicherstellen.
|
||||
5. Render-Nodes aus `amd_gpu_access` übernehmen.
|
||||
6. umgebungsspezifisches Podman-Netz als Quadlet rendern.
|
||||
7. beide `llama.cpp`-Quadlets rendern und Generator-Dry-Run ausführen.
|
||||
8. systemd-User-Manager neu laden und den ausgewählten Testing-Dienst bewusst
|
||||
starten.
|
||||
9. Startlog, API-Health, Modellalias und GPU-Verteilung prüfen.
|
||||
10. einen kleinen Inferenztest ausführen und Ergebnis protokollieren.
|
||||
|
||||
Die Rollenvariablen enthalten eine Map unveränderlicher Modellprofile und
|
||||
einen einzelnen Selektor wie `llama_cpp_active_profile`. Die Rolle rendert
|
||||
beide Quadlets, startet aber nur die ausgewählte Unit. Ein Profilwechsel
|
||||
verändert keine Unit-Datei, sondern stoppt die aktive Unit vollständig und
|
||||
startet anschließend die bereits gerenderte Ziel-Unit.
|
||||
|
||||
Modell-Download und Dienstaktivierung erhalten getrennte Tags. Dadurch kann
|
||||
ein großes Artefakt vorbereitet werden, ohne den aktiven Stack umzuschalten.
|
||||
Check-Modus lädt kein Modell und startet keinen Dienst.
|
||||
|
||||
Production und Testing verwenden dieselbe Rollenlogik, aber getrennte
|
||||
Variablen, Quadlets, Netzwerke, Podman-Stores und systemd-User-Manager. Die
|
||||
Rolle darf nie beide GPU-Umgebungen gleichzeitig starten.
|
||||
|
||||
## 10. Technische Abnahme
|
||||
|
||||
### 10.1 Vor dem Start
|
||||
|
||||
- Modellrevision, Dateiname, Größe und SHA-256 stimmen mit dem Manifest
|
||||
überein.
|
||||
- Das Containerimage stimmt mit Digest und OCI-Labels überein.
|
||||
- Beide Render-Nodes und `/dev/kfd` sind für `gnai-testing` zugreifbar.
|
||||
- Production ist vollständig beendet und hält keine GPU-Prozesse.
|
||||
- Der Quadlet-Generator meldet keine Fehler.
|
||||
- Die generierte Unit enthält keinen veröffentlichten Host-Port.
|
||||
|
||||
### 10.2 Start mit 64k
|
||||
|
||||
- Container und Unit werden `active` und bleiben stabil.
|
||||
- Modellformat und eingebettetes Chat-Template werden erkannt.
|
||||
- alle Layer werden auf ROCm ausgelagert;
|
||||
- beide GPUs erscheinen in den Startlogs;
|
||||
- `/health` und `/v1/models` antworten erfolgreich;
|
||||
- der API-Alias entspricht dem jeweils ausgewählten Profil;
|
||||
- ein kurzer Thinking-Request liefert Reasoning und finale Antwort; und
|
||||
- ein definierter Tool-Call ist syntaktisch gültig.
|
||||
|
||||
### 10.3 Start mit 128k
|
||||
|
||||
- kein OOM oder unbeabsichtigter CPU-Offload;
|
||||
- freier VRAM je GPU und tatsächlicher KV-Verbrauch werden protokolliert;
|
||||
- Prompt-Prefill wird stufenweise mit 8k, 32k, 64k und 128k geprüft;
|
||||
- mindestens ein langer Request endet ohne Kontext- oder Parserfehler; und
|
||||
- ein einstündiger gemischter Test erzeugt keinen Neustart, GPU-Reset oder
|
||||
dauerhaft hängenden Slot.
|
||||
|
||||
Diese Abnahme wird zuerst für Swift und danach für Unsloth wiederholt. Erst
|
||||
wenn beide Profile die technische Abnahme bestanden haben, beginnt die
|
||||
fachliche A/B-Serie aus GNAI-009.
|
||||
|
||||
### 10.4 Wiederanlauf und Idempotenz
|
||||
|
||||
- ein zweiter identischer Ansible-Lauf meldet `changed=0`;
|
||||
- ein kontrollierter Containerneustart erhält Profil und Modell;
|
||||
- ein geplanter Hostneustart startet nur die freigegebene Umgebung;
|
||||
- ein absichtlich gestoppter Testing-Dienst wird nicht ungewollt neu gestartet;
|
||||
- ein Startversuch der zweiten Modell-Unit kann niemals beide Modelle
|
||||
gleichzeitig im VRAM belassen;
|
||||
- `podman ps`, systemd-Status und Health stimmen überein; und
|
||||
- der Rückwechsel auf das vorherige Profil funktioniert ohne erneuten
|
||||
Modelldownload.
|
||||
|
||||
## 11. Noch nicht festgelegte Werte
|
||||
|
||||
- genaue Health-Implementierung im gepinnten Containerimage;
|
||||
- CPU-Thread-, Batch- und UBatch-Tuning für den i5-13600K;
|
||||
- verbindliche maximale Ausgabe für Production und einzelne Nutzer;
|
||||
- Aufnahme des Vision-Projektors in das erste Production-Profil;
|
||||
- gemessener optimaler Split bei unterschiedlicher PCIe-Anbindung;
|
||||
- Aktivierung und Parameter von MTP;
|
||||
- Aufbewahrungs- und Löschregeln für abgelöste Modellartefakte.
|
||||
|
||||
## 12. Quellen
|
||||
|
||||
- [Qwen3.8-27B – Architektur, Reasoning und Sampling](https://huggingface.co/Qwen/Qwen3.8-27B)
|
||||
- [Swift-Qwen3.8-27B – Training und Benchmarks](https://huggingface.co/ukisai/Swift-Qwen3.8-27b)
|
||||
- [Swift-Qwen3.8-27B-GGUF – Dateien, Quantisierung und MTP](https://huggingface.co/ukisai/Swift-Qwen3.8-27B-GGUF)
|
||||
- [Swift-GGUF-Prüfsummen](https://huggingface.co/ukisai/Swift-Qwen3.8-27B-GGUF/blob/main/SHA256SUMS)
|
||||
- [Unsloth Qwen3.8-27B-GGUF – Modellkarte](https://huggingface.co/unsloth/Qwen3.8-27B-GGUF)
|
||||
- [Unsloth UD-Q6_K – Datei und SHA-256](https://huggingface.co/unsloth/Qwen3.8-27B-GGUF/blob/4ca720788d1e01f1bff70c033e0d0028fd02e502/Qwen3.8-27B-UD-Q6_K.gguf)
|
||||
- [llama.cpp Server-Parameter und Router-Modus des gepinnten Builds](https://github.com/ggml-org/llama.cpp/blob/f072b103714dfa1eee531f80b24512faf38e3dd2/tools/server/README.md)
|
||||
- [Podman Quadlet](https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html)
|
||||
- [systemd-Unit-Abhängigkeiten](https://manpages.debian.org/trixie/systemd/systemd.unit.5.en.html)
|
||||
@@ -0,0 +1,19 @@
|
||||
# KI-Dokumentation
|
||||
|
||||
Dieser Bereich enthält die fachliche Zielarchitektur und Deployment-Planung für
|
||||
den KI-Stack auf dem LLM-Server. Gemeinsame Hostgrundlagen liegen im
|
||||
[Geräteindex](../README.md), übergreifende Netzwerk- und Sicherheitsgrundlagen
|
||||
in der [Plattformdokumentation](../../README.md). Der CPU-basierte
|
||||
Video-Encoding-Workload besitzt einen
|
||||
[eigenen Dokumentationsbereich](../encoding/GNENC-000-DOC_Index.md).
|
||||
|
||||
| Dokument | Inhalt |
|
||||
| --- | --- |
|
||||
| [GNAI-001](../../../GNAI-001-PLAN_Project-Roadmap.md) | priorisierter High-Level-Implementierungsplan mit Entscheidungstoren und Abnahmekriterien |
|
||||
| [GNAI-004](GNAI-004-INFRA_Architecture.md) | Zielarchitektur, Komponenten und Datenwege des KI-Stacks |
|
||||
| [GNAI-008](GNAI-008-PLAN_Ansible-Deployment.md) | nicht finaler Arbeitsentwurf für Ansible- und Gitea-Deployment |
|
||||
| [GNAI-009](GNAI-009-PLAN_Llama-Model-Evaluation.md) | nicht finaler A/B-Plan für Swift, Unsloth, Reasoning-, Kontext- und MTP-Abnahme |
|
||||
| [GNAI-010](GNAI-010-TECH_Llama-Serving.md) | technische Sollkonfiguration für zwei geprüfte Modellprofile auf rootless `llama.cpp` mit ROCm |
|
||||
|
||||
Der übergreifende Projektstand steht im
|
||||
[zentralen Dokumentationsindex](../../../GNAI-000-DOC_Index.md).
|
||||
@@ -0,0 +1,32 @@
|
||||
# Video-Encoding – Dokumentationsindex und Projektstand
|
||||
|
||||
**Dokumenten-ID:** GNENC-000-DOC
|
||||
**Stand:** 2026-10-05
|
||||
**Status:** Nicht finaler Arbeitsstand; Encoding-Workload noch nicht installiert
|
||||
|
||||
## Dokumente
|
||||
|
||||
| Dokument | Zweck / Zielgruppe |
|
||||
| --- | --- |
|
||||
| [GNENC-001-PLAN_Architecture.md](GNENC-001-PLAN_Architecture.md) | vorläufige Architektur, Repository-Grenzen, Ressourcenmodell und offene Entscheidungen; Infrastrukturadministratoren |
|
||||
|
||||
## Bestätigter Stand
|
||||
|
||||
- Das gemeinsame Infrastructure-Repository bleibt die einzige Quelle für den
|
||||
Ansible-Sollzustand des physischen Servers.
|
||||
- Encoding-Dokumentation verwendet innerhalb des LLM-Server-Gerätebereichs
|
||||
`docs/platform/llm-server/encoding/` und die Dokumentenkennung `GNENC`.
|
||||
- Encoding ist nach Betreiberfestlegung CPU-basiert und darf parallel zur
|
||||
jeweils aktiven KI-Umgebung laufen.
|
||||
- Encoding soll ausschließlich außerhalb der Stoßzeiten, zunächst nachts,
|
||||
automatisch ausgeführt werden.
|
||||
- Das gesperrte Servicekonto `gn-encoding` mit UID/GID 982 und eigenem
|
||||
rootless Podman-Store ist eingerichtet und verifiziert.
|
||||
- Es sind noch kein Encoding-Image, kein Container, keine Quadlet-Unit und kein
|
||||
Timer eingerichtet.
|
||||
|
||||
## Nächste Festlegungen
|
||||
|
||||
Vor einer Encoding-Implementierung müssen insbesondere Encoder und
|
||||
Container-Image, Ein- und Ausgabeverfahren, genaue Zeitfenster, CPU-/I/O-Grenzen,
|
||||
Abbruchverhalten am Ende des Zeitfensters und Aufbewahrung festgelegt werden.
|
||||
@@ -0,0 +1,142 @@
|
||||
# Arbeitsentwurf
|
||||
|
||||
**Dokumenten-ID:** GNENC-001-PLAN
|
||||
**Titel:** CPU-basiertes Video-Encoding auf dem KI-Server
|
||||
**Stand:** 2026-10-05
|
||||
**Status:** Nicht finaler Arbeitsentwurf zur Persistierung der Planungsentscheidung
|
||||
|
||||
## 1. Zweck und Abgrenzung
|
||||
|
||||
Der physische Server `ghostnet-ki` soll neben den KI-Workloads einen getrennten
|
||||
Workload zum Encoding von Videodateien ausführen. Der Encoding-Workload gehört
|
||||
nicht zum LLM-Stack. Seine fachliche Architektur, Bedienung und späteren SOPs
|
||||
werden deshalb in diesem eigenen Dokumentationsbereich geführt.
|
||||
|
||||
Ansible, Podman, Betriebssystem und hostweite Ressourcenregeln bleiben
|
||||
gemeinsame Plattformbestandteile. Sie werden weiterhin ausschließlich im
|
||||
Infrastructure-Repository verwaltet, damit nicht mehrere Repositories
|
||||
unabhängig konkurrierende Sollzustände auf denselben Host anwenden.
|
||||
|
||||
## 2. Bestätigte Entscheidungen
|
||||
|
||||
| Thema | Entscheidung | Nachweisgrenze |
|
||||
| --- | --- | --- |
|
||||
| Repository | Host- und Deployment-Ansible bleibt im bestehenden Infrastructure-Repository | angenommen |
|
||||
| Dokumentation | Encoding liegt unter `docs/platform/llm-server/encoding/` und verwendet die Kennung `GNENC` | umgesetzt |
|
||||
| Ausführungsidentität | eigenes gesperrtes Konto `gn-encoding`, UID/GID 982 | eingerichtet und mit rootless Podman geprüft |
|
||||
| Containertrennung | eigener rootless Podman-Store und eigener rootverwalteter Quadlet-Pfad | eingerichtet; noch keine Workload definiert |
|
||||
| Rechenressource | Encoding ist nach Betreiberfestlegung rein CPU-basiert und erhält keinen GPU-Zugriff | Planungsentscheidung; reale Last noch nicht gemessen |
|
||||
| Parallelität | darf parallel zu AI Production oder AI Testing laufen | Planungsentscheidung |
|
||||
| Zeitsteuerung | automatische Ausführung ausschließlich außerhalb der Stoßzeiten, zunächst nachts | Richtung festgelegt; konkrete Uhrzeiten fehlen |
|
||||
| Anwendungen | konkrete Encoder, Images, Profile und Queue sind noch nicht gewählt | offen |
|
||||
|
||||
## 3. Zielbild
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
HOST[ghostnet-ki]
|
||||
PROD[gnai-production\nrootless Podman]
|
||||
TEST[gnai-testing\nrootless Podman]
|
||||
ENC[gn-encoding\nrootless Podman]
|
||||
GPU[beide GPUs]
|
||||
CPU[CPU / I-O Budget]
|
||||
TIMER[nächtliche systemd-Zeitsteuerung]
|
||||
|
||||
HOST --> PROD
|
||||
HOST --> TEST
|
||||
HOST --> ENC
|
||||
PROD --> GPU
|
||||
TEST --> GPU
|
||||
ENC --> CPU
|
||||
TIMER --> ENC
|
||||
```
|
||||
|
||||
AI Production und AI Testing bleiben untereinander exklusiv. Der
|
||||
Encoding-Workload ist von dieser KI-Umschaltung unabhängig und darf parallel
|
||||
zur jeweils aktiven KI-Umgebung laufen. Hostwartung kann weiterhin einen Stopp
|
||||
aller Workloads erfordern.
|
||||
|
||||
## 4. Ressourcen- und Zeitsteuerung
|
||||
|
||||
Die nächtliche Einplanung allein schützt Production nicht vollständig: Ein
|
||||
Encoding-Lauf kann länger dauern als sein Startfenster, und Inferenz benötigt
|
||||
trotz GPU-Schwerpunkt CPU für Tokenisierung, Netzwerk und Begleitdienste. Die
|
||||
spätere Encoding-Unit soll deshalb mindestens verwalten:
|
||||
|
||||
- ein eindeutig definiertes Zeitfenster mit Zeitzone;
|
||||
- Verhalten bei verpasstem Start, Neustart und überlangem Lauf;
|
||||
- maximale CPU-Nutzung oder CPU-Gewichtung;
|
||||
- niedrigere Scheduling-Priorität gegenüber interaktiven Diensten;
|
||||
- I/O-Gewichtung und freien Speicher;
|
||||
- geordneten Abbruch beziehungsweise Abschluss am Ende des erlaubten Fensters;
|
||||
- Begrenzung auf genau einen gleichzeitigen Encoding-Job.
|
||||
|
||||
Konkrete Grenzwerte werden erst nach einem repräsentativen Encoding-Test unter
|
||||
gleichzeitiger Inferenzlast festgelegt. `gn-encoding` erhält keine Mitgliedschaft
|
||||
in GPU-Gruppen und keine `/dev/kfd`- oder `/dev/dri`-Weitergabe.
|
||||
|
||||
## 5. Repository-Grenzen
|
||||
|
||||
Vorgesehene Ansible-Struktur:
|
||||
|
||||
```text
|
||||
ansible/
|
||||
├── playbooks/
|
||||
│ ├── host.yml
|
||||
│ ├── ai.yml
|
||||
│ ├── video-encoding.yml
|
||||
│ └── site.yml
|
||||
└── roles/
|
||||
├── host_baseline/
|
||||
├── podman_runtime/
|
||||
├── workload_accounts/
|
||||
├── ai_stack/
|
||||
├── ai_environment_switch/
|
||||
└── video_encoding/
|
||||
```
|
||||
|
||||
`host.yml` verwaltet ausschließlich gemeinsame Plattformzustände.
|
||||
`ai.yml` darf nur KI-Zustände verändern. `video-encoding.yml` verwaltet nur das
|
||||
Konto `gn-encoding`, seine Pfade, Images, Quadlets, Timer und Health Checks. Ein
|
||||
späteres `site.yml` kann die unterstützte Gesamtkonfiguration in definierter
|
||||
Reihenfolge anwenden.
|
||||
|
||||
Ein separates Quellcode-Repository wird erst benötigt, wenn eigene
|
||||
Encoding-Software, eine Oberfläche oder ein Queue-Dienst entwickelt wird. Ein
|
||||
solches Repository erzeugt ein versioniertes Artefakt oder Image; ausschließlich
|
||||
das Infrastructure-Repository legt fest, welche Version auf dem Server läuft.
|
||||
|
||||
## 6. Aktueller Implementierungsstand
|
||||
|
||||
Die gemeinsame Rolle `workload_accounts` hat am 20.09.2026 eingerichtet:
|
||||
|
||||
```text
|
||||
Konto: gn-encoding (UID/GID 982)
|
||||
Konfiguration: /etc/ghostnet-encoding
|
||||
Daten: /var/lib/ghostnet-encoding
|
||||
Eingang: /var/lib/ghostnet-encoding/input
|
||||
Ausgang: /var/lib/ghostnet-encoding/output
|
||||
Arbeitsdaten: /var/lib/ghostnet-encoding/work
|
||||
Quadlets: /etc/containers/systemd/users/982
|
||||
Podman-Store: /var/lib/ghostnet-encoding/.local/share/containers/storage
|
||||
```
|
||||
|
||||
Passwort und interaktive Shell sind gesperrt. Der systemd-User-Manager läuft
|
||||
dank Linger unabhängig von einer Anmeldung. Der Podman-Store wurde als
|
||||
rootless und getrennt von beiden KI-Stores geprüft. Es wurden noch keine Images
|
||||
oder Container angelegt.
|
||||
|
||||
## 7. Offene Entscheidungen
|
||||
|
||||
1. Welcher Encoder und welches fest versionierte Container-Image werden genutzt?
|
||||
2. Welche Codecs, Qualitätsprofile und Hardwarekompatibilitäten sind erforderlich?
|
||||
3. Woher kommen Eingabedateien, und wie werden fertige Dateien abgeholt?
|
||||
4. Welches konkrete nächtliche Zeitfenster und welche Zeitzone gelten?
|
||||
5. Darf ein laufender Job das Zeitfenster überschreiten oder wird er gestoppt?
|
||||
6. Welche CPU-, Speicher- und I/O-Grenzen schützen die KI-Dienste?
|
||||
7. Wie werden Warteschlange, Wiederholung, Teilresultate und Fehler behandelt?
|
||||
8. Welche Aufbewahrungs-, Backup- und Löschregeln gelten für Video- und
|
||||
Arbeitsdateien?
|
||||
9. Welche Statusanzeige oder Benachrichtigung benötigen Nutzer?
|
||||
|
||||
Diese Werte werden nicht vorab in Playbooks angenommen.
|
||||
@@ -0,0 +1,159 @@
|
||||
# Ghostnet.AI-Host – Basisinstallation
|
||||
|
||||
**Dokumenten-ID:** GNAI-002-INFRA
|
||||
**Titel:** Initialisierung und OS-Installation des KI-Hosts `10.0.0.90`
|
||||
**Status:** Installationsanleitung mit lesendem Teilabgleich; vollständige Abnahme offen
|
||||
**Referenzen:** -
|
||||
|
||||
---
|
||||
|
||||
## Änderungsverlauf
|
||||
|
||||
| Datum | Version | Autor | Beschreibung der Änderung |
|
||||
| --- | --- | --- | --- |
|
||||
| 2026-08-24 | 1.0 | spektr | Erstellung des Initial-Dokuments (Hardware & OS) |
|
||||
| 2026-08-24 | 1.1 | spektr | Detaillierung der BIOS-ReBAR-Settings, Netinst-CLI-Schritte, Post-Installation & Abnahmekriterien hinzugefügt. |
|
||||
| 2026-09-16 | 1.2 | spektr | Benutzer- und Netzwerkaufgaben nach GNAI-003 verlagert; ungesicherte Downloadpfade und Domainvorgabe entfernt. |
|
||||
| 2026-09-18 | 1.3 | spektr | GPU-Abnahme auf nachweisbare Geräteerkennung begrenzt; Firmwarepaket präzisiert. |
|
||||
| 2026-09-19 | 1.4 | spektr | Getrennte SSH-Zugänge für `ghost` und `ghost-agent` als erfolgreich getestet übernommen. |
|
||||
| 2026-09-22 | 1.5 | Codex | In die hostbezogene Struktur `platform/ghostnet-ai/` verschoben und Geltungsbereich präzisiert. |
|
||||
|
||||
---
|
||||
|
||||
## 1. Zweck und Geltungsbereich
|
||||
|
||||
Dieses Dokument beschreibt Hardware, BIOS und Debian-Installation des KI-Servers. Benutzer, statische Netzkonfiguration und SSH-Härtung werden in [GNAI-003](GNAI-003-INFRA_Users.md) eingerichtet. Die Schritte sind eine Anleitung, kein Nachweis ihrer Ausführung auf dem bestehenden Host.
|
||||
|
||||
---
|
||||
|
||||
## 2. Spezifikationen (Hardware-Konfiguration)
|
||||
|
||||
| Komponente | Spezifikation | Bemerkung |
|
||||
| --- | --- | --- |
|
||||
| **Gehäuse** | Silverstone SST-RM42-502 | Rackmount / Server-Chassis |
|
||||
| **Mainboard** | ASRock Z790 PG Lightning | Socket LGA1700 |
|
||||
| **CPU** | Intel Core i5-13600K | 14 Kerne (6P + 8E) |
|
||||
| **CPU-Kühler** | Thermalright Peerless Assassin 120 Mini | - |
|
||||
| **RAM** | Crucial Pro DDR5 RAM 64GB Kit (2x32GB) | Dimm Slot A2 & B2 |
|
||||
| **PSU** | Corsair RM1200e | 1200W ATX 3.0 |
|
||||
| **SSD** | 2 TB Samsung 970 EVO Plus | Primäre NVMe (Slot M2_1) |
|
||||
| **GPU 1** | Sapphire Nitro+ AMD Radeon RX 6900 XT OC 16GB | PCIe Slot 1 (x16) |
|
||||
| **GPU 2** | Sapphire Nitro+ AMD Radeon RX 6900 XT OC 16GB | PCIe Slot 2 (x8/x4 via PCH) |
|
||||
|
||||
### 2.1 BIOS-Konfiguration
|
||||
|
||||
Vor der Betriebssystem-Installation sind folgende Schritte im BIOS (Aufrufen via `DEL` oder `F2` beim Booten) durchzuführen:
|
||||
|
||||
1. **Firmware-Update (Instant Flash):**
|
||||
* BIOS-Datei auf einen FAT32-formatierten USB-Stick entpacken.
|
||||
* Im BIOS navigieren zu: `Tool` -> `Instant Flash`.
|
||||
* BIOS-Version auf **`19.02`** (oder neuer) aktualisieren.
|
||||
|
||||
2. **RAM-Performance:**
|
||||
* Navigieren zu: `OC Tweaker` -> `DRAM Configuration`.
|
||||
* **Load XMP Setting:** auf **XMP Profile 1** stellen.
|
||||
|
||||
3. **Virtualisierung & PCI-Einstellungen (Wichtig für GPU/ROCm):**
|
||||
* Navigieren zu: `Advanced` -> `CPU Configuration` -> **Intel Virtualization Technology** -> `Enabled`.
|
||||
* Navigieren zu: `Advanced` -> `PCI Configuration`:
|
||||
* **Above 4G Decoding:** `Enabled`
|
||||
* **Re-Size BAR Support:** `Enabled`; tatsächliche GPU-Zuordnung nach der Installation prüfen. Aus der BIOS-Option allein folgt noch kein nachgewiesener ROCm-Leistungsgewinn.
|
||||
|
||||
---
|
||||
|
||||
## 3. Installation des Betriebssystems
|
||||
|
||||
### 3.1 Vorbereitung des Installationsmediums (am Admin-PC)
|
||||
|
||||
1. **Debian ISO herunterladen:** Die gewünschte Debian-Version und die passende `amd64`-Netinst-ISO auf [Debian CD Images](https://cdimage.debian.org/debian-cd/) auswählen. ISO und `SHA512SUMS` aus demselben Release-Verzeichnis herunterladen; Dateinamen nicht aus dieser Anleitung ableiten.
|
||||
|
||||
2. **Checksum verifizieren:**
|
||||
```bash
|
||||
sha512sum --ignore-missing -c SHA512SUMS
|
||||
```
|
||||
Erwartet wird eine `OK`-Zeile für die tatsächlich heruntergeladene ISO. Bei einem Fehler Download und Prüfsummenquelle kontrollieren.
|
||||
|
||||
3. **Bootfähigen USB-Stick erstellen:**
|
||||
* [Ventoy](https://www.ventoy.net) auf dem USB-Stick installieren.
|
||||
* Die heruntergeladene ISO-Datei direkt per Dateimanager auf die Ventoy-Partition kopieren.
|
||||
|
||||
---
|
||||
|
||||
### 3.2 Schritt-für-Schritt Installationsanleitung (Debian Netinst)
|
||||
|
||||
1. **Booten:**
|
||||
* USB-Stick einstecken, System starten und per `F11` das Boot-Menü öffnen.
|
||||
* USB-Stick auswählen -> Im Ventoy-Menü `debian-netinst.iso` wählen -> **Graphical Install** auswählen.
|
||||
|
||||
2. **Lokalisierung:**
|
||||
* **Language:** `German - Deutsch`
|
||||
* **Land/Territorium:** `Deutschland`
|
||||
* **Tastaturlayout:** `Deutsch`
|
||||
|
||||
3. **Netzwerk & Hostname:**
|
||||
* **Hostname:** `ghostnet-ki` (auf dem bestehenden KI-Server am 18.09.2026 per `hostnamectl` bestätigt).
|
||||
* **Domain-Name:** Nur eine tatsächlich konfigurierte lokale Domain eintragen; andernfalls leer lassen. `ghostnet.ki` ist in dieser Dokumentation lediglich ein optionaler SSH-Alias.
|
||||
* **Netzwerk:** Per DHCP konfigurieren lassen. *(Feste IP wird nach der Installation vergeben).*
|
||||
|
||||
4. **Benutzerverwaltung:**
|
||||
* **Root-Passwort:** Starkes Passwort aus dem Passwort-Manager eintragen.
|
||||
* **Neuer Benutzer:**
|
||||
* Vollständiger Name: `Admin`
|
||||
* Benutzername: vor der Installation festlegen. Der bestehende KI-Server verwendet `ghost` als initiales Administrationskonto; `admin` ist dort nicht vorhanden.
|
||||
* Passwort: Sicheres Kennwort vergeben.
|
||||
|
||||
5. **Partitionierung (Manuell / Geführt):**
|
||||
* Methode: `Geführt - gesamte Festplatte verwenden`.
|
||||
* Disk auswählen: `Samsung SSD 970 EVO Plus 2TB`.
|
||||
* Schema: `Alle Dateien auf eine Partition (für Anfänger empfohlen)`.
|
||||
* **Wichtig:** Änderungen auf die Festplatte schreiben -> `Ja`.
|
||||
|
||||
6. **Paketmanager-Konfiguration:**
|
||||
* Spiegelserver-Land: `Deutschland`
|
||||
* `deb.debian.org` wählen.
|
||||
* HTTP-Proxy: Leer lassen.
|
||||
|
||||
7. **Software-Auswahl (Software selection):**
|
||||
* [_] Debian desktop environment *(ABWÄHLEN)*
|
||||
* [_] GNOME / KDE / etc. *(ABWÄHLEN)*
|
||||
* [x] **SSH server** *(AUSWÄHLEN)*
|
||||
* [x] **standard system utilities** *(AUSWÄHLEN)*
|
||||
|
||||
8. **Bootloader (GRUB):**
|
||||
* GRUB-Bootloader in den primären Laufwerksbereich installieren: `Ja`
|
||||
* Gerät wählen: `/dev/nvme0n1` (Samsung SSD).
|
||||
|
||||
9. **Abschluss:**
|
||||
* USB-Stick entfernen und auf `Weiter` klicken, um das System neu zu starten.
|
||||
|
||||
---
|
||||
|
||||
### 3.3 Grundlegende Werkzeuge und Firmware
|
||||
|
||||
Für die AMD-GPUs ist in Debian 13 das Paket [`firmware-amd-graphics`](https://packages.debian.org/trixie/firmware-amd-graphics) aus `non-free-firmware` vorgesehen. Das breitere Metapaket `firmware-linux-nonfree` zieht dieses Paket bereits als Abhängigkeit nach sich und wird für diesen Schritt nicht zusätzlich benötigt. Die folgende Anweisung gehört zu einer Neuinstallation oder zum späteren Ansible-Sprint, nicht zu diesem Dokumentationsabgleich:
|
||||
|
||||
```bash
|
||||
# Vorher prüfen, ob die Debian-Paketquellen die Komponente non-free-firmware enthalten.
|
||||
apt update
|
||||
apt install -y curl wget git htop tmux pciutils firmware-amd-graphics
|
||||
```
|
||||
|
||||
Für Sudo, Netzwerk und SSH mit [GNAI-003](GNAI-003-INFRA_Users.md) fortfahren. Vor Änderungen auf einem laufenden Host die dort dokumentierte Netzverwaltung prüfen.
|
||||
|
||||
---
|
||||
|
||||
## 4. Abnahmekriterien
|
||||
|
||||
Vor der Übergabe an das KI-Team (Phase 2) muss das System folgende Punkte bestehen:
|
||||
|
||||
* **RAM-Check:** `free -h` zeigt mindestens ~62 GB RAM an.
|
||||
* **GPU-Erkennung:** `lspci -nn | grep -Ei 'vga|display|3d'` listet **zwei** AMD-Grafikgeräte auf. Das genaue Kartenmodell anhand des Hardwareinventars oder der Geräte selbst prüfen; die PCI-Familienkennung allein genügt dafür nicht.
|
||||
* **Netzwerk:** Der Server ist nach GNAI-003 unter `10.0.0.90` erreichbar. Die getrennten SSH-Zugänge für `ghost` und `ghost-agent` wurden am 19.09.2026 erfolgreich getestet.
|
||||
* **Storage:** `lsblk -f` zeigt die vorgesehene Partitionierung der 2-TB-SSD; `df -h /` zeigt die nutzbare Dateisystemgröße (in binären Einheiten kleiner als 2 TB).
|
||||
* **Re-Size BAR:** BIOS-Einstellung und zugewiesene PCIe-BAR-Größen getrennt prüfen; ein bloßer `dmesg | grep -i BAR`-Treffer gilt nicht als Funktions- oder Leistungsnachweis.
|
||||
|
||||
---
|
||||
|
||||
## 5. Lesender Teilabgleich des bestehenden Hosts am 18.09.2026
|
||||
|
||||
Per SSH wurden Debian 13.6, Hostname `ghostnet-ki`, Intel Core i5-13600K, 62 GiB nutzbarer RAM und zwei AMD Navi-21-GPUs mit PCI-ID `1002:73bf` beobachtet. `lsblk` zeigt eine 1,8-TiB-NVMe mit EFI-, ext4-Root- und Swap-Partition; `df -h /` zeigte 1,8 TiB Dateisystemgröße. `lspci` bestätigt die Gerätefamilie, aber weder das genaue Kartenmodell noch ROCm-Funktion und ReBAR-Leistung. BIOS-Version, XMP, Firmwarestand und Restore wurden nicht geprüft.
|
||||
@@ -0,0 +1,206 @@
|
||||
# Ghostnet.AI-Host – Benutzer, Netzwerk und SSH
|
||||
|
||||
**Dokumenten-ID:** GNAI-003-INFRA
|
||||
**Titel:** Benutzer-, Netzwerk- und SSH-Sicherheitsarchitektur des KI-Hosts `10.0.0.90`
|
||||
**Status:** Betriebsanleitung; tatsächliche Netzverwaltung und Konten vor Anwendung prüfen
|
||||
|
||||
---
|
||||
|
||||
## Änderungsverlauf
|
||||
|
||||
| Datum | Version | Autor | Beschreibung der Änderung |
|
||||
| --- | --- | --- | --- |
|
||||
| 2026-08-26 | 1.0 | spektr | Erstellung des Dokuments (Benutzer, Netzwerkkonfiguration, SSH-Härtung) |
|
||||
| 2026-09-15 | 1.1 | spektr | Kontenbestand, DNS-Verwaltung und SSH-Dienstnamen präzisiert; doppelte Basisschritte bereinigt. |
|
||||
| 2026-09-16 | 1.2 | spektr | Tatsächliche Konten, Gruppen, Netz- und SSH-Konfiguration sowie Grenzen des Login-Tests dokumentiert. |
|
||||
| 2026-09-18 | 1.3 | spektr | Workstation-spezifische Schlüsselpfade und Host-Fingerprint in den lokalen Arbeitskontext verlagert. |
|
||||
| 2026-09-19 | 1.4 | spektr | Persönlichen Login `ghost` und Agentenlogin `ghost-agent` als erfolgreich getestet dokumentiert. |
|
||||
| 2026-09-20 | 1.5 | spektr / Codex | Deployment- und getrennte rootless Workload-Konten mit festen IDs und Zuständigkeiten ergänzt. |
|
||||
| 2026-09-20 | 1.6 | spektr / Codex | `render`-Mitgliedschaft und geprüfte AMD-Geräterechte der beiden KI-Konten ergänzt. |
|
||||
| 2026-09-22 | 1.7 | Codex | In die hostbezogene Struktur `platform/ghostnet-ai/` verschoben und Geltungsbereich präzisiert. |
|
||||
|
||||
---
|
||||
|
||||
## 1. Zweck und Geltungsbereich
|
||||
|
||||
Dieses Dokument beschreibt Benutzer, statische IP und SSH des KI-Servers. Der ursprüngliche lesende Kontenabgleich vom 18.09.2026 umfasste `ghost`, `debugofen` und `ghost-agent`; `admin` existiert auf diesem Host nicht. Am 19. und 20.09.2026 kamen der Ansible-Deployment-Account sowie drei gesperrte Workload-Konten hinzu. Die folgenden Befehle sind für Neuinstallation oder gezielte Änderung gedacht und ersetzen keine Prüfung des vorhandenen Systems. Der WireGuard-LXC besitzt ein separates Konto `admin`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Voraussetzungen & Berechtigungen
|
||||
|
||||
* Bereits installiertes Debian-Grundsystem gemäß [GNAI-002](GNAI-002-INFRA_BaseSystem.md).
|
||||
* Root-Zugriff oder Benutzer mit `sudo`-Rechten.
|
||||
* Admin-Workstation mit lokal installierter SSH-Suite (`ssh`, `ssh-keygen`); für den bestehenden Host ist die lokale Konsole oder ein bereits berechtigter Zugang zur Schlüsselaufnahme nötig.
|
||||
|
||||
---
|
||||
|
||||
## 3. Prozessbeschreibung
|
||||
|
||||
### 3.1 Benutzerverwaltung & Sudo-Berechtigungen (Server-Seite)
|
||||
|
||||
#### 1. Sudo-Paket installieren & prüfen (falls noch nicht geschehen)
|
||||
|
||||
Als `root` am Server anmelden:
|
||||
|
||||
```bash
|
||||
su -
|
||||
apt update && apt install -y sudo
|
||||
```
|
||||
|
||||
#### 2. Benutzer anlegen & Berechtigungen vergeben
|
||||
|
||||
Vor dem Anlegen prüfen, ob das Konto bereits existiert (`getent passwd <username>`). Konten und Home-Verzeichnisse nur nach Prüfung ihrer Daten und Berechtigungen entfernen.
|
||||
|
||||
Nur bei einer **Neuinstallation** einen vorher festgelegten Administrationsbenutzer anlegen und zur `sudo`-Gruppe hinzufügen. Auf dem bestehenden KI-Server besitzen `ghost` und `debugofen` bereits sudo-Gruppenmitgliedschaft. Das folgende Beispiel mit `ghost` dort nicht ausführen:
|
||||
|
||||
```bash
|
||||
# Nur auf einem neuen System ausführen.
|
||||
sudo adduser ghost
|
||||
sudo usermod -aG sudo ghost
|
||||
```
|
||||
|
||||
Weitere Konten nur nach Rollenentscheidung anlegen. Die sudo-Gruppenmitgliedschaft ist pro Konto ausdrücklich festzulegen; auf dem bestehenden Host keine bereits vorhandenen Konten erneut anlegen.
|
||||
|
||||
Die folgenden technischen Konten werden ausschließlich durch das
|
||||
Ansible-Projekt verwaltet:
|
||||
|
||||
| Konto | UID/GID | Zweck | interaktiver Login / sudo |
|
||||
| --- | --- | --- | --- |
|
||||
| `ansible-deploy` | 1003 | privilegierte Ansible-Ausführung | SSH-Key, gesperrtes Passwort, `NOPASSWD` gemäß eigener sudoers-Regel |
|
||||
| `gnai-production` | 980 | rootless AI Production | gesperrtes Passwort, `/usr/sbin/nologin`, kein sudo |
|
||||
| `gnai-testing` | 981 | rootless AI Testing | gesperrtes Passwort, `/usr/sbin/nologin`, kein sudo |
|
||||
| `gn-encoding` | 982 | rootless CPU-Encoding | gesperrtes Passwort, `/usr/sbin/nologin`, kein sudo |
|
||||
|
||||
Die drei Workload-Konten besitzen nicht überlappende SubUID-/SubGID-Bereiche,
|
||||
eigene Daten- und Podman-Stores sowie persistente systemd-User-Manager. Ihre
|
||||
rootverwalteten Quadlet-Verzeichnisse liegen unter
|
||||
`/etc/containers/systemd/users/<UID>`. Direkte SSH-Schlüssel werden dort nicht
|
||||
hinterlegt. `gnai-production` und `gnai-testing` gehören zusätzlich zur
|
||||
Gerätegruppe `render`; `gn-encoding` erhält keinen GPU-Zugriff.
|
||||
|
||||
---
|
||||
|
||||
### 3.2 Statische Netzwerkkonfiguration (Server-Seite)
|
||||
|
||||
#### 1. Netzschnittstelle ermitteln & anpassen
|
||||
|
||||
Primäre Netzwerkschnittstelle und zuständigen Netzwerkdienst ermitteln (z. B. `enp8s0`):
|
||||
|
||||
```bash
|
||||
ip addr show
|
||||
systemctl is-active networking NetworkManager systemd-networkd dhcpcd
|
||||
```
|
||||
|
||||
Nur falls `ifupdown` den Host tatsächlich verwaltet, `/etc/network/interfaces` bearbeiten. Bei NetworkManager, systemd-networkd oder dhcpcd deren Konfiguration verwenden; die Schnittstelle darf nicht von mehreren Diensten zugleich verwaltet werden.
|
||||
|
||||
```bash
|
||||
sudo nano /etc/network/interfaces
|
||||
```
|
||||
|
||||
Netzwerkkonfiguration für statische IP eintragen:
|
||||
|
||||
```text
|
||||
# Loopback Interface
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
|
||||
# Primary Network Interface
|
||||
allow-hotplug enp8s0
|
||||
iface enp8s0 inet static
|
||||
address 10.0.0.90/24
|
||||
gateway 10.0.0.1
|
||||
dns-nameservers 10.0.0.1 1.1.1.1
|
||||
```
|
||||
|
||||
#### 2. DNS-Resolver prüfen
|
||||
|
||||
`/etc/resolv.conf` kann von einem Netzwerk- oder Resolverdienst erzeugt werden. Den aktiven Dienst und die wirksamen Nameserver prüfen (`readlink -f /etc/resolv.conf`, `cat /etc/resolv.conf`); Änderungen in dessen Konfiguration vornehmen. Die aktuelle Solladresse des Heimnetz-DNS ist `10.0.0.1`.
|
||||
|
||||
#### 3. Netzwerkdienst neustarten & Funktion prüfen
|
||||
|
||||
```bash
|
||||
sudo systemctl restart networking # nur bei ifupdown und mit Konsolenzugang
|
||||
ip -4 addr
|
||||
ping -c 4 google.com
|
||||
```
|
||||
|
||||
Vor einem Remote-Neustart einen Rückweg über Konsole/Proxmox sicherstellen. Ein Reboot behebt keine widersprüchliche Netzkonfiguration.
|
||||
|
||||
---
|
||||
|
||||
### 3.3 SSH-Schlüssel und getrennte Konten
|
||||
|
||||
Für persönliche und automatisierte Zugriffe getrennte Konten und klar benannte Schlüsselpaare verwenden. Auf dem bestehenden KI-Server sind `ghost` und `ghost-agent` getrennt; `ghost-agent` hat keine sudo-Gruppenmitgliedschaft. Schlüsseldateien, lokale SSH-Aliase und der auf dieser Workstation bekannte Hostschlüssel-Fingerprint sind nur im lokalen, nicht versionierten Arbeitskontext vermerkt. Private Schlüssel und Passphrasen gehören weder in diese Dokumentation noch in ein Serverinventar. Ein Kommentar wie `@ghostnet-ki` im Public Key ist nur ein Label; die Zuordnung erfolgt über den Schlüsselinhalt und das jeweilige `authorized_keys`.
|
||||
|
||||
Neue öffentliche Schlüssel auf dem bestehenden Host über eine bereits berechtigte Sitzung oder die lokale Konsole in das **richtige Konto** unter `~/.ssh/authorized_keys` eintragen; bestehende Schlüssel dabei erhalten. Verzeichnis auf `700`, Datei auf `600` und Eigentümer auf das Zielkonto setzen. Passwort-SSH ist bereits deaktiviert, daher ist `ssh-copy-id` ohne bestehenden Schlüsselzugang hier kein Wiederherstellungsverfahren.
|
||||
|
||||
Vor Erstverbindung den Hostschlüssel-Fingerprint aus einer vertrauenswürdigen Konsole abgleichen. Die lokalen SSH-Aliase müssen auf `10.0.0.90`, das jeweils richtige Konto und Schlüsseldatei zeigen; `IdentitiesOnly yes` und strikte Hostschlüsselprüfung verwenden. Die folgenden Beispiele setzen entsprechend eingerichtete Aliase auf der Admin-Workstation voraus:
|
||||
|
||||
```bash
|
||||
ssh ghostnet-ai-ghost 'id -un; hostnamectl --static'
|
||||
ssh ghostnet-ai-agent 'id -un; hostnamectl --static'
|
||||
```
|
||||
|
||||
Für `ghost` ist auf der aktuellen Workstation eine Passphrase-Eingabe am Terminal erforderlich, sofern der Schlüssel nicht bereits im SSH-Agenten entsperrt wurde. Beide Profile sind getestet: `ghost-agent` wurde am 19.09.2026 erneut technisch geprüft; der Betreiber bestätigte am selben Tag den erfolgreichen interaktiven Login als `ghost`.
|
||||
|
||||
---
|
||||
|
||||
### 3.4 SSH-Dienst härten (Server-Seite)
|
||||
|
||||
Nur auf einem **neu eingerichteten System** nach erfolgreichem SSH-Key-Test die Passwort-Authentifizierung deaktivieren. Auf dem bestehenden KI-Server ist das folgende Drop-in bereits vorhanden; es wurde am 18.09.2026 lesend geprüft.
|
||||
|
||||
#### 1. Drop-in-Konfiguration für SSH erstellen
|
||||
|
||||
```bash
|
||||
sudo nano /etc/ssh/sshd_config.d/99-security.conf
|
||||
```
|
||||
|
||||
Den folgenden Inhalt einfügen:
|
||||
|
||||
```text
|
||||
# Root-Login über SSH komplett verbieten
|
||||
PermitRootLogin no
|
||||
|
||||
# Passwort-Authentifizierung deaktivieren (nur SSH-Keys erlauben)
|
||||
PasswordAuthentication no
|
||||
KbdInteractiveAuthentication no
|
||||
|
||||
# Leere Passwörter verbieten
|
||||
PermitEmptyPasswords no
|
||||
|
||||
# Nur explizite SSH-Key Methoden erlauben
|
||||
PubkeyAuthentication yes
|
||||
```
|
||||
|
||||
#### 2. SSH-Konfiguration prüfen und Dienst neustarten
|
||||
|
||||
> **Achtung:** Die bestehende SSH-Sitzung **nicht** schließen, bevor der Login in einem zweiten Terminalfenster erfolgreich verifiziert wurde!
|
||||
|
||||
```bash
|
||||
# Syntaktische Prüfung der Konfiguration
|
||||
sudo sshd -t
|
||||
|
||||
# Wenn sshd -t keine Fehler ausgibt, Dienst neustarten
|
||||
sudo systemctl restart ssh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Abnahmekriterien (Checkliste für den Junior-Admin)
|
||||
|
||||
* [ ] **Benutzerprüfung:** `ghost` und `debugofen` können ihre vorgesehenen sudo-Rechte nutzen; `ghost-agent` bleibt ohne sudo-Zuweisung.
|
||||
* [x] **Workload-Konten:** `gnai-production`, `gnai-testing` und `gn-encoding` besitzen getrennte rootless Podman-Stores und aktive persistente User-Manager; geprüft am 20.09.2026.
|
||||
* [x] **GPU-Geräterechte:** Beide KI-Konten können `/dev/kfd`, `/dev/dri/renderD128` und `/dev/dri/renderD129` lesen und schreiben; das Encoding-Konto gehört nicht zu `render`; geprüft am 20.09.2026.
|
||||
* [ ] **Netzwerk:** Server ist unter der statischen IP `10.0.0.90` erreichbar. Ausgehender Datenverkehr funktioniert (`ping google.com`).
|
||||
* [x] **SSH-Aliase:** `ghostnet-ai-ghost` meldet `ghost`, `ghostnet-ai-agent` meldet `ghost-agent`. Beide Zugänge wurden am 19.09.2026 bestätigt. Die Passphrase des persönlichen Schlüssels ist keine Server-Passwortanmeldung.
|
||||
* [ ] **Root-Sperre:** Direct-Login als Root per SSH wird abgelehnt (`ssh root@10.0.0.90` -> *Permission denied*).
|
||||
* [ ] **Passwort-Sperre:** Login-Versuch ohne hinterlegten SSH-Key wird direkt abgelehnt (keine Passwort-Aufforderung mehr).
|
||||
|
||||
---
|
||||
|
||||
## 5. Lesender Ist-Abgleich am 18.09.2026
|
||||
|
||||
`ghost` (UID 1000) und `debugofen` (UID 1001) gehören zur Gruppe `sudo`; `ghost-agent` (UID 1002) gehört nicht dazu. Der technische Login als `ghost-agent` und der vom Betreiber interaktiv geprüfte Login als `ghost` funktionieren mit ihren jeweils eigenen Schlüsseln. Die Prüfung belegt die Kontentrennung und den Schlüsselzugang, aber keine noch offenen sudo-, Root-Sperr- oder Negativtests.
|
||||
|
||||
`enp8s0` hat `10.0.0.90/24`, die Default-Route führt über `10.0.0.1`. `networking` und `ssh` sind aktiv und aktiviert. `/etc/network/interfaces` enthält die statische Konfiguration; `/etc/resolv.conf` nennt `10.0.0.1` und `1.1.1.1` und trägt einen dhcpcd-Generierungskommentar. Ein aktuell laufender `dhcpcd`-Dienst wurde nicht beobachtet; vor Resolveränderungen die tatsächliche Verwaltung erneut prüfen. Das SSH-Drop-in deaktiviert Root-, Passwort- und Keyboard-Interactive-Anmeldung und aktiviert Public-Key-Anmeldung.
|
||||
Reference in New Issue
Block a user