- Remove Funk/ from .gitignore (repo is private) - Add HOMELAB.md and agent configs for Hermes context - Update hermes TUI doc: must run as hermes user from /srv/data/hermes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
37 KiB
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)
- Facilité d'administration
- Apprentissage / montée en compétences
- Performance brute (utiliser le 6700XT au max)
- 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,argocdCLI,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 :
# 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) :
model:
default: <nom-modele>
provider: lmstudio
base_url: http://127.0.0.1:1234/v1
context_length: 16384
Goose agents :
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 :
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) :
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 :
# 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 :
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 :
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 :
# 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
- 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)
- Installation AlmaLinux sur storage-01 ✅
- Configuration IP de base — storage-01 joignable (192.168.10.1 LAN cluster) ✅
- IP WAN storage-01 fixée à 192.168.1.200 ✅
- Configuration VLAN sur le GS308EV4 ✅ — tous ports Access, storage-01 sur deux NICs physiques
lab-infra/ansible/initialisé — inventory, rôles common + gateway ✅- nftables/NAT/IP forwarding opérationnels ✅
- 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)
- Installation AlmaLinux sur gpu-01 ✅ — ping OK depuis storage-01
- Ajout de gpu-01 à l'inventaire Ansible ✅
- Rôle
rocm: repo AMD + drivers +HSA_OVERRIDE_GFX_VERSION=10.3.0✅ - Validation ROCm : RX 6700XT détectée ✅
- Rôle
lm_studio: install + service systemd + bind0.0.0.0:1234✅ - Téléchargement modèles : gemma-4-e2b, gemma-4-e4b ✅
- 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 chatinteractif - (Optionnel) Service systemd pour daemon background
- Données persistantes sur le RAID5 (
/srv/data/hermes)
Phase 4 — Stack données sur storage-01 (Ansible)
- Rôle
nfs_server: RAID5md127monté/srv/data, exports NFS ✅ - Rôle
nfs_clientsur gpu-01 — automount/mnt/nfs+/mnt/models✅ - 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/avectalconfig.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 nodesmontre 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-provisionerpointant sur storage-01 - Application ArgoCD : Longhorn (avec
iscsi-toolsextension 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
giteasur storage-01 - Migration du repo
lab-infravers 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.shcomme PID principal (reste en vie, surveillellmster) Restart=on-failurepour redémarrage automatiqueHSA_OVERRIDE_GFX_VERSION=10.3.0injecté dans l'environnement du service
CLI : /usr/local/bin/lms est un wrapper global (délègue au user lmstudio)
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
.envdans~/.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/limitssur les pods - Sinon risque OOM-killer sur le nœud entier
10.5. Single control-plane
- Si compute-01 tombe, plus de
kubectl applymais les workloads continuent - Snapshots etcd réguliers :
talosctl etcd snapshotvers 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)
# 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(oulms.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:)
- Branches :
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
md127monté/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/