Namespace nex: Brücke zu appsrv08, Zugang für nex-admin, Pull-Secret
ci / test (push) Failing after 22s
ci / test (push) Failing after 22s
Vorbereitung für Agenten im Cluster (nex-Spec 2026-10-19, D259/D263/D265): Services ohne Selector auf die Host-Ports von appsrv08, ServiceAccount mit Role nur für den Namespace nex, Pull-Secret aus einer Datei auf ops. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
02adb2f4b2
commit
31f6435c2f
@@ -10,6 +10,7 @@ Ausgerollt von ops mit `kubectl` (Rolle `k3s-apps`, Playbook `playbooks/k3s.yml`
|
||||
| `nex-speech/` | NEX-Sprachdienste auf gpu-pve52 (Playbook `playbooks/nex-speech-k3s.yml`) |
|
||||
| `nex-searxng/` | NEX-Websuche SearXNG, NodePort 30880 (Playbook `playbooks/nex-searxng-k3s.yml`), Einstellungen aus dem nex-Repo |
|
||||
| `portainer/` | Portainer CE 2.45.1 auf der Dienst-VM k3s, Datenbank von appsrv08 übernommen (Playbook `playbooks/portainer-k3s.yml` mit Rollback BE→CE), NodePort 30779 |
|
||||
| `nex/` | Namespace nex für Agenten im Cluster: Brücke zu den NEX-Diensten auf appsrv08 (Services ohne Selector), Zugang für nex-admin (ServiceAccount, Role), Pull-Secret Gitea (Playbook `playbooks/nex-k3s.yml`); nex-Spec 2026-10-19 |
|
||||
| `rustdesk/` | RustDesk-Server hbbs/hbbr, Service LoadBalancer 21115–21119 auf jeder Knoten-Adresse (Playbook `playbooks/rustdesk-k3s.yml`), Schlüssel im Secret `rustdesk-keys` |
|
||||
|
||||
Portainer: Umgebung „Kubernetes → Agent“, Adresse `<IP eines Knotens>:30778`.
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# Zugang von nex-admin (läuft auf appsrv08) zum Namespace nex: Agenten als Deployment/PVC/Secret/Service anlegen,
|
||||
# exec, Logs, Skalieren (nex-Spec 2026-10-19 Cluster-Agenten, D265). Kein cluster-admin, nur dieser Namespace.
|
||||
# Token: Secret nex-admin-token (Typ service-account-token, dauerhaft); playbooks/nex-k3s.yml legt es als Datei auf
|
||||
# ops ab (/root/.config/knaster/nex-admin-k8s.token) – von dort in die Stack-Variable NEX_ADMIN_K8S_TOKEN.
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: nex-admin
|
||||
namespace: nex
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: nex-admin
|
||||
namespace: nex
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: [pods, pods/log]
|
||||
verbs: [get, list, watch, create, delete]
|
||||
- apiGroups: [""]
|
||||
resources: [pods/exec]
|
||||
verbs: [create, get]
|
||||
- apiGroups: [""]
|
||||
resources: [services, persistentvolumeclaims, secrets]
|
||||
verbs: [get, list, create, update, patch, delete]
|
||||
- apiGroups: [""]
|
||||
resources: [endpoints, events]
|
||||
verbs: [get, list]
|
||||
- apiGroups: [apps]
|
||||
resources: [deployments, deployments/scale]
|
||||
verbs: [get, list, watch, create, update, patch, delete]
|
||||
- apiGroups: [metrics.k8s.io]
|
||||
resources: [pods]
|
||||
verbs: [get, list]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: nex-admin
|
||||
namespace: nex
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: nex-admin
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: nex-admin
|
||||
namespace: nex
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: nex-admin-token
|
||||
namespace: nex
|
||||
annotations:
|
||||
kubernetes.io/service-account.name: nex-admin
|
||||
type: kubernetes.io/service-account-token
|
||||
@@ -0,0 +1,87 @@
|
||||
# Brücke zu den NEX-Diensten auf appsrv08 (nex-Spec 2026-10-19 Cluster-Agenten, D259).
|
||||
# Die Agenten-Pods im Namespace nex sprechen dieselben Namen wie die Container im Docker-Netz llm-internal auf
|
||||
# appsrv08 (nex-router, nex-know, nex-access, grp-mcp-intern); hier zeigen Services ohne Selector mit festen
|
||||
# Endpoints auf die Host-Ports von appsrv08 (nur auf 192.168.100.29 veröffentlicht, Stacks nex und grp-mcp-intern).
|
||||
# Zieht ein Dienst in den Cluster (Weg 3), ersetzt sein echter Service den Eintrag – der Name bleibt.
|
||||
# LiteLLM bleibt ohne Brücke: Agenten sprechen nur den Router, der Router LiteLLM (nex AGENTS.md: kein Pfad an
|
||||
# LiteLLM vorbei, und kein zusätzlicher Host-Port dafür).
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nex-router
|
||||
namespace: nex
|
||||
labels: {knaster.app: nex-bridge}
|
||||
spec:
|
||||
ports:
|
||||
- {name: http, port: 4100, targetPort: 4100}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Endpoints
|
||||
metadata:
|
||||
name: nex-router
|
||||
namespace: nex
|
||||
labels: {knaster.app: nex-bridge}
|
||||
subsets:
|
||||
- addresses: [{ip: 192.168.100.29}]
|
||||
ports: [{name: http, port: 4100}]
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nex-know
|
||||
namespace: nex
|
||||
labels: {knaster.app: nex-bridge}
|
||||
spec:
|
||||
ports:
|
||||
- {name: http, port: 4200, targetPort: 4200}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Endpoints
|
||||
metadata:
|
||||
name: nex-know
|
||||
namespace: nex
|
||||
labels: {knaster.app: nex-bridge}
|
||||
subsets:
|
||||
- addresses: [{ip: 192.168.100.29}]
|
||||
ports: [{name: http, port: 4200}]
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nex-access
|
||||
namespace: nex
|
||||
labels: {knaster.app: nex-bridge}
|
||||
spec:
|
||||
ports:
|
||||
- {name: http, port: 4300, targetPort: 4300}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Endpoints
|
||||
metadata:
|
||||
name: nex-access
|
||||
namespace: nex
|
||||
labels: {knaster.app: nex-bridge}
|
||||
subsets:
|
||||
- addresses: [{ip: 192.168.100.29}]
|
||||
ports: [{name: http, port: 4300}]
|
||||
---
|
||||
# GRP-MCP (Rolle projektleitung): Stack grp-mcp-intern auf appsrv08, Dienst gateway, Container-Port 8790
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: grp-mcp-intern
|
||||
namespace: nex
|
||||
labels: {knaster.app: nex-bridge}
|
||||
spec:
|
||||
ports:
|
||||
- {name: http, port: 8790, targetPort: 8790}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Endpoints
|
||||
metadata:
|
||||
name: grp-mcp-intern
|
||||
namespace: nex
|
||||
labels: {knaster.app: nex-bridge}
|
||||
subsets:
|
||||
- addresses: [{ip: 192.168.100.29}]
|
||||
ports: [{name: http, port: 8790}]
|
||||
@@ -230,6 +230,7 @@ Die GPU-VMs bilden mit der Dienst-VM `k3s` einen k3s-Cluster (v1.36.5+k3s1). Ste
|
||||
- **NEX-Sprachdienste** laufen seit 15.10. als Deployments im Namespace `nex` mit Host-Netz, Ports 8178/8179 auf der VM-Adresse wie zuvor; ufw der VM gilt weiter (nur appsrv08). Umzugstest 15.10.: TTS → STT Rundlauf liefert den Text exakt, M4000 belegt, Compose-Stack ist angehalten (Rückweg: `kubectl -n nex delete deploy nex-stt nex-tts`, dann in der VM `docker compose -f /opt/nex-speech/docker-compose.yml start`). `playbooks/nex-speech.yml` bricht deshalb ab, außer mit `-e nex_speech_compose=true`.
|
||||
- **NEX-Websuche (SearXNG)** läuft seit 15.10. zusätzlich in k3s (Namespace `nex`, NodePort 30880 auf jeder Knoten-Adresse, `playbooks/nex-searxng-k3s.yml`, Einstellungen = Kopie aus dem nex-Repo `deploy/nex/searxng/settings.yml`). Der Dienst auf appsrv08 läuft parallel weiter, bis die Hermes-Agenten dort auf `SEARXNG_URL=http://<Knoten-IP>:30880` umgestellt sind (nex-Admin `onboarding`), danach den Dienst `searxng` im Portainer-Stack `nex` entfernen.
|
||||
- **Portainer CE im Cluster** (seit 15.10., Namespace `portainer-ce`, Pod auf der Dienst-VM k3s, Daten auf deren local-path, `https://192.168.100.124:30779`): Datenbank aus dem Volume `portainer_data` von appsrv08 (Portainer EE 2.45.1) übernommen, mit dem offiziellen Werkzeug `portainer-ee --rollback-to-ce` auf Community Edition zurückgesetzt (laut Portainer-Doku nur nach CE→BE-Upgrade möglich, hat bei der frischen BE-Installation trotzdem funktioniert; CE verweigert eine BE-Datenbank sonst mit Hinweis). Benutzer, Passwörter, API-Schlüssel und Umgebungen sind erhalten, der Schlüssel aus `it/.hermes.env` funktioniert unverändert. Sicherung `/root/.config/knaster/portainer_data.tgz` auf ops (15.10., 2,5 MB). **Umgebungen:** Nr. 4 „godel-local“ war der Docker-Socket von appsrv08 und ist im Cluster tot; auf appsrv08 läuft dafür `portainer_agent` (2.45.1, nur `192.168.100.29:9001`), den ein Admin als neue Umgebung „appsrv08“ (Agent) anlegt und für `godel-deployer` freigibt. Die Kubernetes-Umgebung (Nr. 5, Agent `:30778`) bleibt. **Noch auf appsrv08:** Portainer EE (`:9443`) läuft parallel weiter, nginx leitet `portainer.mygodel.de` dorthin; Umstellung = `proxy_pass` in `/var/docker/nginx/etc/nginx/conf.d/portainer.conf` auf `https://192.168.100.124:30779`, `nginx -t`, Reload (Runbook `IT Verbesserungen/GRP_Linux/Portainer-DNS-Runbook.md`), dann EE stoppen. Für nex-Admin und `.hermes.env` (URL `:9443`, Endpoint 4) zusätzlich Weiterleitung `:9443` → Cluster oder Werte anpassen. Lizenz: CE hat keine Knotengrenze.
|
||||
- **Agenten im Cluster (NEX)** (seit 19.10., nex-Spec `docs/specs/2026-10-19-nex-cluster-agenten-design.md` im nex-Repo): Neue Hermes-Agenten entstehen als Deployment im Namespace `nex`, angelegt von nex-admin auf appsrv08 über die Kubernetes-API (`playbooks/nex-k3s.yml`: ServiceAccount `nex-admin` mit Role nur für diesen Namespace, Token auf ops `/root/.config/knaster/nex-admin-k8s.token`, Pull-Secret `gitea-registry` aus `/root/.config/knaster/gitea-registry.env`). Die Pods erreichen die NEX-Dienste auf appsrv08 über Services ohne Selector (`deploy/k8s/nex/bridge.yaml`: nex-router 4100, nex-know 4200, nex-access 4300, grp-mcp-intern 8790 → `192.168.100.29`, dort als Host-Ports nur auf dieser Adresse veröffentlicht). Nutzeradressen bleiben `https://192.168.100.29:<port>/`: Traefik auf appsrv08 leitet an den NodePort `<port>+20000` weiter. Agenten-Volumes liegen auf `local-path` (an den ersten Knoten gebunden).
|
||||
- **RustDesk-Server** (ID-Server hbbs + Relay hbbr) läuft seit 15.10. zusätzlich in k3s (Namespace `rustdesk`, ein Pod auf einem GPU-Knoten, Datenbank auf `local-path`, Service-Typ LoadBalancer: Ports 21115–21119/tcp und 21116/udp auf **jeder** Knoten-Adresse). Das Schlüsselpaar ist dasselbe wie auf appsrv08 (Kopie auf ops `/root/.config/knaster/rustdesk/`, im Cluster als Secret `rustdesk-keys`), der Public Key der Clients `BPhsgjAI8yicpNRA3YsnBzfx78JUuNglrYZSZANVCUY=` bleibt gültig. hbbs nennt den Clients als Relay die Adresse `rustdesk_public_host` (Standard: k3s-VM, Variable im Playbook). **Umstellung der Clients steht aus:** sie tragen heute `192.168.100.29` als ID-Server; nötig sind eine feste Adresse (DHCP-Reservierung der k3s-VM oder DNS-Name am DC) und danach die neue Adresse in den installierten Clients sowie im Installer-Namen des Portainer-Stacks `rustdesk-updater` (`SERVER_HOST`). Bis dahin läuft der Stack `rustdesk` auf appsrv08 weiter.
|
||||
- **Images** aus Compose-Builds landen per `docker save | k3s ctr images import -` im containerd von k3s (Rolle nex-speech-k3s); kein Registry nötig. Für neue Dienste ohne Build reicht `image:` aus Docker Hub oder nvcr.io.
|
||||
- **ufw in den VMs:** die Rolle k3s-agent erlaubt 10250/tcp, 8472/udp, 30000–32767/tcp aus dem LAN und alles aus dem Pod-Netz 10.42.0.0/16.
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
# Namespace nex für Agenten im Cluster (nex-Spec 2026-10-19 Cluster-Agenten): Brücke zu appsrv08 (D259), Zugang für
|
||||
# nex-admin (D265), Pull-Secret für die Gitea-Registry (D263). Läuft auf ops: ansible-playbook playbooks/nex-k3s.yml
|
||||
#
|
||||
# Voraussetzung für das Pull-Secret: /root/.config/knaster/gitea-registry.env mit REGISTRY_USER=… und
|
||||
# REGISTRY_TOKEN=… (Gitea-Token mit read:package). Fehlt die Datei, wird dieser Schritt übersprungen.
|
||||
# Ausgabe: Token-Datei für nex-admin (nur Pfad), CA des Clusters als base64 (für NEX_ADMIN_K8S_CA_B64) und API-Adresse.
|
||||
- name: Namespace nex für Cluster-Agenten
|
||||
hosts: localhost
|
||||
connection: local
|
||||
gather_facts: false
|
||||
vars:
|
||||
manifests: "{{ playbook_dir }}/../../../../deploy/k8s/nex"
|
||||
registry_env: /root/.config/knaster/gitea-registry.env
|
||||
token_file: /root/.config/knaster/nex-admin-k8s.token
|
||||
api_url: "https://{{ hostvars['k3s'].ansible_host }}:6443"
|
||||
tasks:
|
||||
- name: Namespace nex
|
||||
ansible.builtin.command: kubectl apply -f {{ playbook_dir }}/../../../../deploy/k8s/nex-speech/namespace.yaml
|
||||
register: ns
|
||||
changed_when: "'created' in ns.stdout"
|
||||
|
||||
- name: Brücke zu appsrv08 (Services ohne Selector + Endpoints)
|
||||
ansible.builtin.command: kubectl apply -f {{ manifests }}/bridge.yaml
|
||||
register: bridge
|
||||
changed_when: "'configured' in bridge.stdout or 'created' in bridge.stdout"
|
||||
|
||||
- name: Zugang für nex-admin (ServiceAccount, Role, RoleBinding, Token-Secret)
|
||||
ansible.builtin.command: kubectl apply -f {{ manifests }}/admin-access.yaml
|
||||
register: access
|
||||
changed_when: "'configured' in access.stdout or 'created' in access.stdout"
|
||||
|
||||
- name: Token des ServiceAccounts lesen (Secret wird vom Controller befüllt)
|
||||
ansible.builtin.shell: >
|
||||
set -o pipefail;
|
||||
kubectl -n nex get secret nex-admin-token -o go-template='{{ "{{" }}.data.token{{ "}}" }}' | base64 -d
|
||||
args: {executable: /bin/bash}
|
||||
register: token
|
||||
until: token.stdout | length > 0
|
||||
retries: 10
|
||||
delay: 2
|
||||
changed_when: false
|
||||
no_log: true
|
||||
|
||||
- name: Token als Datei auf ops ablegen (600, nie ausgeben)
|
||||
ansible.builtin.copy:
|
||||
content: "{{ token.stdout }}"
|
||||
dest: "{{ token_file }}"
|
||||
mode: "0600"
|
||||
no_log: true
|
||||
|
||||
- name: Registry-Zugang vorhanden?
|
||||
ansible.builtin.stat:
|
||||
path: "{{ registry_env }}"
|
||||
register: reg
|
||||
|
||||
- name: Pull-Secret gitea-registry (aus {{ registry_env }})
|
||||
ansible.builtin.shell: >
|
||||
set -o pipefail; set -a; . {{ registry_env }}; set +a;
|
||||
kubectl -n nex create secret docker-registry gitea-registry --docker-server=gitea.mindboost.team
|
||||
--docker-username="$REGISTRY_USER" --docker-password="$REGISTRY_TOKEN" --dry-run=client -o yaml | kubectl apply -f -
|
||||
args: {executable: /bin/bash}
|
||||
register: pull
|
||||
changed_when: "'configured' in pull.stdout or 'created' in pull.stdout"
|
||||
when: reg.stat.exists
|
||||
no_log: true
|
||||
|
||||
- name: CA des Clusters (base64, eine Zeile)
|
||||
ansible.builtin.command: kubectl config view --raw --minify -o jsonpath='{.clusters[0].cluster.certificate-authority-data}'
|
||||
register: ca
|
||||
changed_when: false
|
||||
|
||||
- name: Brücke prüfen (Pod im Namespace nex löst die Namen auf)
|
||||
ansible.builtin.shell: >
|
||||
kubectl -n nex run bridge-check --rm -i --restart=Never --image=alpine:3.20 --
|
||||
sh -c 'for h in nex-router nex-know nex-access grp-mcp-intern; do nslookup $h >/dev/null 2>&1 && echo "$h dns ok" || echo "$h dns FEHLT"; done'
|
||||
register: check
|
||||
changed_when: false
|
||||
|
||||
- name: Ergebnis
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "Token für nex-admin: {{ token_file }} (NEX_ADMIN_K8S_TOKEN)"
|
||||
- "NEX_ADMIN_K8S_URL={{ api_url }}"
|
||||
- "NEX_ADMIN_K8S_CA_B64={{ ca.stdout }}"
|
||||
- "Pull-Secret gitea-registry: {{ 'angelegt/aktuell' if reg.stat.exists else 'übersprungen (' + registry_env + ' fehlt)' }}"
|
||||
- "{{ check.stdout_lines }}"
|
||||
Reference in New Issue
Block a user