docs: passe de cohérence documentaire (STT, Ghostfolio, embeddings, Qdrant) (#15)

Aligne la documentation sur l'état réel du cluster au 2026-06-18 :

- Qdrant : crash-loop résolu le 17/06 (statut « HS / EN COURS » corrigé dans
  CLAUDE.md, README, admin/ia/rag.md, admin/incidents.md, admin/ia/stt.md).
  Précision : collection funk-docs supprimée lors de la réparation → re-ingest
  RAG encore à faire (pas de surévaluation du statut).
- STT (assistant vocal « Jarvis ») : ajouté partout (était déployé mais non documenté).
- Ghostfolio : ajouté partout (était déployé mais non documenté).
- Embeddings dédiés nomic-embed-text :1238 : marqués opérationnels (roadmap
  README disait encore « TODO »).
- llama-server : ×3 → ×4 (instance embeddings) ; gap IaC précisé (embeddings géré
  par le rôle, seules les 2 instances CPU restent manuelles).
- Structure k8s/ corrigée dans CLAUDE.md : ajout stt/ + ghostfolio/, suppression
  des sous-dossiers traefik/ + metallb/ inexistants (installés via helm au bootstrap).
- Namespaces et liste des secrets Vault (vault_pg_ghostfolio_password) complétés.
- admin/README.md : index complété (~10 docs manquants : email, n8n, open-webui,
  ghostfolio, k9s, stt, rag, hermes-voice/souls/auto-improve, alertmanager-webhook).
- Funk/ (conception d'origine : LM Studio + agents Goose, obsolète) déplacé sous
  archive/ avec bandeau « obsolète » + archive/README.md (historique git préservé).


Claude-Session: https://claude.ai/code/session_017Qjq5jHiqNepnobJpHYpCa

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
ALI YESILKAYA 2026-06-18 11:16:34 +02:00 committed by GitHub
parent e1ec26d546
commit c023ab0e58
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 102 additions and 31 deletions

View file

@ -1,871 +0,0 @@
# Homelab IA — Architecture et plan de déploiement
> Document de référence consolidé pour le déploiement d'un homelab IA combinant
> un cluster Kubernetes (Talos) pour les services applicatifs et deux hôtes
> Linux dédiés pour l'inférence LLM (LM Studio) et l'agent autonome (Hermes Agent).
> Stack IaC complète : Ansible + talhelper + ArgoCD, GitOps de bout en bout.
> À utiliser comme contexte pour Claude Code.
---
## 1. Objectifs
- Faire tourner **Hermes Agent** (Nous Research) comme agent autonome
auto-amélioré, alimenté par **LM Studio** comme serveur d'inférence local.
- Apprendre Kubernetes en environnement réaliste (cluster multi-nœuds, GitOps)
pour les services applicatifs (Open WebUI, n8n, monitoring...).
- Apprendre Talos Linux (OS immuable, API-only, pensé pour Kubernetes).
- Maximiser l'utilisation du GPU AMD RX 6700XT pour l'inférence LLM.
- Tout gérer en Infrastructure as Code (versionné dans Git).
## 2. Priorités (par ordre)
1. Facilité d'administration
2. Apprentissage / montée en compétences
3. Performance brute (utiliser le 6700XT au max)
4. Haute disponibilité / résilience
## 3. Vision long terme
- Cible : ~10 machines à 2-3 ans, mix Talos (cluster k8s) + AlmaLinux (services natifs)
- Tout doit être reproductible depuis Git (réinstall complète possible en quelques heures)
- BlueBanquise reste une option à reconsidérer si on dépasse 25+ hôtes Linux classiques
ou si on ajoute du HPC/Slurm
---
## 4. Inventaire matériel
| Machine | CPU | RAM | Stockage | Réseau | Rôle |
|---|---|---|---|---|---|
| **storage-01** | AMD Ryzen 3 3200G (Zen+, 2019) | 32 GB DDR4 | 1× SSD OS + 4×1 TB RAID5 (2.7 TB exploitables) | 1× Gigabit | Hors cluster — passerelle + admin + stockage + Hermes Agent |
| **compute-01** | AMD A10-9700E (Excavator, 2016) | 16 GB DDR3 | SSD SATA | 1× Gigabit (r8169) | k8s control-plane (Talos) |
| **compute-02** | AMD A10-9700E | 8 GB DDR3 | SSD SATA | 1× Gigabit (r8169) | k8s worker (Talos) |
| **compute-03** | AMD A10-9700E | 8 GB DDR3 | SSD SATA | 1× Gigabit (r8169) | k8s worker (Talos) |
| **gpu-01** | AMD Ryzen 9 5950X (16 cœurs) | 32 GB DDR4 | NVMe 1 TB | 1× Gigabit | **Hors cluster** — hôte LM Studio dédié — RX 6700XT 12 GB |
| **switch** | Netgear GS308EV4 (8 ports Gigabit, managé Plus, VLAN 802.1Q) | — | — | — | Switch principal |
**Validation Talos déjà effectuée** : compute-01 boote correctement avec
Talos v1.13.0 en mode maintenance, IP DHCP obtenue, chipset r8169 reconnu nativement.
---
## 5. Architecture réseau
### 5.1. Topologie physique
```
Internet
┌───────────┐
│ Freebox │ 192.168.1.254 (gateway + DHCP LAN domestique)
└─────┬─────┘
│ VLAN 1 (port 1)
┌─────┴────────────────────────────────────────┐
│ Switch Netgear GS308EV4 │
│ P1 P2 P3 P4 P5 P6 P7 P8 │
│ V1 V1 V10 V10 V10 V10 V10 V1 │
└──┬───┬───┬───┬───┬───┬───┬───┬──────────────┘
│ │ │ │ │ │ │ └── PC perso (192.168.1.x)
│ │ │ │ │ │ └────── storage-01 NIC2 (192.168.10.1) ← LAN cluster
│ │ │ │ │ └────────── compute-03 (.10.13)
│ │ │ │ └────────────── compute-02 (.10.12)
│ │ │ └────────────────── compute-01 (.10.11)
│ │ └────────────────────── gpu-01 (.10.20)
│ └────────────────────────── storage-01 NIC1 (192.168.1.200) ← WAN
└────────────────────────────── Freebox
┌──────────────────────────────┐
│ storage-01 (AlmaLinux 9.7) │
│ - NIC1 (port 2) : 192.168.1.200 ← WAN / LAN domestique
│ - NIC2 (port 7) : 192.168.10.1 ← LAN cluster + gateway + NAT
└─────┬────────────────────────┘
│ (routage + NAT entre les deux interfaces)
┌────┴────┬──────────┬──────────┐
│ │ │ │
compute-01 compute-02 compute-03 gpu-01
.10.11 .10.12 .10.13 .10.20
(Talos) (Talos) (Talos) (AlmaLinux + LM Studio)
```
### 5.2. Vue logique des composants IA
```
┌──────────────────────────────────────────────────────────────────────┐
│ Cluster Kubernetes (Talos) │
│ 3 nœuds : compute-01/02/03 │
│ │
│ • Open WebUI • Traefik (ingress) │
│ • LiteLLM (proxy) • MetalLB (LB L2) │
│ • n8n (workflows) • cert-manager │
│ • Prometheus/Grafana • ArgoCD (GitOps) │
└──────────────────┬─────────────────────────┬─────────────────────────┘
│ │
│ NFS / S3 / DB │ HTTP /v1/chat/completions
▼ ▼
┌──────────────────────────────┐ ┌──────────────────────────────────┐
│ storage-01 (AlmaLinux 9.7) │ │ gpu-01 (AlmaLinux 9.7) │
│ 192.168.10.1 HORS CLUSTER │ │ 192.168.10.20 HORS CLUSTER │
│ │ │ │
│ Données : │ │ • LM Studio (mode serveur) │
│ • NFS (/srv/nfs/k8s) │ │ écoute sur :1234 │
│ • PostgreSQL │ │ • API OpenAI-compatible │
│ • Qdrant (vectoriel) │ │ • RX 6700XT via ROCm/Vulkan │
│ • MinIO (S3) │ │ • CLI `lms` pour pilotage │
│ │ │ │
│ Agent IA : │ │ │
│ • Hermes Agent ★ │◄──┤ Hermes appelle LM Studio │
│ (service systemd) │ │ via http://192.168.10.20:1234 │
│ │ │ │
│ Infra : │ │ │
│ • Passerelle / NAT │ │ │
│ • dnsmasq (lab.local) │ │ │
│ • Bastion admin │ │ │
└──────────────────────────────┘ └──────────────────────────────────┘
```
### 5.3. Plan d'adressage IP
**LAN domestique (existant, géré par la Freebox) — 192.168.1.0/24**
| IP | Hôte | Notes |
|---|---|---|
| `192.168.1.254` | Freebox | Gateway internet + DHCP |
| `192.168.1.10` | Machine 9950x3d (poste perso) | Déjà occupé |
| `192.168.1.200` | **storage-01** (interface WAN cluster) | IP statique réservée |
**LAN cluster (nouveau, isolé) — 192.168.10.0/24**
| IP | Hostname | Rôle |
|---|---|---|
| `192.168.10.1` | storage-01 | Gateway + admin + stockage + Hermes Agent (hors cluster k8s) |
| `192.168.10.11` | compute-01 | k8s control-plane (Talos, 16 GB) |
| `192.168.10.12` | compute-02 | k8s worker (Talos, 8 GB) |
| `192.168.10.13` | compute-03 | k8s worker (Talos, 8 GB) |
| `192.168.10.20` | gpu-01 | Hôte LM Studio dédié (AlmaLinux, hors cluster k8s) |
| `192.168.10.200-230` | (pool) | MetalLB — IP virtuelles des services k8s exposés |
**Ports applicatifs notables**
| Hôte | Port | Service | Notes |
|---|---|---|---|
| gpu-01 | 1234 | LM Studio API (OpenAI-compatible) | `http://192.168.10.20:1234/v1` |
| storage-01 | 22 | SSH (bastion + cible Ansible) | |
| storage-01 | 53 | dnsmasq | Résolution `*.lab.local` |
| storage-01 | 2049 | NFS | Exports `/srv/data/nfs/k8s` + `/srv/data/models` |
| storage-01 | 5432 | PostgreSQL | |
| storage-01 | 6333 | Qdrant HTTP | |
| storage-01 | 9000 | MinIO S3 | |
| storage-01 | 9001 | MinIO Console | |
| MetalLB | 80/443 | Traefik | Tous les Ingress du cluster |
| MetalLB | 8080 | ArgoCD UI | `argocd.lab.local` |
**Réseaux internes Kubernetes (ne pas modifier sauf raison spécifique)**
- Pods (Flannel) : `10.42.0.0/16`
- Services : `10.43.0.0/16`
### 5.4. Câblage du switch GS308EV4
Tous les ports en mode **Access** (pas de trunk). storage-01 utilise deux NICs physiques — une par VLAN.
| Port | Connecté à | VLAN | Mode |
|---|---|---|---|
| 1 | Freebox | 1 - Default | Access |
| 2 | storage-01 NIC1 (WAN, 192.168.1.200) | 1 - Default | Access |
| 3 | gpu-01 (192.168.10.20) | 10 - cluster-funk | Access |
| 4 | compute-01 (192.168.10.11) | 10 - cluster-funk | Access |
| 5 | compute-02 (192.168.10.12) | 10 - cluster-funk | Access |
| 6 | compute-03 (192.168.10.13) | 10 - cluster-funk | Access |
| 7 | storage-01 NIC2 (LAN cluster, 192.168.10.1) | 10 - cluster-funk | Access |
| 8 | PC perso (192.168.1.x) | 1 - Default | Access |
**Avantage vs trunk** : pas de subinterface `eno1.10` à configurer — deux interfaces physiques classiques dans NetworkManager. Plus simple, moins de risque d'erreur au démarrage.
### 5.5. Route statique sur la Freebox
À configurer dans *Paramètres Freebox > Mode avancé > Configuration de routage* :
```
Réseau de destination : 192.168.10.0
Masque : 255.255.255.0
Passerelle : 192.168.1.200
```
Cela permet à toutes les machines du LAN domestique de joindre directement le LAN cluster.
---
## 6. Choix d'OS
| Machine | OS | Justification |
|---|---|---|
| storage-01 | **AlmaLinux 9.7 minimal** | Stack RHEL maîtrisée, stabilité, support sécurité jusqu'en 2032, doc abondante NFS/nftables/MinIO/Postgres |
| compute-01 | **Talos Linux v1.13+** | Choix d'apprentissage assumé, validation matérielle OK |
| compute-02 | **Talos Linux v1.13+** | idem |
| compute-03 | **Talos Linux v1.13+** | idem |
| gpu-01 | **AlmaLinux 9.7 minimal** | Cohérence RHEL avec storage-01, ROCm via repo officiel AMD pour RHEL 9 |
**Note sur AlmaLinux + ROCm pour gpu-01** : la majorité de la doc communautaire
ROCm + RX 6700XT vise Ubuntu. Sur AlmaLinux 9, utiliser le repo officiel AMD
pour RHEL 9 (`https://repo.radeon.com/rocm/`). Adapter les commandes dnf au
lieu d'apt. En cas de blocage ROCm, Vulkan reste un fallback fonctionnel pour LM Studio.
---
## 7. Architecture applicative
### 7.1. Topologie cluster Kubernetes
- **Control-plane** : single (compute-01 uniquement)
- **Workers** : compute-02, compute-03
- **storage-01 et gpu-01 sont HORS du cluster**
### 7.2. Découpage des responsabilités
**gpu-01 — Hôte d'inférence LLM (hors cluster)**
- LM Studio en mode serveur headless
- API OpenAI-compatible exposée sur `:1234`
**storage-01 — Bastion admin + données + agent (hors cluster)**
- Passerelle réseau (NAT, routage VLAN)
- Bastion : `talosctl`, `kubectl`, `helm`, `argocd` CLI, `ansible`
- Services données : NFS, PostgreSQL, Qdrant, MinIO
- **Hermes Agent** en service systemd
- dnsmasq pour la résolution `*.lab.local`
**Cluster Kubernetes (Talos, 3 nœuds)**
- Couche infra : MetalLB, Traefik, cert-manager, Longhorn, nfs-subdir-external-provisioner
- Couche observabilité : kube-prometheus-stack, Loki
- Couche applicative IA : Open WebUI, LiteLLM, n8n
- **GitOps via ArgoCD**
### 7.3. Flux principal Hermes ↔ LM Studio
```
Utilisateur → CLI Hermes / Telegram / autre interface
(sur storage-01)
Hermes Agent (skills, mémoire, tools)
[storage-01 :daemon systemd]
│ HTTP POST /v1/chat/completions
LM Studio (modèle chargé en VRAM 6700XT)
[gpu-01 :1234]
│ Réponse streaming
Hermes Agent (parsing tool calls, exécution)
Utilisateur
```
### 7.4. Deux setups IA — poste perso vs cluster Funk
#### Setup 1 — Poste perso (9950X3D) — dev local
**Matériel** : Ryzen 9 9950X3D · 32 GB DDR5 · RX 9070 XT 16 GB VRAM
| Composant | Config |
|---|---|
| LM Studio | `http://127.0.0.1:1234/v1` (bind localhost par défaut) |
| Ollama | `http://127.0.0.1:11434` (aussi installé) |
| Hermes Agent | config `base_url: http://127.0.0.1:1234/v1` |
| Goose agents | `Funk/Funk-infra/agents/` → pointent sur `http://localhost:1234/v1` |
**Démarrage LM Studio :**
```bash
# Charger le modèle avec un context suffisant (système Hermes ≈ 15 000 tokens)
lms load <modele> --context-length 16384
lms server start --port 1234
```
**Config Hermes (`~/.hermes/config.yaml`) :**
```yaml
model:
default: <nom-modele>
provider: lmstudio
base_url: http://127.0.0.1:1234/v1
context_length: 16384
```
**Goose agents :**
```bash
cd Funk/Funk-infra/agents/<nom-agent>
goose session --config goose-config.yaml
```
| Agent Goose | Modèle cible | Usage |
|---|---|---|
| `global` | qwen2.5-14b | Raisonnement général |
| `infra` | qwen2.5-coder-14b | k8s, Talos, Helm, ArgoCD |
| `sysadmin` | qwen2.5-7b | Admin système, diagnostics |
| `monitoring` | llama3.2-3b | Logs, métriques |
| `data` | qwen2.5-14b | Analyse de données |
---
#### Setup 2 — Cluster Funk (production)
**Matériel GPU** : gpu-01, Ryzen 9 5950X · 32 GB DDR4 · RX 6700XT 12 GB VRAM
| Composant | Config |
|---|---|
| LM Studio | `http://192.168.10.20:1234/v1` (bind `0.0.0.0`) |
| Hermes Agent | service systemd sur storage-01 |
| Hermes `base_url` | `http://192.168.10.20:1234/v1` |
**Démarrage LM Studio sur gpu-01 :**
```bash
HSA_OVERRIDE_GFX_VERSION=10.3.0 lms load <modele> --context-length 16384
lms server start --port 1234 --bind 0.0.0.0
```
**Config Hermes sur storage-01 (`~/.hermes/config.yaml`) :**
```yaml
model:
default: <nom-modele>
provider: lmstudio
base_url: http://192.168.10.20:1234/v1
context_length: 16384
```
**Différence clé avec setup 1** : `HSA_OVERRIDE_GFX_VERSION=10.3.0` obligatoire pour la RX 6700XT (gfx1031 non officiellement supporté par ROCm). Sur la RX 9070 XT du poste perso, cette variable n'est pas nécessaire.
---
## 8. Stack IaC / Automatisation
### 8.1. Principe : un seul repo Git, trois flux de déploiement
Toute l'infrastructure est gérée en GitOps depuis un repo Git unique. Trois outils
se partagent les responsabilités selon la cible :
```
┌──────────────────────────────────────────────────────────────────┐
│ Repo Git du lab (sur poste admin + remote Gitea/GitHub privé) │
│ │
│ lab-infra/ │
│ ├── ansible/ ← gère storage-01 + gpu-01 │
│ │ ├── inventory.yml │
│ │ ├── playbooks/ │
│ │ ├── roles/ │
│ │ └── group_vars/ │
│ ├── talos/ ← configs Talos via talhelper │
│ │ ├── talconfig.yaml │
│ │ ├── talsecret.sops.yaml │
│ │ └── clusterconfig/ (généré par talhelper) │
│ ├── k8s/ ← manifests / Helm values │
│ │ ├── apps/ ← apps déployées par ArgoCD │
│ │ ├── infra/ ← MetalLB, Traefik, cert-manager... │
│ │ └── argocd-bootstrap/ ← Application of Applications │
│ └── docs/ │
│ └── HOMELAB.md ← ce document │
└────────┬─────────────────────────┬────────────────────┬──────────┘
│ │ │
│ ansible-playbook │ talosctl │ git push
│ (push manuel SSH) │ apply-config │ → ArgoCD
│ │ (push manuel) │ pull auto
▼ ▼ ▼
storage-01, compute-01/02/03 Workloads dans
gpu-01 (Talos) le cluster k8s
(AlmaLinux)
```
### 8.2. Outil 1 — Ansible (vanilla) pour les hôtes Linux classiques
**Cible** : storage-01 et gpu-01 (et futures machines AlmaLinux/Debian).
**Choix** : Ansible vanilla, **rôles écrits from scratch**. Pas de framework type
BlueBanquise — surdimensionné pour 5-10 hôtes Linux à terme. À reconsidérer si
on dépasse 25+ hôtes ou si on ajoute du HPC/Slurm.
**Structure des rôles prévus** :
```
ansible/
├── ansible.cfg
├── inventory.yml
├── group_vars/
│ ├── all.yml # variables communes (DNS, NTP, users)
│ ├── gateway.yml # variables passerelle
│ └── gpu_hosts.yml # variables hôtes GPU
├── host_vars/
│ ├── storage-01.yml
│ └── gpu-01.yml
├── playbooks/
│ ├── site.yml # playbook racine
│ ├── storage-01.yml
│ └── gpu-01.yml
└── roles/
├── common/ # paquets de base, sshd, sudoers, motd
├── gateway/ # NetworkManager VLAN, IP forwarding, nftables, NAT
├── dnsmasq/ # DNS local *.lab.local
├── nfs_server/ # NFS server + exports
├── postgresql/ # Postgres + DB init
├── qdrant/ # Qdrant via Podman
├── minio/ # MinIO via Podman
├── rocm/ # repo AMD + drivers + HSA_OVERRIDE
├── lm_studio/ # install + service systemd
└── hermes_agent/ # install + config + systemd unit
```
**Inventaire** :
```yaml
# ansible/inventory.yml
all:
children:
gateway:
hosts:
storage-01:
ansible_host: 192.168.10.1
gpu_hosts:
hosts:
gpu-01:
ansible_host: 192.168.10.20
almalinux:
children:
gateway:
gpu_hosts:
```
**Lancement type** :
```bash
cd ansible/
ansible-playbook -i inventory.yml playbooks/site.yml --check # dry-run
ansible-playbook -i inventory.yml playbooks/site.yml # apply
ansible-playbook -i inventory.yml playbooks/site.yml --tags rocm # cibler 1 rôle
```
**Bonnes pratiques** :
- Tous les rôles **idempotents** (peuvent être ré-exécutés sans casse).
- Variables sensibles dans **Ansible Vault** (mot de passe Postgres, clés API).
- Un playbook = une machine ou un groupe, pas de "playbook fourre-tout".
- Tags systématiques pour cibler un rôle spécifique.
### 8.3. Outil 2 — talhelper pour les nœuds Talos
**Cible** : compute-01, compute-02, compute-03.
**Pourquoi talhelper** : `talosctl gen config` brut produit des configs longues
et difficiles à maintenir. `talhelper` lit un seul fichier `talconfig.yaml`
clair et génère les configs YAML par nœud, avec gestion des secrets (chiffrement
SOPS) et patches par nœud.
**Structure** :
```
talos/
├── talconfig.yaml # config déclarative (lue par talhelper)
├── talsecret.sops.yaml # secrets chiffrés (token bootstrap, certs CA)
├── talenv.yaml # version Talos, version k8s
├── patches/ # patches YAML par nœud ou groupe
│ ├── compute-01.yaml
│ └── all-controlplane.yaml
└── clusterconfig/ # généré : ne PAS commit (au .gitignore)
├── lab-compute-01.yaml
├── lab-compute-02.yaml
└── lab-compute-03.yaml
```
**Workflow** :
```bash
cd talos/
# 1. Éditer talconfig.yaml (ajout d'un nœud, changement de version k8s, etc.)
vim talconfig.yaml
# 2. Régénérer les configs nœud
talhelper genconfig
# 3. Appliquer sur un nœud spécifique
talhelper gencommand apply --node compute-02 | bash
# 4. Bootstrap (uniquement la première fois, sur le control-plane)
talhelper gencommand bootstrap --node compute-01 | bash
# 5. Récupérer le kubeconfig
talhelper gencommand kubeconfig | bash
```
**Avantage GitOps** : le `talconfig.yaml` est ta source de vérité. N'importe qui
(toi dans 6 mois) peut lire ce fichier et comprendre l'état du cluster Talos
sans avoir à inspecter les nœuds.
### 8.4. Outil 3 — ArgoCD pour les workloads Kubernetes
**Cible** : tout ce qui tourne dans le cluster k8s (apps + infra k8s).
**Principe** : ArgoCD surveille en continu un dossier Git et garantit que l'état
réel du cluster correspond à ce qui est déclaré dans Git.
**Pattern recommandé : "App of Apps"**
```
k8s/
├── argocd-bootstrap/ # bootstrap ArgoCD lui-même
│ └── argocd-install.yaml
├── apps-of-apps/
│ └── root.yaml # une Application qui pointe sur apps/
├── infra/ # déployé d'abord
│ ├── metallb/
│ │ ├── application.yaml
│ │ └── values.yaml
│ ├── traefik/
│ ├── cert-manager/
│ ├── longhorn/
│ └── nfs-provisioner/
└── apps/ # déployé ensuite
├── litellm/
│ ├── application.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ └── ingress.yaml
├── open-webui/
├── n8n/
├── monitoring/ # kube-prometheus-stack
└── external-services/ # ExternalName vers storage-01
├── postgresql-external.yaml
├── qdrant-external.yaml
└── lm-studio-external.yaml # ExternalName vers gpu-01:1234
```
**Workflow d'utilisation** :
```bash
# 1. Modifier un manifest
vim k8s/apps/open-webui/deployment.yaml
# 2. Commit + push
git add k8s/apps/open-webui/
git commit -m "open-webui: bump to v0.4.0, add OIDC config"
git push
# 3. ArgoCD détecte le changement (poll par défaut toutes les 3 min)
# → applique automatiquement dans le cluster
# → visible dans l'UI argocd.lab.local
```
**Bénéfices** :
- Source de vérité unique (Git)
- Historique complet via `git log`
- Rollback en `git revert` + push
- Recréation totale du cluster en cas de désastre : install Talos via talhelper,
install ArgoCD via le bootstrap, ArgoCD redéploie tout le reste tout seul
### 8.5. Récapitulatif des responsabilités
| Outil | Cible | Mode de déploiement | Stockage source |
|---|---|---|---|
| **Ansible** | Hôtes Linux classiques (storage-01, gpu-01, futures Alma) | Push manuel (`ansible-playbook`) | `lab-infra/ansible/` |
| **talhelper** | Nœuds Talos (compute-01/02/03) | Push manuel (`talosctl apply-config`) | `lab-infra/talos/` |
| **ArgoCD** | Workloads Kubernetes (apps + infra k8s) | Pull automatique depuis Git | `lab-infra/k8s/` |
### 8.6. Repo Git : où l'héberger ?
**Court terme** : repo local sur ton poste + remote sur **GitHub** (privé) ou
**GitLab** (privé) → simple et fiable.
**Moyen terme (recommandé)** : héberger un **Gitea** ou **Forgejo** sur
storage-01 (rôle Ansible dédié) + miroir GitHub pour backup. Avantages :
- Indépendance totale (pas besoin d'Internet pour déployer)
- Apprentissage de l'auto-hébergement Git
- ArgoCD pointe sur `git.lab.local/admin/lab-infra.git`
### 8.7. Gestion des secrets
**Pas de secrets en clair dans Git, jamais.** Trois mécanismes selon la couche :
- **Ansible** : Ansible Vault pour les secrets (`ansible-vault encrypt_string`)
- **Talos** : SOPS + age (intégré nativement à talhelper)
- **Kubernetes** : Sealed Secrets (Bitnami) ou External Secrets Operator
pointant sur un Vault — pour démarrer, Sealed Secrets est plus simple
---
## 9. Feuille de route par phases
Chaque phase est validable indépendamment. Ne passer à la suivante que quand
la précédente fonctionne.
### Phase 0 — Préparation
- [x] Validation matérielle Talos sur 1 ThinkCentre (compute-01) ✅ effectué
- [ ] Vérifier que tous les ThinkCentre ont des SSD SATA opérationnels
- [ ] Préparer 1 clé USB pour Talos + 1 clé USB pour AlmaLinux
- [ ] Initialiser le repo Git `lab-infra/` sur le poste admin
### Phase 1 — Infra réseau (storage-01 manuel + bootstrap Ansible)
- [x] Installation AlmaLinux sur storage-01 ✅
- [x] Configuration IP de base — storage-01 joignable (192.168.10.1 LAN cluster) ✅
- [x] IP WAN storage-01 fixée à 192.168.1.200 ✅
- [x] Configuration VLAN sur le GS308EV4 ✅ — tous ports Access, storage-01 sur deux NICs physiques
- [x] `lab-infra/ansible/` initialisé — inventory, rôles common + gateway ✅
- [x] nftables/NAT/IP forwarding opérationnels ✅
- [x] Rôle `dnsmasq` — résolution `*.lab.local`
- [ ] Route statique sur la Freebox (LAN domestique → LAN cluster)
- [ ] Validation ping bidirectionnel LAN domestique ↔ LAN cluster
### Phase 2 — Hôte gpu-01 + LM Studio (Ansible)
- [x] Installation AlmaLinux sur gpu-01 ✅ — ping OK depuis storage-01
- [x] Ajout de gpu-01 à l'inventaire Ansible ✅
- [x] Rôle `rocm` : repo AMD + drivers + `HSA_OVERRIDE_GFX_VERSION=10.3.0`
- [x] Validation ROCm : RX 6700XT détectée ✅
- [x] Rôle `lm_studio` : install + service systemd + bind `0.0.0.0:1234`
- [x] Téléchargement modèles : gemma-4-e2b, gemma-4-e4b ✅
- [x] Validation : `curl http://192.168.10.20:1234/v1/models`
### Phase 3 — Hermes Agent sur storage-01 (Ansible)
- [ ] Rôle `hermes_agent` : install via script officiel
- [ ] Configuration provider custom endpoint (LM Studio gpu-01)
- [ ] Premier `hermes chat` interactif
- [ ] (Optionnel) Service systemd pour daemon background
- [ ] Données persistantes sur le RAID5 (`/srv/data/hermes`)
### Phase 4 — Stack données sur storage-01 (Ansible)
- [x] Rôle `nfs_server` : RAID5 `md127` monté `/srv/data`, exports NFS ✅
- [x] Rôle `nfs_client` sur gpu-01 — automount `/mnt/nfs` + `/mnt/models`
- [x] Modèles LM Studio migrés sur NFS (`/srv/data/models`) ✅
- [ ] Rôle `postgresql` : install + DB init
- [ ] Rôle `qdrant` : Podman + service systemd
- [ ] Rôle `minio` : Podman + service systemd
- [ ] Validation : tous les services accessibles depuis le LAN cluster
### Phase 5 — Cluster Talos (talhelper)
- [ ] Génération image Talos via factory.talos.dev (extensions `iscsi-tools`)
- [ ] Init `lab-infra/talos/` avec `talconfig.yaml` (3 nœuds)
- [ ] `talhelper genconfig`
- [ ] Apply sur compute-01 (control-plane) puis bootstrap etcd
- [ ] Apply sur compute-02 et compute-03 (workers)
- [ ] Validation : `kubectl get nodes` montre 3 nœuds Ready
### Phase 6 — Bootstrap ArgoCD + infra k8s (GitOps)
- [ ] Install ArgoCD manuel via Helm (une seule fois, l'install ArgoCD reste manuelle)
- [ ] Init `lab-infra/k8s/` avec App of Apps pattern
- [ ] Création des Applications ArgoCD pour : MetalLB, Traefik, cert-manager
- [ ] Validation : pod nginx test exposé via Ingress, accessible depuis le LAN
### Phase 7 — Stockage k8s (ArgoCD)
- [ ] Application ArgoCD : `nfs-subdir-external-provisioner` pointant sur storage-01
- [ ] Application ArgoCD : Longhorn (avec `iscsi-tools` extension Talos)
- [ ] Validation : 1 PVC NFS + 1 PVC Longhorn créés et montés
### Phase 8 — Apps IA (ArgoCD)
- [ ] Applications ArgoCD : ExternalName vers Postgres, Qdrant, LM Studio
- [ ] Application ArgoCD : LiteLLM (proxy vers LM Studio)
- [ ] Application ArgoCD : Open WebUI (front, pointe sur LiteLLM)
- [ ] Application ArgoCD : n8n
- [ ] Application ArgoCD : kube-prometheus-stack + Loki
- [ ] Validation finale : workflow n8n → Open WebUI → LiteLLM → LM Studio
### Phase 9 — Optionnel : Gitea local + miroir
- [ ] Rôle Ansible `gitea` sur storage-01
- [ ] Migration du repo `lab-infra` vers Gitea local
- [ ] Reconfiguration ArgoCD pour pointer sur `git.lab.local`
- [ ] Miroir push vers GitHub privé (sauvegarde)
---
## 10. Points d'attention spécifiques
### 10.1. RX 6700XT et ROCm sur AlmaLinux 9
- gfx1031 non officiellement supporté par ROCm
- **Variable obligatoire** : `HSA_OVERRIDE_GFX_VERSION=10.3.0`
- ROCm s'exécute directement sur l'OS hôte (gpu-01), pas dans des conteneurs
- Fallback Vulkan possible dans LM Studio si ROCm coince
- Doc ROCm pour RHEL 9 : https://rocm.docs.amd.com/projects/install-on-linux/
### 10.2. LM Studio en mode serveur
**Installation** : script officiel `https://lmstudio.ai/install.sh` — nécessite un patch ELF
sur `watcher.node` (module Node natif compilé GCC 12) car AlmaLinux 9 / GCC 11 ne fournit
que `GLIBCXX_3.4.29` alors que le binaire requiert `GLIBCXX_3.4.30`. Le symbole concerné
existe dans la libstdc++ système sous `GLIBCXX_3.4.11` — le patch réécrit le VERNEED ELF.
Détail complet : `ansible/roles/lm_studio/TROUBLESHOOTING.md`.
**Service systemd** : `lm-studio.service` sur gpu-01, géré par Ansible.
- Wrapper `run-llmster.sh` comme PID principal (reste en vie, surveille `llmster`)
- `Restart=on-failure` pour redémarrage automatique
- `HSA_OVERRIDE_GFX_VERSION=10.3.0` injecté dans l'environnement du service
**CLI** : `/usr/local/bin/lms` est un wrapper global (délègue au user `lmstudio`)
```bash
lms ls # liste les modèles
lms get <modele> # télécharger
lms load <modele> --context-length 16384 # charger en mémoire
lms server status
```
**Modèles** : stockés sur le NFS partagé (`/srv/data/models` sur storage-01),
montés sur gpu-01 via automount systemd (`/mnt/models`).
`/opt/lmstudio/.lmstudio/models` est un symlink → `/mnt/models`.
Tout modèle téléchargé avec `lms get` atterrit directement sur le RAID.
**Endpoint** : `http://192.168.10.20:1234/v1` (API OpenAI-compatible)
### 10.3. Hermes Agent
- Daemon : possible via systemd unit
- Mémoire/skills stockés dans `~/.hermes/` → **à mettre sur le RAID5**
- Variables `.env` dans `~/.hermes/.env`
### 10.4. RAM serrée sur ThinkCentre 8 GB
- Réserver système+kubelet : `system-reserved=cpu=500m,memory=1Gi`,
`kube-reserved=cpu=500m,memory=1Gi`
- Toujours définir `resources.requests/limits` sur les pods
- Sinon risque OOM-killer sur le nœud entier
### 10.5. Single control-plane
- Si compute-01 tombe, plus de `kubectl apply` mais les workloads continuent
- Snapshots etcd réguliers : `talosctl etcd snapshot` vers storage-01 (cron Ansible)
### 10.6. RAID5 sur storage-01
- **Dispositif** : `md127` (mdadm software RAID5, 4×1 TB = 2.7 TB utiles, ext4)
- **Monté** : `/srv/data` — point de montage principal de toutes les données persistantes
- **Superbloc mdadm sur les disques** : survit à un changement d'OS, ré-assemblage auto au boot
- **Arborescence** :
```
/srv/data/
├── nfs/k8s/ → NFS provisioner Kubernetes
├── models/ → modèles LM Studio (partagés avec gpu-01 via NFS)
├── hermes/ → mémoire/skills Hermes Agent
├── postgres/ → données PostgreSQL
├── qdrant/ → données Qdrant
└── minio/ → données MinIO
```
- **À sauvegarder** : configs k8s (Git), dumps BDD, mémoire Hermes (`/srv/data/hermes`)
- **Modèles LLM** : stockés sur le RAID (`/srv/data/models`) — re-téléchargeables si perte
### 10.7. Sécurité passerelle = bastion admin
- storage-01 cumule passerelle + bastion + Hermes + données → SPOF assumé
- gpu-01 sans rôle réseau critique : si tombe, perte inférence uniquement
### 10.8. Bootstrap : ce qui reste manuel
GitOps ne peut pas faire le bootstrap absolu. Restent toujours manuels :
- Installation OS sur chaque machine (AlmaLinux ou Talos depuis USB)
- Configuration IP/SSH minimale pour qu'Ansible puisse se connecter
- Premier déploiement ArgoCD via Helm
- Tout le reste est ensuite géré par Ansible / talhelper / ArgoCD
---
## 11. Outils à installer sur storage-01 (bastion admin)
```bash
# Outils Kubernetes / Talos
- talosctl (admin Talos)
- talhelper (génération configs Talos)
- kubectl (admin Kubernetes)
- helm (charts)
- argocd CLI
- k9s (TUI Kubernetes)
# Outils IaC
- ansible-core
- sops + age (secrets Talos)
# Outils IA
- hermes (Hermes Agent)
- curl/jq (debug API LM Studio)
# Outils système
- nftables, dnsmasq, nfs-utils, postgresql-server, podman
- minio, qdrant (binaires ou conteneurs Podman)
# Optionnel
- gitea ou forgejo (Git local)
- restic ou borgbackup (sauvegardes)
```
Tous installés via les rôles Ansible — pas de installation manuelle au-delà
du bootstrap initial.
---
## 12. Conventions et nommage
- Hostnames : `storage-01`, `compute-01`, `compute-02`, `compute-03`, `gpu-01`
- Domaine local : `lab.local`
- Services exposés via cluster : `<service>.lab.local` → IP MetalLB de Traefik
- Endpoint LM Studio : `http://192.168.10.20:1234/v1` (ou `lms.lab.local`)
- Namespaces k8s :
- `argocd` (ArgoCD lui-même)
- `infra` (MetalLB, Traefik, cert-manager, monitoring)
- `storage` (Longhorn, NFS provisioner)
- `external` (ExternalName vers services storage-01/gpu-01)
- `ai` (Open WebUI, LiteLLM)
- `workflows` (n8n)
- Git :
- Branches : `main` (= prod), `feature/*` (changements en cours)
- Commits : conventional commits (`feat:`, `fix:`, `chore:`, `docs:`)
---
## 13. État actuel (2026-05-10)
### Rôles Ansible déployés
| Rôle | Cible | État |
|---|---|---|
| `common` | storage-01, gpu-01 | ✅ |
| `gateway` | storage-01 | ✅ nftables, NAT, IP forwarding |
| `dnsmasq` | storage-01 | ✅ résolution `*.lab.local` |
| `nfs_server` | storage-01 | ✅ RAID5 monté `/srv/data`, exports NFS |
| `nfs_client` | gpu-01 | ✅ automount `/mnt/nfs` + `/mnt/models` |
| `rocm` | gpu-01 | ✅ RX 6700XT détectée |
| `lm_studio` | gpu-01 | ✅ service actif, API accessible |
| `postgresql` | storage-01 | ❌ stub vide |
| `qdrant` | storage-01 | ❌ stub vide |
| `minio` | storage-01 | ❌ stub vide |
| `hermes_agent` | storage-01 | ❌ stub vide |
### Infrastructure
- ✅ storage-01 opérationnel — `192.168.1.200` (WAN) / `192.168.10.1` (LAN cluster)
- ✅ gpu-01 opérationnel — `192.168.10.20`, LM Studio accessible
- ✅ RAID5 `md127` monté `/srv/data` (2.7 TB, ext4, état clean)
- ✅ NFS exports actifs : `/srv/data/nfs/k8s`, `/srv/data/models`
- ✅ LM Studio `http://192.168.10.20:1234/v1` — répond depuis storage-01
- ✅ Modèles sur NFS : gemma-4-e2b, gemma-4-e4b, nomic-embed-text
- ❌ Route statique Freebox non configurée (LAN domestique → LAN cluster)
- ❌ Premier commit GitHub non effectué
- ❌ Cluster k8s non démarré (Phase 5)
- ❌ Hermes Agent non installé sur storage-01 (Phase 3)
---
## 14. Annexes — Liens utiles
**Infrastructure**
- Talos Linux : https://www.talos.dev/
- Image Factory Talos : https://factory.talos.dev/
- talhelper : https://github.com/budimanjojo/talhelper
- AlmaLinux 9 : https://almalinux.org/
- ROCm install (RHEL 9) : https://rocm.docs.amd.com/projects/install-on-linux/
**Kubernetes / GitOps**
- ArgoCD : https://argo-cd.readthedocs.io/
- ArgoCD App of Apps pattern : https://argo-cd.readthedocs.io/en/stable/operator-manual/cluster-bootstrapping/
- Sealed Secrets : https://github.com/bitnami-labs/sealed-secrets
- Longhorn : https://longhorn.io/
- MetalLB : https://metallb.universe.tf/
**IaC**
- Ansible docs : https://docs.ansible.com/
- Ansible Vault : https://docs.ansible.com/ansible/latest/vault_guide/index.html
- SOPS : https://github.com/getsops/sops
**IA**
- LM Studio : https://lmstudio.ai/
- LM Studio CLI : https://lmstudio.ai/docs/cli
- Hermes Agent : https://github.com/NousResearch/hermes-agent
- LiteLLM : https://docs.litellm.ai/
- Open WebUI : https://docs.openwebui.com/