Funk-lab/CLAUDE.md

7.5 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Présentation du projet

Funk — homelab IA composé d'un cluster Kubernetes (Talos) pour les services applicatifs, d'un hôte d'inférence LLM dédié (gpu-01 / LM Studio), et d'un agent autonome (Hermes Agent) sur storage-01. Stack IaC complète : Ansible + talhelper + ArgoCD.

Référence complète : Funk/Funk-infra/HOMELAB.md

État actuel (2026-05-09) :

  • Hermes Agent opérationnel sur le poste perso (LM Studio local 127.0.0.1:1234)
  • storage-01 installé — 192.168.10.1 (LAN cluster), 192.168.1.200 (WAN)
  • gpu-01 installé — ping OK depuis storage-01 (192.168.10.20)
  • Phase 1/2 en cours — Ansible, ROCm, LM Studio, dnsmasq, NAT restent à faire
  • Repo lab-infra/ à initialiser, aucun cluster k8s monté

Architecture des machines

Machine IP OS Rôle
storage-01 192.168.10.1 AlmaLinux 9.7 Passerelle + bastion admin + données + Hermes Agent — hors cluster
compute-01 192.168.10.11 Talos v1.13+ k8s control-plane
compute-02 192.168.10.12 Talos v1.13+ k8s worker
compute-03 192.168.10.13 Talos v1.13+ k8s worker
gpu-01 192.168.10.20 AlmaLinux 9.7 LM Studio dédié (RX 6700XT) — hors cluster

Points clés d'architecture :

  • storage-01 et gpu-01 sont hors du cluster k8s — ils sont consommés via des ExternalName services ou Endpoints manuels
  • Le cluster n'a que 3 nœuds (compute-01/02/03), pas de GPU dans k8s
  • Single control-plane assumé : si compute-01 tombe, kubectl apply ne fonctionne plus mais les workloads continuent

Structure du repo lab-infra (cible)

lab-infra/
├── ansible/              # gère storage-01 + gpu-01 (AlmaLinux)
│   ├── inventory.yml
│   ├── playbooks/site.yml
│   ├── group_vars/
│   └── roles/            # common, gateway, dnsmasq, nfs_server, postgresql,
│                         # qdrant, minio, rocm, lm_studio, hermes_agent
├── talos/                # nœuds Talos via talhelper
│   ├── talconfig.yaml    # source de vérité (lue par talhelper)
│   ├── talsecret.sops.yaml
│   ├── talenv.yaml
│   ├── patches/
│   └── clusterconfig/    # généré → au .gitignore, ne pas committer
└── k8s/                  # workloads k8s (ArgoCD)
    ├── argocd-bootstrap/
    ├── apps-of-apps/root.yaml
    ├── infra/            # metallb, traefik, cert-manager, longhorn, nfs-provisioner
    └── apps/             # litellm, open-webui, n8n, monitoring,
                          # external-services (ExternalName vers storage-01/gpu-01)

Stack IaC — trois outils, trois cibles

Outil Cible Déclenchement
Ansible storage-01, gpu-01 (AlmaLinux) Push manuel : ansible-playbook
talhelper compute-01/02/03 (Talos) Push manuel : talosctl apply-config
ArgoCD Workloads k8s Pull automatique depuis Git

Ansible

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  # un rôle

Rôles prévus : common, gateway, dnsmasq, nfs_server, postgresql, qdrant, minio, rocm, lm_studio, hermes_agent. Tous idempotents. Secrets via Ansible Vault.

talhelper (nœuds Talos)

cd talos/
talhelper genconfig                                        # régénérer configs nœuds
talhelper gencommand apply --node compute-02 | bash        # appliquer sur un nœud
talhelper gencommand bootstrap --node compute-01 | bash    # bootstrap (1ère fois seulement)
talhelper gencommand kubeconfig | bash                     # récupérer kubeconfig

talos/clusterconfig/ est généré — ne pas committer (mettre au .gitignore).

ArgoCD (workloads k8s)

