From 83668557e563d748cc4280c7b6efd261495dcd01 Mon Sep 17 00:00:00 2001 From: spektr Date: Mon, 21 Sep 2026 12:40:17 +0200 Subject: [PATCH] =?UTF-8?q?GNAI-007:=20ROCm-Probelauf=20hinzugef=C3=BCgt.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 +- ansible/README.md | 48 ++++- ansible/playbooks/verify-rocm-container.yml | 11 + ansible/roles/rocm_container_probe/README.md | 17 ++ .../rocm_container_probe/defaults/main.yml | 12 ++ .../roles/rocm_container_probe/tasks/main.yml | 204 ++++++++++++++++++ docs/GNAI-000-DOC_Index.md | 16 +- docs/ai/GNAI-007-PLAN_Ansible-Deployment.md | 70 ++++-- 8 files changed, 355 insertions(+), 31 deletions(-) create mode 100644 ansible/playbooks/verify-rocm-container.yml create mode 100644 ansible/roles/rocm_container_probe/README.md create mode 100644 ansible/roles/rocm_container_probe/defaults/main.yml create mode 100644 ansible/roles/rocm_container_probe/tasks/main.yml diff --git a/README.md b/README.md index ea5bf02..560634e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Dokumentation für die Infrastruktur eines privaten Homelabs mit öffentlich erreichbaren Webdiensten, einem WireGuard-Zugang und einem dedizierten KI-Server. Dieses Repository hält Architekturentscheidungen, den belegten Betriebsstand und Verfahren für Einrichtung, Betrieb und Sicherheitsabnahme fest. -> **Projektstand: 20.09.2026.** Die Dokumentationsrevisionen sind zusammengeführt. Mehrere Sicherheitsmaßnahmen und technische Endabnahmen stehen noch aus. Auf dem KI-Server sind Host-Baseline, Podman, getrennte rootless Workload-Konten und der Gerätezugriff der KI-Konten auf beide AMD-GPUs eingerichtet; LLM- und Encoding-Anwendungen sind noch nicht installiert. +> **Projektstand: 20.09.2026.** Die Dokumentationsrevisionen sind zusammengeführt. Mehrere Sicherheitsmaßnahmen und technische Endabnahmen stehen noch aus. Auf dem KI-Server sind Host-Baseline, Podman, getrennte rootless Workload-Konten und der Gerätezugriff der KI-Konten auf beide AMD-GPUs eingerichtet. Ein gepinnter `llama.cpp`-ROCm-Build erkennt beide Karten im rootless Testing-Container; dauerhafte LLM- und Encoding-Anwendungen sind noch nicht installiert. ## Überblick @@ -26,7 +26,7 @@ Die drei vorgesehenen VPN-Netzwerkrollen sind **Familie** (gesamtes Heimnetz), * | Bereich | Belegter Stand | Nächster Nachweis oder Schritt | | --- | --- | --- | -| KI-Server | Debian-Basissystem, Deployment-Zugang, Podman 5.4.2, drei getrennte rootless Workload-Konten und geprüfter Zugriff beider KI-Konten auf `/dev/kfd` sowie zwei AMD-Render-Nodes | ROCm im rootless `llama.cpp`-Container prüfen und danach den Testing-Stack bereitstellen | +| KI-Server | Debian-Basissystem, Deployment-Zugang, Podman 5.4.2, drei getrennte rootless Workload-Konten sowie zwei als `gfx1030` und als `llama.cpp`-ROCm-Geräte geprüfte RX 6900 XT | `llama.cpp` als Testing-Quadlet mit geprüftem Modellprofil bereitstellen | | VPN | wg-easy v15.4.0 auf CT 105 aktiv; ein externer Laptop-Teiltest liegt vor | Rollenrechte serverseitig umsetzen und mit erlaubten sowie gesperrten Zielen testen | | Reverse Proxy | NPM auf CT 103 dokumentiert; Fail2Ban laut Betreiber fest gepinnt und wieder in Betrieb | Effektive Jails, Sperren und Wiederanlauf prüfen | | Identität | Keycloak/OIDC laut Betreiber produktiv für Vaultwarden | Öffentlich erreichbare Keycloak-Administration sperren und den OIDC-Login erneut prüfen | @@ -41,11 +41,11 @@ Der [Dokumentationsindex](docs/GNAI-000-DOC_Index.md) enthält die datierten Que | [Plattform-Dokumentation](docs/platform/README.md) | Hostbasis, Zugriffsmodell, Reverse Proxy und VPN | | [KI-Dokumentation](docs/ai/README.md) | KI-Zielarchitektur und nicht finale Ansible-Deployment-Planung | | [Video-Encoding](docs/video-encoding/GNENC-000-DOC_Index.md) | getrennter Planungs- und späterer SOP-Bereich für den CPU-Encoding-Workload | -| [Ansible-Control-Projekt](ansible/README.md) | lokale Einrichtung, Preflight, Deployment-Account, Host-Baseline, Podman, Workload-Konten und AMD-GPU-Zugriff | +| [Ansible-Control-Projekt](ansible/README.md) | lokale Einrichtung, Preflight, Deployment-Account, Host-Baseline, Podman, Workload-Konten, AMD-GPU-Zugriff und rootless ROCm-Probe | ## Arbeiten mit dem Repository -Dieses Repository enthält Dokumentation und ein Ansible-Control-Projekt. Es prüft Inventar, SSH-Zugang, Hostidentität, Betriebssystem und die dokumentierten GPUs. Gemeinsame Rollen verwalten Plattform, Podman und getrennte Workload-Identitäten. KI- und Encoding-Fachdokumentation bleiben in getrennten Bereichen; Images und Container sind noch nicht bereitgestellt. Vor einer Infrastrukturänderung den Ist-Stand am betroffenen Host prüfen, die passende Fachanleitung lesen und die dort genannten Abnahmeschritte dokumentieren. +Dieses Repository enthält Dokumentation und ein Ansible-Control-Projekt. Es prüft Inventar, SSH-Zugang, Hostidentität, Betriebssystem und die dokumentierten GPUs. Gemeinsame Rollen verwalten Plattform, Podman und getrennte Workload-Identitäten. KI- und Encoding-Fachdokumentation bleiben in getrennten Bereichen. Im Testing-Store liegt nur das gepinnte ROCm-Probeimage; dauerhafte Anwendungscontainer sind noch nicht bereitgestellt. Vor einer Infrastrukturänderung den Ist-Stand am betroffenen Host prüfen, die passende Fachanleitung lesen und die dort genannten Abnahmeschritte dokumentieren. WireGuard-Profile enthalten private Schlüssel. Sie gehören nicht in Git, Ausgaben oder freigegebene Dokumente. Das lokale Verzeichnis `docs/platform/GNAI-006-profiles/` schließt Profil-Dateien über eine eigene `.gitignore` aus; es ist kein Verteiler- oder Backup-Ort für Zugangsdaten. diff --git a/ansible/README.md b/ansible/README.md index 6809619..c3b7eb5 100644 --- a/ansible/README.md +++ b/ansible/README.md @@ -184,7 +184,47 @@ On the current host, the verified devices are `/dev/kfd`, remains outside that group. The final convergence run reported `ok=55 changed=0 unreachable=0 failed=0`. -This proves host-level device permissions only. No ROCm userspace image has -been pulled and no GPU workload has run yet. The next workload check must pass -these devices into a rootless container with `GroupAdd=keep-groups` and prove -that `llama.cpp` detects both cards as `gfx1030`. +This proves host-level device permissions. The separate container probe below +verifies the next layer without creating a permanent service. + +## Rootless ROCm container probe + +The dedicated verification playbook uses `gnai-testing` and an immutable amd64 +manifest of the official `llama.cpp` ROCm image: + +```text +version: b11058 +revision: f072b103714dfa1eee531f80b24512faf38e3dd2 +digest: sha256:b429b8be10d3d0353a09c98f4ab840fe516f779858a28f9fd4e5bdf10bc63cb4 +``` + +It passes `/dev/kfd` and both discovered render nodes with +`--group-add=keep-groups`. `rocminfo` must report exactly two `gfx1030` agents, +and `llama-server --list-devices` must expose exactly two ROCm devices. No +architecture override is set. + +Run the probe from the `ansible/` directory: + +```bash +../.venv/bin/ansible-playbook \ + --inventory inventories/host/hosts.yml \ + playbooks/verify-rocm-container.yml \ + --syntax-check + +../.venv/bin/ansible-playbook \ + --inventory inventories/host/hosts.yml \ + playbooks/verify-rocm-container.yml \ + --check \ + --private-key ~/.ssh/id_ed25519_ghostnet_ai_ansible_deploy + +../.venv/bin/ansible-playbook \ + --inventory inventories/host/hosts.yml \ + playbooks/verify-rocm-container.yml \ + --private-key ~/.ssh/id_ed25519_ghostnet_ai_ansible_deploy +``` + +The verified live run on 2026-09-20 reported +`ok=33 changed=0 unreachable=0 failed=0`. The ephemeral probe containers are +removed; the pinned image remains in the separate Testing image store for the +next deployment step. This proves ROCm discovery and the `llama.cpp` backend, +but not model loading, multi-GPU splitting or inference. diff --git a/ansible/playbooks/verify-rocm-container.yml b/ansible/playbooks/verify-rocm-container.yml new file mode 100644 index 0000000..7cf3a03 --- /dev/null +++ b/ansible/playbooks/verify-rocm-container.yml @@ -0,0 +1,11 @@ +--- +- name: Verify rootless ROCm access for the AI Testing account + hosts: llm_servers + become: true + gather_facts: true + gather_subset: + - min + + roles: + - role: amd_gpu_access + - role: rocm_container_probe diff --git a/ansible/roles/rocm_container_probe/README.md b/ansible/roles/rocm_container_probe/README.md new file mode 100644 index 0000000..3213016 --- /dev/null +++ b/ansible/roles/rocm_container_probe/README.md @@ -0,0 +1,17 @@ +# Rootless ROCm container probe + +This verification role pulls one immutable amd64 manifest of the official +`llama.cpp` `server-rocm` image into the `gnai-testing` rootless Podman store. +It verifies the OCI image revision and build version, then runs +`rocminfo` and `llama-server --list-devices` in ephemeral containers. + +The probe uses the same security-relevant options planned for the permanent +Quadlet: `/dev/kfd`, only the discovered AMD render nodes and +`GroupAdd=keep-groups`. It sets no graphics-architecture override, publishes no +port, mounts no model and removes the probe container after execution. The +pinned image remains in the Testing store for the subsequent `llama.cpp` +deployment. + +Success proves that ROCm sees two `gfx1030` agents and that `llama.cpp` exposes +both as usable ROCm devices to the rootless Testing container. It does not yet +prove model loading, multi-GPU splitting or inference. diff --git a/ansible/roles/rocm_container_probe/defaults/main.yml b/ansible/roles/rocm_container_probe/defaults/main.yml new file mode 100644 index 0000000..e3c18d5 --- /dev/null +++ b/ansible/roles/rocm_container_probe/defaults/main.yml @@ -0,0 +1,12 @@ +--- +rocm_probe_account: + name: gnai-testing + uid: 981 + home: /var/lib/ghostnet-ai/testing + +rocm_probe_image: >- + ghcr.io/ggml-org/llama.cpp@sha256:b429b8be10d3d0353a09c98f4ab840fe516f779858a28f9fd4e5bdf10bc63cb4 +rocm_probe_expected_image_revision: f072b103714dfa1eee531f80b24512faf38e3dd2 +rocm_probe_expected_image_version: b11058 +rocm_probe_expected_architecture: gfx1030 +rocm_probe_expected_device_count: 2 diff --git a/ansible/roles/rocm_container_probe/tasks/main.yml b/ansible/roles/rocm_container_probe/tasks/main.yml new file mode 100644 index 0000000..cbe332d --- /dev/null +++ b/ansible/roles/rocm_container_probe/tasks/main.yml @@ -0,0 +1,204 @@ +--- +- name: Verify ROCm probe declarations and discovered devices + ansible.builtin.assert: + that: + - rocm_probe_account.name | length > 0 + - rocm_probe_account.uid | int > 0 + - rocm_probe_account.home is match("^/") + - rocm_probe_image is match("^ghcr\.io/.+@sha256:[0-9a-f]{64}$") + - rocm_probe_expected_image_revision is match("^[0-9a-f]{40}$") + - rocm_probe_expected_image_version | length > 0 + - rocm_probe_expected_architecture is match("^gfx[0-9]+$") + - rocm_probe_expected_device_count | int > 0 + - amd_gpu_render_devices | length == rocm_probe_expected_device_count | int + fail_msg: ROCm probe inputs or AMD render-device discovery are invalid. + +- name: Read rootless Podman OCI runtime + ansible.builtin.command: + argv: + - /usr/sbin/runuser + - --user + - "{{ rocm_probe_account.name }}" + - -- + - /usr/bin/env + - "HOME={{ rocm_probe_account.home }}" + - "XDG_RUNTIME_DIR=/run/user/{{ rocm_probe_account.uid }}" + - /usr/bin/podman + - info + - --format=json + chdir: "{{ rocm_probe_account.home }}" + changed_when: false + check_mode: false + register: rocm_probe_podman_info_command + +- name: Parse rootless Podman OCI runtime + ansible.builtin.set_fact: + rocm_probe_podman_info: "{{ rocm_probe_podman_info_command.stdout | from_json }}" + +- name: Require crun for rootless keep-groups support + ansible.builtin.assert: + that: + - rocm_probe_podman_info.host.ociRuntime.name == "crun" + - rocm_probe_podman_info.host.security.rootless + fail_msg: The ROCm probe requires rootless Podman with the crun OCI runtime. + +- name: Check whether the pinned ROCm image is already present + ansible.builtin.command: + argv: + - /usr/sbin/runuser + - --user + - "{{ rocm_probe_account.name }}" + - -- + - /usr/bin/env + - "HOME={{ rocm_probe_account.home }}" + - "XDG_RUNTIME_DIR=/run/user/{{ rocm_probe_account.uid }}" + - /usr/bin/podman + - image + - exists + - "{{ rocm_probe_image }}" + chdir: "{{ rocm_probe_account.home }}" + changed_when: false + failed_when: rocm_probe_image_exists.rc not in [0, 1] + check_mode: false + register: rocm_probe_image_exists + +- name: Pull the pinned ROCm image into the Testing store + ansible.builtin.command: + argv: + - /usr/sbin/runuser + - --user + - "{{ rocm_probe_account.name }}" + - -- + - /usr/bin/env + - "HOME={{ rocm_probe_account.home }}" + - "XDG_RUNTIME_DIR=/run/user/{{ rocm_probe_account.uid }}" + - /usr/bin/podman + - pull + - "{{ rocm_probe_image }}" + chdir: "{{ rocm_probe_account.home }}" + changed_when: true + when: + - not ansible_check_mode + - rocm_probe_image_exists.rc == 1 + +- name: Inspect the pinned ROCm image + ansible.builtin.command: + argv: + - /usr/sbin/runuser + - --user + - "{{ rocm_probe_account.name }}" + - -- + - /usr/bin/env + - "HOME={{ rocm_probe_account.home }}" + - "XDG_RUNTIME_DIR=/run/user/{{ rocm_probe_account.uid }}" + - /usr/bin/podman + - image + - inspect + - --format=json + - "{{ rocm_probe_image }}" + chdir: "{{ rocm_probe_account.home }}" + changed_when: false + register: rocm_probe_image_inspect_command + when: not ansible_check_mode + +- name: Parse the pinned ROCm image metadata + ansible.builtin.set_fact: + rocm_probe_image_metadata: >- + {{ (rocm_probe_image_inspect_command.stdout | from_json) | first }} + when: not ansible_check_mode + +- name: Verify the pinned ROCm image metadata + ansible.builtin.assert: + that: + - >- + rocm_probe_image_metadata.Labels['org.opencontainers.image.revision'] == + rocm_probe_expected_image_revision + - >- + rocm_probe_image_metadata.Labels['org.opencontainers.image.version'] == + rocm_probe_expected_image_version + fail_msg: The pulled ROCm image metadata does not match the pinned build. + when: not ansible_check_mode + +- name: Detect both AMD GPU architectures through ROCm in a rootless container + ansible.builtin.command: + argv: >- + {{ + [ + '/usr/sbin/runuser', + '--user', rocm_probe_account.name, + '--', + '/usr/bin/env', + 'HOME=' + rocm_probe_account.home, + 'XDG_RUNTIME_DIR=/run/user/' + (rocm_probe_account.uid | string), + '/usr/bin/podman', + 'run', '--rm', '--pull=never', + '--group-add=keep-groups' + ] + + ((['/dev/kfd'] + amd_gpu_render_devices) + | map('regex_replace', '^', '--device=') | list) + + [ + '--entrypoint=/usr/bin/rocminfo', + rocm_probe_image + ] + }} + chdir: "{{ rocm_probe_account.home }}" + changed_when: false + register: rocm_probe_rocminfo_output + when: not ansible_check_mode + +- name: Verify ROCm reported both expected GPU architectures + ansible.builtin.assert: + that: + - >- + (rocm_probe_rocminfo_output.stdout + rocm_probe_rocminfo_output.stderr) + | regex_findall("(?m)^\\s*Name:\\s+" ~ rocm_probe_expected_architecture ~ "\\s*$") + | length == rocm_probe_expected_device_count | int + fail_msg: >- + rocminfo did not report the expected architecture for both AMD GPUs. + when: not ansible_check_mode + +- name: Detect both AMD GPUs through llama.cpp in a rootless container + ansible.builtin.command: + argv: >- + {{ + [ + '/usr/sbin/runuser', + '--user', rocm_probe_account.name, + '--', + '/usr/bin/env', + 'HOME=' + rocm_probe_account.home, + 'XDG_RUNTIME_DIR=/run/user/' + (rocm_probe_account.uid | string), + '/usr/bin/podman', + 'run', '--rm', '--pull=never', + '--group-add=keep-groups' + ] + + ((['/dev/kfd'] + amd_gpu_render_devices) + | map('regex_replace', '^', '--device=') | list) + + [rocm_probe_image, '--list-devices'] + }} + chdir: "{{ rocm_probe_account.home }}" + changed_when: false + register: rocm_probe_device_output + when: not ansible_check_mode + +- name: Verify llama.cpp reported both expected ROCm devices + ansible.builtin.assert: + that: + - >- + (rocm_probe_device_output.stdout + rocm_probe_device_output.stderr) + | regex_findall("(?m)^\s*ROCm[0-9]+:") + | unique | length == rocm_probe_expected_device_count | int + fail_msg: >- + The rootless llama.cpp ROCm image did not report both expected GPUs. + when: not ansible_check_mode + +- name: Report verified rootless ROCm devices + ansible.builtin.debug: + msg: + account: "{{ rocm_probe_account.name }}" + image: "{{ rocm_probe_image }}" + image_version: "{{ rocm_probe_expected_image_version }}" + image_revision: "{{ rocm_probe_expected_image_revision }}" + architecture: "{{ rocm_probe_expected_architecture }}" + devices: "{{ amd_gpu_render_devices }}" + when: not ansible_check_mode diff --git a/docs/GNAI-000-DOC_Index.md b/docs/GNAI-000-DOC_Index.md index 84d24b0..6b24a9a 100644 --- a/docs/GNAI-000-DOC_Index.md +++ b/docs/GNAI-000-DOC_Index.md @@ -47,7 +47,7 @@ Der KI-Server heißt laut lesendem SSH-Abgleich vom 18.09.2026 `ghostnet-ki`; da Die getrennten Schlüsselzugänge für `ghost` und `ghost-agent` wurden am 19.09.2026 erfolgreich getestet. Der persönliche Login wurde vom Betreiber interaktiv bestätigt; der Agentenlogin wurde technisch gegen Benutzername und Hostname geprüft. -Der KI-Server wurde als frisches Debian-Basissystem begonnen. Inzwischen sind die dokumentierten Ansible-Zugänge, die minimale Host-Baseline und Podman eingerichtet. Die LLM-Anwendungen sind weiterhin nicht installiert; GNAI-003 und GNAI-004 beschreiben für sie ein Zielbild. +Der KI-Server wurde als frisches Debian-Basissystem begonnen. Inzwischen sind die dokumentierten Ansible-Zugänge, die minimale Host-Baseline und Podman eingerichtet. Ein gepinntes ROCm-Probeimage liegt im rootless Testing-Store und hat beide GPUs erkannt; dauerhafte LLM-Anwendungen sind weiterhin nicht installiert. GNAI-003 und GNAI-004 beschreiben für sie ein Zielbild. Der [Ansible-Deployment-Entwurf](ai/GNAI-007-PLAN_Ansible-Deployment.md) persistiert den seit dem 19.09.2026 fortgeschriebenen Planungs- und Implementierungsstand. Er ist ausdrücklich keine verbindliche Spezifikation und kein Nachweis einer vollständig eingerichteten Deploymentkette. Die dort genannten Runner-, Freigabe- und Secret-Verfahren sind bis zu ihrer Prüfung und formalen Übernahme vorläufig. @@ -61,7 +61,9 @@ Die Rolle `podman_runtime` wurde am 20.09.2026 angewendet. Sie installierte Podm Die Rolle `workload_accounts` richtete am 20.09.2026 die gesperrten Konten `gnai-production` (UID/GID 980), `gnai-testing` (981) und `gn-encoding` (982) mit festen, getrennten SubID-Bereichen ein. Konfiguration bleibt rootverwaltet; Daten, rootless Podman-Stores und persistente systemd-User-Manager sind pro Workload getrennt. Der korrigierte Gesamtlauf meldete `ok=36 changed=0 unreachable=0 failed=0` und bestätigte drei unterschiedliche rootless Graph Roots. Images und Container wurden nicht angelegt. -Die Rolle `amd_gpu_access` bestätigte am 20.09.2026 zwei Sapphire-Navi-21-Geräte `1002:73bf` mit dem Kernel-Treiber `amdgpu`, `/dev/kfd` sowie die zugehörigen Render-Nodes `/dev/dri/renderD128` und `/dev/dri/renderD129`. Alle drei Geräte verwenden die Gruppe `render`. `gnai-production` und `gnai-testing` besitzen nachweislich Lese-/Schreibzugriff; `gn-encoding` erhielt keine GPU-Gruppe. Nach Beseitigung einer konkurrierenden Gruppenverwaltung endete der Gesamtlauf mit `ok=55 changed=0 unreachable=0 failed=0`. ROCm im Container und tatsächliche Inferenz sind damit noch nicht nachgewiesen. +Die Rolle `amd_gpu_access` bestätigte am 20.09.2026 zwei Sapphire-Navi-21-Geräte `1002:73bf` mit dem Kernel-Treiber `amdgpu`, `/dev/kfd` sowie die zugehörigen Render-Nodes `/dev/dri/renderD128` und `/dev/dri/renderD129`. Alle drei Geräte verwenden die Gruppe `render`. `gnai-production` und `gnai-testing` besitzen nachweislich Lese-/Schreibzugriff; `gn-encoding` erhielt keine GPU-Gruppe. Nach Beseitigung einer konkurrierenden Gruppenverwaltung endete der Gesamtlauf mit `ok=55 changed=0 unreachable=0 failed=0`. Dieser Rollenlauf allein wies ROCm im Container oder tatsächliche Inferenz noch nicht nach. + +Das getrennte Playbook `verify-rocm-container.yml` lud am 20.09.2026 für `gnai-testing` den offiziellen `llama.cpp`-ROCm-Build `b11058` als unveränderliches amd64-Manifest `sha256:b429b8be10d3d0353a09c98f4ab840fe516f779858a28f9fd4e5bdf10bc63cb4`. Mit `/dev/kfd`, beiden Render-Nodes und `keep-groups` meldete `rocminfo` genau zwei `gfx1030`-Agents; `llama-server --list-devices` meldete `ROCm0` und `ROCm1` als Radeon RX 6900 XT. Der abschließende Lauf endete mit `ok=33 changed=0 unreachable=0 failed=0`. Die Probecontainer wurden entfernt; dauerhafter Container-, Modell- und Inferenzbetrieb sind damit noch nicht abgenommen. ## 3. Sicherheitsbefunde mit Priorität @@ -116,4 +118,14 @@ deckte auf, dass `workload_accounts` ergänzende Gruppen wieder entfernte. Nach Der Abschlusslauf war mit `changed=0` idempotent; Images und Container blieben weiterhin leer. +Der anschließende rootless ROCm-Probelauf verwendete das Image ausschließlich +über seinen amd64-Manifest-Digest. Die OCI-Labels bestätigten Build `b11058` +und Revision `f072b103714dfa1eee531f80b24512faf38e3dd2`. Zwei kurzlebige +Container prüften mit denselben minimalen Geräteoptionen zunächst die +Architektur über `rocminfo` und anschließend die Geräteerkennung von +`llama-server`. Beide RX 6900 XT wurden als `gfx1030` und als getrennte +`ROCm0`-/`ROCm1`-Geräte erkannt. Der finale Wiederholungslauf meldete +`changed=0`; das Image bleibt im Testing-Store, laufende Container, Modelle +und Inferenz wurden nicht eingerichtet. + Die WireGuard-Evidenz bleibt der Teiltest vom 12.09.2026: CachyOS-Laptop von WAN, Handshake, Ping auf Gateway/Proxmox und direkter IPv4-Internetpfad bestanden; KI-Server war ausgeschaltet. Neue Rollen, weitere Clientvarianten, Gateway-GUI-Zugriff, WAN-/IPv6-Isolation, Neustart und Restore sind noch nicht abgenommen. GNAI-006 ist erst nach Klärung der offenen Werte, Umsetzung und dokumentierter Abnahme technisch abgeschlossen. diff --git a/docs/ai/GNAI-007-PLAN_Ansible-Deployment.md b/docs/ai/GNAI-007-PLAN_Ansible-Deployment.md index fe109fc..9b3a376 100644 --- a/docs/ai/GNAI-007-PLAN_Ansible-Deployment.md +++ b/docs/ai/GNAI-007-PLAN_Ansible-Deployment.md @@ -30,6 +30,7 @@ | 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 | --- @@ -122,7 +123,7 @@ 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` und `workload_accounts` vorhanden und angewendet; GPU- und Anwendungsrollen fehlen noch | +| 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 | @@ -391,8 +392,11 @@ 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` wird auf eine geprüfte - Version und den zugehörigen Digest festgelegt; +- 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; @@ -442,12 +446,13 @@ angebundenen Slot läuft. `row` wird wegen seiner Ablösung im aktuellen mehr Kommunikation zwischen den Karten und ist mit dem geplanten quantisierten V-KV-Cache derzeit nicht kompatibel. -Vor dem dauerhaften Start muss ein begrenzter Test `gfx1030`, beide GPUs, -Device-Reihenfolge, verfügbaren VRAM 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 51.200 Token Kontext -gemessen. Der 1:1-Wert ist erst nach diesem Test ein abgenommener -Betriebsparameter. +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 +51.200 Token Kontext gemessen. Der 1:1-Wert ist erst nach diesem Test ein +abgenommener Betriebsparameter. --- @@ -627,8 +632,9 @@ nicht als Nachweis einer erfolgreichen Einrichtung. - Podman- und Quadlet-Grundruntime sowie getrennte rootless Workload-Konten eingerichtet und geprüft; - rootless Host-Gerätezugriff auf beide RX 6900 XT eingerichtet und geprüft; -- ROCm-Funktion im rootless Container prüfen; -- `llama.cpp` und lokaler Funktionstest; +- ROCm-Funktion und `llama.cpp`-Geräteerkennung im rootless Testing-Container + mit gepinntem Image geprüft; +- `llama.cpp` mit Modell und lokalem Inferenztest; - Wiederanlauf nach geplantem Reboot prüfen. ### Phase E – Anwendungen und Veröffentlichung @@ -676,9 +682,9 @@ Vor der technischen Spezifikation sind mindestens folgende Punkte zu klären: Wiederanlauf von Production sicher, wenn Runner oder Testing ausfallen? 15. Welche internen Hostnamen und Ports erhält Testing, und welche LAN-/VPN- Rollen dürfen darauf zugreifen? -16. Funktioniert der benötigte AMD-GPU-Zugriff zuverlässig aus den getrennten - rootless Konten, und welche Gruppen-, Device- und Containeroptionen sind - dafür minimal erforderlich? +16. Muss der für Testing nachgewiesene AMD-GPU-Zugriff vor der ersten + Production-Freigabe mit demselben gepinnten Image separat unter + `gnai-production` abgenommen werden? 17. Wie wird ein Testing-Deployment eines Pull-Request-Commits in der eingesetzten Gitea-Version autorisiert, ohne Production-Secrets oder Production-Runner für veränderbaren PR-Code verfügbar zu machen? @@ -844,26 +850,48 @@ 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.11 sind der aktuelle Planungsstand, der ausdrücklich begrenzte +Mit Version 0.12 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 sowie deren begrenzter AMD-Gerätezugriff persistiert. +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 ROCm-Container- oder Anwendungsrolle, ein Production-Inventar oder ein +- eine dauerhafte `llama.cpp`-Anwendungsrolle, ein Production-Inventar oder ein Vault erstellt, - eine Production- oder Testing-Umgebung eingerichtet oder voneinander abgenommen, -- ROCm-Userspace, GPU-Container oder Anwendungen auf dem KI-Server installiert, +- 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 technische Schritt ist ein begrenzter rootless ROCm-Container-Test -für beide KI-Konten, der beide Karten als `gfx1030` erkennen muss. Darauf bauen -die `llama.cpp`-Rolle, das initiale Testing-Modellprofil und später LiteLLM auf. +Der nächste technische Schritt ist die `llama.cpp`-Rolle für Testing. Sie soll +das gepinnte Image als rootless Quadlet betreiben, das initiale Modell mit +geprüfter Checksumme einbinden und den vorgesehenen Layer-Split sowie eine +reale Inferenz abnehmen. Darauf baut anschließend LiteLLM auf.