Pattern "App of Apps" : modifier un manifest → git commit + git push → ArgoCD applique automatiquement (poll toutes les ~3 min).

vim k8s/apps/open-webui/deployment.yaml
git add k8s/apps/open-webui/
git commit -m "open-webui: bump to v0.4.0"
git push

Réseaux internes Kubernetes

  • CNI (Flannel) — Pods : 10.42.0.0/16
  • Services : 10.43.0.0/16
  • MetalLB pool (IP virtuelles Traefik/services) : 192.168.10.200-230

Contrainte GPU critique

La RX 6700XT (gfx1031) n'est pas officiellement supportée par ROCm. Sur gpu-01, toujours s'assurer que la variable est présente dans l'environnement LM Studio / le service systemd :

HSA_OVERRIDE_GFX_VERSION=10.3.0

ROCm tourne directement sur l'hôte AlmaLinux (pas dans des conteneurs). Fallback : Vulkan dans LM Studio si ROCm coince.

LM Studio bind sur 127.0.0.1 par défaut → pour l'exposer au réseau :

lms server start --port 1234 --bind 0.0.0.0

CLI LM Studio utile : lms ls, lms load <model>, lms unload, lms server status


Deux setups IA

Setup 1 — Poste perso (9950X3D) — dev local

Composant Endpoint
LM Studio http://127.0.0.1:1234/v1
Ollama http://127.0.0.1:11434
Hermes Agent base_url: http://127.0.0.1:1234/v1
# Prérequis : modèle chargé avec context suffisant (system prompt Hermes ≈ 15 000 tokens)
lms load <modele> --context-length 16384
lms server start --port 1234

Agents Goose dans Funk/Funk-infra/agents/ — pointent tous sur http://localhost:1234/v1 :

cd Funk/Funk-infra/agents/<nom-agent>
goose session --config goose-config.yaml

Setup 2 — Cluster Funk (production)

Composant Endpoint
LM Studio (gpu-01) http://192.168.10.20:1234/v1
Hermes Agent service systemd sur storage-01, pointe sur gpu-01
# Sur gpu-01 — HSA_OVERRIDE requis pour RX 6700XT (gfx1031)
HSA_OVERRIDE_GFX_VERSION=10.3.0 lms load <modele> --context-length 16384
lms server start --port 1234 --bind 0.0.0.0

Détail complet des deux setups : Funk/Funk-infra/HOMELAB.md § 7.4


Conventions

  • Domaine : lab.local — wildcard DNS via dnsmasq sur storage-01
  • Ingress : <service>.lab.local → IP MetalLB de Traefik (192.168.10.200-230)
  • LM Studio endpoint : http://192.168.10.20:1234/v1
  • Namespaces k8s : argocd, infra, storage, external, ai, workflows
  • Git : branches main (prod) / feature/* — commits en conventional commits (feat:, fix:, chore:, docs:)
  • Secrets : Ansible Vault (Ansible), SOPS+age (Talos via talhelper), Sealed Secrets (k8s)

Opérations cluster importantes

etcd backup (single control-plane → critique) :

talosctl etcd snapshot /path/to/backup.db --nodes compute-01

À planifier en cron Ansible vers le RAID5 de storage-01.

Hermes Agent — données persistantes dans ~/.hermes/ → doit résider sur le RAID5, pas sur le SSD OS.

Ce qui reste toujours manuel (GitOps ne peut pas bootstrapper lui-même) :

  • Installation OS depuis USB (AlmaLinux ou Talos)
  • Configuration IP/SSH minimale pour le premier Ansible
  • Premier déploiement ArgoCD via Helm

RAM serrée sur compute-02/03 (8 GB)

Toujours définir resources.requests et resources.limits sur les pods déployés sur ces nœuds — sans quoi l'OOM-killer peut tuer kubelet.

Réservations à configurer dans talhelper :

system-reserved: cpu=500m,memory=1Gi
kube-reserved:   cpu=500m,memory=1Gi