From 0aa4c3650da51d60589a73594045925df1d6331f Mon Sep 17 00:00:00 2001 From: Alkatrazz Date: Wed, 10 Jun 2026 15:21:24 +0200 Subject: [PATCH] =?UTF-8?q?docs(ansible):=20README=20par=20r=C3=B4le=20(19?= =?UTF-8?q?)=20+=20gaps=20IaC=20d=C3=A9couverts=20=C3=A0=20l'audit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audit complet des rôles vs état réel du cluster. Chaque rôle a maintenant un README.md : but, variables (défauts + surcharges host_vars/vault), handlers, vérifications, caveats. Gaps découverts et documentés : - minio : rôle VIDE (tasks/main.yml sans aucune tâche), service inactif — placeholder à implémenter ou retirer (ports 9000/9001 ouverts pour rien) - llama_server : ne gère que l'instance GPU :1234 — les services CPU llama-server-system :1236 et llama-server-monitor :1237 sont créés à la main sur gpu-01, non reproductibles par make apply-gpu (+ un llama-server-dev.service non documenté présent sur l'hôte) - hermes_auto_improve : defaults port/repo/branch/log non consommés (valeurs en dur dans les scripts), défaut branch obsolète - lm_studio : README de dépréciation, candidat suppression Tables admin/ops/ansible.md, README.md et CLAUDE.md corrigées en conséquence. Co-Authored-By: Claude Fable 5 --- CLAUDE.md | 6 ++-- README.md | 2 +- admin/ops/ansible.md | 7 ++-- ansible/roles/alertmanager_webhook/README.md | 32 +++++++++++++++++ ansible/roles/common/README.md | 27 ++++++++++++++ ansible/roles/dnsmasq/README.md | 28 +++++++++++++++ ansible/roles/gateway/README.md | 35 ++++++++++++++++++ ansible/roles/hermes_agent/README.md | 37 +++++++++++++++++++ ansible/roles/hermes_auto_improve/README.md | 28 +++++++++++++++ ansible/roles/hermes_user/README.md | 29 +++++++++++++++ ansible/roles/litellm/README.md | 26 ++++++++++++++ ansible/roles/llama_server/README.md | 38 ++++++++++++++++++++ ansible/roles/lm_studio/README.md | 14 ++++++++ ansible/roles/minio/README.md | 16 +++++++++ ansible/roles/nfs_client/README.md | 20 +++++++++++ ansible/roles/nfs_server/README.md | 25 +++++++++++++ ansible/roles/node_exporter/README.md | 26 ++++++++++++++ ansible/roles/postfix_relay/README.md | 31 ++++++++++++++++ ansible/roles/postgresql/README.md | 26 ++++++++++++++ ansible/roles/qdrant/README.md | 29 +++++++++++++++ ansible/roles/rag/README.md | 35 ++++++++++++++++++ ansible/roles/rocm/README.md | 27 ++++++++++++++ 22 files changed, 538 insertions(+), 6 deletions(-) create mode 100644 ansible/roles/alertmanager_webhook/README.md create mode 100644 ansible/roles/common/README.md create mode 100644 ansible/roles/dnsmasq/README.md create mode 100644 ansible/roles/gateway/README.md create mode 100644 ansible/roles/hermes_agent/README.md create mode 100644 ansible/roles/hermes_auto_improve/README.md create mode 100644 ansible/roles/hermes_user/README.md create mode 100644 ansible/roles/litellm/README.md create mode 100644 ansible/roles/llama_server/README.md create mode 100644 ansible/roles/lm_studio/README.md create mode 100644 ansible/roles/minio/README.md create mode 100644 ansible/roles/nfs_client/README.md create mode 100644 ansible/roles/nfs_server/README.md create mode 100644 ansible/roles/node_exporter/README.md create mode 100644 ansible/roles/postfix_relay/README.md create mode 100644 ansible/roles/postgresql/README.md create mode 100644 ansible/roles/qdrant/README.md create mode 100644 ansible/roles/rag/README.md create mode 100644 ansible/roles/rocm/README.md diff --git a/CLAUDE.md b/CLAUDE.md index 56319b9..73dc8e5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -85,13 +85,15 @@ Les tags correspondent aux noms de rôles. Tous les rôles sont idempotents. ### Rôles — storage-01 -`hermes_user`, `common`, `gateway` (nftables + NAT), `dnsmasq`, `nfs_server`, `qdrant`, `postgresql`, `minio`, `litellm`, `hermes_agent`, `rag`, `node_exporter`, `alertmanager_webhook`, `postfix_relay`, `hermes_auto_improve` (worker doc Hermes + trigger server :9095) +`hermes_user`, `common`, `gateway` (nftables + NAT), `dnsmasq`, `nfs_server`, `qdrant`, `postgresql`, `minio` (⚠️ rôle vide, service non déployé), `litellm`, `hermes_agent`, `rag`, `node_exporter`, `alertmanager_webhook`, `postfix_relay`, `hermes_auto_improve` (worker doc Hermes + trigger server :9095) ### Rôles — gpu-01 `hermes_user`, `common`, `rocm`, `nfs_client`, `llama_server`, `node_exporter` -> `lm_studio` est présent dans `ansible/roles/` mais n'est plus utilisé dans aucun playbook. +> Chaque rôle a un `README.md` (variables, caveats, gaps IaC). +> `lm_studio` est présent dans `ansible/roles/` mais n'est plus utilisé dans aucun playbook (déprécié, voir son README). +> ⚠️ Gap IaC : les instances llama-server CPU `:1236`/`:1237` sur gpu-01 sont des services **manuels**, non gérés par le rôle `llama_server` (voir `ansible/roles/llama_server/README.md`). ### Caveat premier déploiement — hermes_agent diff --git a/README.md b/README.md index 1a92559..473ab1b 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ L'ensemble est géré en Infrastructure-as-Code : **Ansible** pour les hôtes Al │ ├── alertmanager_webhook # Pont AlertManager → ask-agent │ ├── postfix_relay # SMTP sortant via Gmail │ ├── node_exporter # Métriques Prometheus -│ ├── minio # Stockage objet S3 +│ ├── minio # ⚠️ placeholder vide — service non déployé │ ├── postgresql # Base de données │ ├── qdrant # Base vectorielle │ └── hermes_auto_improve # Worker analyse doc + trigger server :9095 diff --git a/admin/ops/ansible.md b/admin/ops/ansible.md index f9ed56b..8964ff0 100644 --- a/admin/ops/ansible.md +++ b/admin/ops/ansible.md @@ -7,7 +7,8 @@ Le venv est dans `.venv/`, le `Makefile` fournit les raccourcis. ## Playbooks et rôles -Deux playbooks, un par machine (le `site.yml` les enchaîne) : +Deux playbooks, un par machine (le `site.yml` les enchaîne). +Chaque rôle a un `README.md` détaillé dans `ansible/roles//` (variables, caveats, gaps). ### `playbooks/storage-01.yml` — hosts `gateway` (15 rôles) @@ -20,7 +21,7 @@ Deux playbooks, un par machine (le `site.yml` les enchaîne) : | `nfs_server` | `nfs_server` | Exports NFS (modèles LLM, volumes k8s) | | `qdrant` | `qdrant` | Base vectorielle (RAG) — `:6333` | | `postgresql` | `postgresql` | PG 16 — bases hermes, litellm, n8n, openwebui, grafana | -| `minio` | `minio` | Stockage objet S3 | +| `minio` | `minio` | ⚠️ **Rôle vide** (placeholder) — service inactif, à implémenter ou retirer | | `litellm` | `litellm` | Proxy LLM `:4000` + script `ask-agent` | | `hermes_agent` | `hermes_agent` | Agent Hermes + skills + souls | | `rag` | `rag` | Scripts `rag-ingest` / `rag-query` + docs RAG | @@ -37,7 +38,7 @@ Deux playbooks, un par machine (le `site.yml` les enchaîne) : | `common` | `common` | Paquets de base, sécurité, NTP | | `rocm` | `rocm` | Stack GPU AMD (ROCm 7.x, `HSA_OVERRIDE_GFX_VERSION=10.3.0`) | | `nfs_client` | `nfs_client` | Montage `/mnt/models` depuis storage-01 | -| `llama_server` | `llama_server` | llama-server ×3 : GPU `:1234`, CPU `:1236`/`:1237` | +| `llama_server` | `llama_server` | llama-server **GPU `:1234` uniquement** — ⚠️ les instances CPU `:1236`/`:1237` (system/monitor) sont des services créés à la main sur gpu-01, hors IaC | | `node_exporter` | `node_exporter` | Métriques Prometheus (+ rocm power) | > ⚠️ Le rôle `lm_studio` existe encore dans `ansible/roles/` mais n'est référencé par **aucun** playbook (remplacé par `llama_server` en mai 2026). Candidat à la suppression. diff --git a/ansible/roles/alertmanager_webhook/README.md b/ansible/roles/alertmanager_webhook/README.md new file mode 100644 index 0000000..a3fc47b --- /dev/null +++ b/ansible/roles/alertmanager_webhook/README.md @@ -0,0 +1,32 @@ +# alertmanager_webhook + +Pont AlertManager → Hermes : reçoit les alertes Prometheus en webhook et les délègue au profil `monitor` via ask-agent/LiteLLM. + +## Ce que fait le rôle + +- Déploie `webhook.py` dans `/opt/alertmanager-webhook` (serveur HTTP `:9093`, user `hermes`) +- Service systemd `alertmanager-webhook` +- Ouvre le port 9093 dans nftables (cluster + pods — AlertManager tourne dans k8s) + +## Variables principales + +| Variable | Défaut | +|---|---| +| `webhook_port` | 9093 | +| `webhook_user` | `hermes` | +| `litellm_url` | `http://127.0.0.1:4000/v1` | + +## Configuration côté AlertManager + +Le receiver est défini dans `k8s/infra/monitoring/values.yaml` → URL `http://192.168.10.1:9093/`. (n8n a aussi son propre receiver pour les emails — les deux coexistent.) + +## Test manuel + +```bash +curl -X POST http://192.168.10.1:9093/ -H 'Content-Type: application/json' \ + -d '{"alerts":[{"status":"firing","labels":{"alertname":"Test","severity":"warning"},"annotations":{"summary":"test"}}]}' +``` + +## Caveat + +Le traitement est asynchrone (thread) pour répondre vite à AlertManager — incident passé : BrokenPipeError quand la réponse LLM dépassait le timeout AlertManager (voir progress 2026-05-14). diff --git a/ansible/roles/common/README.md b/ansible/roles/common/README.md new file mode 100644 index 0000000..724399c --- /dev/null +++ b/ansible/roles/common/README.md @@ -0,0 +1,27 @@ +# common + +Socle de base pour tous les hôtes AlmaLinux (storage-01, gpu-01). + +## Ce que fait le rôle + +- Hostname + timezone (`Europe/Paris`) + EPEL + upgrade complet des paquets +- Paquets de base : vim, curl, jq, git, htop, chrony, bind-utils… +- NTP via chrony +- User `ansible` (groupe + sudo passwordless + clé SSH autorisée) +- Ajoute `admin_user` au groupe `systemd-journal` (lecture des logs sans sudo) +- Durcissement sshd + +## Variables principales + +| Variable | Défaut | Rôle | +|---|---|---| +| `timezone` | `Europe/Paris` | Timezone système | +| `base_packages` | liste (14 paquets) | Paquets installés partout | +| `ansible_service_user` | `ansible` | User de connexion Ansible | + +`admin_user` vient de `group_vars/all/vars.yml`. + +## Handlers + +- `Restart sshd` +- `Reboot if kernel updated` — **attention** : un upgrade kernel déclenche un reboot diff --git a/ansible/roles/dnsmasq/README.md b/ansible/roles/dnsmasq/README.md new file mode 100644 index 0000000..50b89a1 --- /dev/null +++ b/ansible/roles/dnsmasq/README.md @@ -0,0 +1,28 @@ +# dnsmasq + +DNS + DHCP du LAN cluster sur storage-01. Résout `*.lab.local` et fournit les baux DHCP statiques des nœuds Talos (boot PXE-like par MAC). + +## Ce que fait le rôle + +- Installe dnsmasq + config `/etc/dnsmasq.conf` +- Override systemd : attend l'interface USB ethernet avant de démarrer +- DHCP statique par adresse MAC pour compute-01/02/03 +- Wildcard `*.lab.local` → `traefik_ip` (192.168.10.200) + +## Variables principales + +| Variable | Défaut | Rôle | +|---|---|---| +| `dns_upstream` | 1.1.1.1, 9.9.9.9 | Résolveurs amont | +| `dhcp_range_start/end` | 192.168.10.50-99 | Plage DHCP dynamique | +| `dhcp_static_hosts` | compute-01/02/03 (par MAC) | Baux statiques Talos | +| `traefik_ip` | 192.168.10.200 | Cible du wildcard `*.lab.local` | +| `dns_cluster_hosts` | les 5 machines | Entrées DNS fixes | + +## Handlers + +- `Restart dnsmasq`, `Reload systemd` + +## Caveat + +Si on change le MAC d'un nœud Talos (remplacement carte mère/NIC), mettre à jour `dhcp_static_hosts` **avant** de rebooter le nœud, sinon il prend une IP dynamique et sort du cluster. diff --git a/ansible/roles/gateway/README.md b/ansible/roles/gateway/README.md new file mode 100644 index 0000000..19cffc2 --- /dev/null +++ b/ansible/roles/gateway/README.md @@ -0,0 +1,35 @@ +# gateway + +Fait de storage-01 le routeur/firewall du LAN cluster : nftables + NAT + IP forwarding. +Remplace firewalld (stoppé et désactivé). + +## Ce que fait le rôle + +- Désactive firewalld, installe nftables +- Active `net.ipv4.ip_forward` +- Déploie `/etc/nftables.conf` depuis `templates/nftables.conf.j2` (policy **drop** en input/forward) + +## Ports ouverts (template nftables.conf.j2) + +| Port | Source | Service | +|---|---|---| +| 22 | LAN maison + cluster | SSH | +| 53 udp/tcp, 67 udp | LAN cluster | dnsmasq (DNS + DHCP) | +| 111, 2049 | LAN cluster | NFS | +| 5432, 6333/6334, 9000/9001 | LAN cluster | PostgreSQL, Qdrant, MinIO | +| 9100, 9093 | cluster + pods 10.42/16 | node_exporter, alertmanager-webhook | +| 8080 | maison + cluster + pods | Hermes gateway | +| 9119 | `hermes_dashboard_allowed_ip` uniquement | Dashboard Hermes | +| 4000 | maison + cluster + pods | LiteLLM | +| 25 | cluster + pods | Postfix relay | +| 9095 | cluster + pods | hermes-auto-improve trigger | + +NAT : masquerade du LAN cluster vers le WAN. + +## Variables + +Pas de defaults — utilise `lan_interface`, `wan_interface` (host_vars) et `hermes_dashboard_allowed_ip` (defaults du rôle hermes_agent). + +## Caveat + +**Tout nouveau service réseau doit ajouter son port ici** (modifier le template puis `--tags gateway`). C'est la cause de plusieurs incidents passés (voir `admin/incidents.md` 2026-05-13, port monitoring bloqué). diff --git a/ansible/roles/hermes_agent/README.md b/ansible/roles/hermes_agent/README.md new file mode 100644 index 0000000..0e43883 --- /dev/null +++ b/ansible/roles/hermes_agent/README.md @@ -0,0 +1,37 @@ +# hermes_agent + +Installe et configure l'agent Hermes sur storage-01 : binaire, profils, skills, dashboard, gateway HTTP. + +## Ce que fait le rôle + +- Dépendances + installation du binaire via le script officiel (`curl | bash`) — **seulement si absent** +- `HERMES_HOME=/srv/data/hermes` (données NVMe), code dans `/opt/hermes` +- `.env` : `LM_BASE_URL` → LiteLLM `:4000`, `LM_API_KEY=lm-studio` +- `config.yaml` (profils), services systemd `hermes-agent` (gateway `:8080`) et `hermes-dashboard` (`:9119`) +- Wrapper global `/usr/local/bin/hermes` +- Ouvre le port gateway dans nftables + +## Variables principales + +| Variable | Défaut | +|---|---| +| `hermes_home` | `/opt/hermes` (code) | +| `hermes_data_dir` | `/srv/data/hermes` (profils/données) | +| `hermes_lm_base_url` | `http://127.0.0.1:4000/v1` | +| `hermes_model` | `hermes-default` | +| `hermes_gateway_port` | 8080 | +| `hermes_dashboard_port` / `hermes_dashboard_allowed_ip` | 9119 / 192.168.1.10 | + +## ⚠️ Caveat premier déploiement — deux runs + +L'installation du binaire et sa configuration sont dans le même rôle, mais la config est conditionnelle à `hermes_binary.stat.exists` **évalué avant l'installation**. Sur un hôte vierge : + +```bash +ansible-playbook -i inventory.yml playbooks/storage-01.yml --tags hermes_agent # run 1 : installe +ansible-playbook -i inventory.yml playbooks/storage-01.yml --tags hermes_agent # run 2 : configure +``` + +## Pièges connus (voir aussi memory feedback_hermes_litellm) + +- `LM_API_KEY=lm-studio` obligatoire dans `.env` — sans elle, Hermes boucle en erreurs d'auth +- Lancer `hermes -z` depuis `/srv/data/hermes` (HERMES_HOME), pas depuis `/opt/hermes` diff --git a/ansible/roles/hermes_auto_improve/README.md b/ansible/roles/hermes_auto_improve/README.md new file mode 100644 index 0000000..4677dd2 --- /dev/null +++ b/ansible/roles/hermes_auto_improve/README.md @@ -0,0 +1,28 @@ +# hermes_auto_improve + +Worker d'analyse quotidienne de la doc par Hermes : trigger server HTTP (`:9095`) + script d'analyse. Le scheduling est fait par **n8n** (workflow « Hermes Doc Worker », 22h) — pas de timer systemd. + +## Ce que fait le rôle + +- Installe `gh` CLI (repo officiel) +- Déploie les deux scripts depuis `tools/hermes-auto-improve/` : + - `/usr/local/bin/hermes-auto-improve` (← `auto-improve.py`) + - `/usr/local/bin/hermes-auto-improve-server` (← `trigger-server.py`) +- Service systemd `hermes-auto-improve` (le serveur HTTP, `HOME=/root` pour les credentials gh) +- Crée `/var/lib/hermes-auto-improve` (state) + +## Variables + +| Variable | Défaut | Note | +|---|---|---| +| `hermes_auto_improve_state_dir` | `/var/lib/hermes-auto-improve` | seule variable réellement consommée | +| `hermes_auto_improve_port` / `repo` / `branch` / `log` | … | ⚠️ **non utilisées** — les valeurs sont codées en dur dans les scripts Python (port 9095, `/srv/data/lab/Funk-lab`, branche `hermes/daily-work`) ; le défaut `branch: feature/hermes-auto-improve` est obsolète | + +## Dépendances hors-rôle + +- Credentials `gh` de root sur storage-01 (auth GitHub faite à la main — non gérée par Ansible) +- Clone `/srv/data/lab/Funk-lab` (créé à la main) +- Port 9095 ouvert par le rôle `gateway` +- Hermes + LiteLLM + llama-server up (les analyses passent par le profil funk-ai) + +Doc d'exploitation : `admin/ia/hermes-auto-improve.md` diff --git a/ansible/roles/hermes_user/README.md b/ansible/roles/hermes_user/README.md new file mode 100644 index 0000000..4ee1ffc --- /dev/null +++ b/ansible/roles/hermes_user/README.md @@ -0,0 +1,29 @@ +# hermes_user + +Crée le user `hermes` (uid/gid 1002) sur **les deux hôtes** avec SSH partagé : la clé générée sur storage-01 est autorisée sur gpu-01, ce qui permet à l'agent de lancer des commandes read-only sur le nœud GPU. + +## Ce que fait le rôle + +- User/groupe `hermes` (1002:1002, home `/opt/hermes`) +- Sudo **limité** (listes blanches différentes storage-01 / gpu-01) +- Génération clé ed25519 sur storage-01 → poussée dans `authorized_keys` de gpu-01 +- `~/.ssh/config` + known_hosts pré-remplis (gpu-01) +- Copie du kubeconfig dans `~hermes/.kube/` si présent (accès kubectl read-only) +- Ajout au groupe `systemd-journal` + +## Variables principales + +| Variable | Défaut | +|---|---| +| `hermes_user` / `hermes_group` | `hermes` | +| `hermes_uid` / `hermes_gid` | 1002 | +| `hermes_home` | `/opt/hermes` | +| `hermes_ssh_key_type` | ed25519 | + +## Ordre d'exécution + +Doit passer **avant** `hermes_agent` (qui suppose le user existant). C'est le premier rôle des deux playbooks. + +## Caveat + +Le rôle touche les deux machines mais avec des branches conditionnelles (`inventory_hostname`) — un run sur storage-01 seul ne pousse pas la clé sur gpu-01 : lancer les **deux** playbooks après régénération de clé. diff --git a/ansible/roles/litellm/README.md b/ansible/roles/litellm/README.md new file mode 100644 index 0000000..fb7c95d --- /dev/null +++ b/ansible/roles/litellm/README.md @@ -0,0 +1,26 @@ +# litellm + +Proxy LLM multi-modèles sur storage-01 (`:4000`) — point d'entrée unique pour Hermes, n8n, Open WebUI et ask-agent. Route vers llama-server (gpu-01) ou l'API Anthropic. + +## Ce que fait le rôle + +- venv `/opt/litellm/venv` avec `litellm[proxy]`, user système `litellm` +- Config `/etc/litellm/config.yaml` : alias `hermes-default`, `qwen3-8b`, `system`, `monitor`, `claude…` +- Service systemd + scripts utilitaires : + - `/usr/local/bin/ask-agent` — délégation inter-agents (texte → profil system/monitor/brain) + - `/usr/local/bin/hermes-switch` — bascule l'alias `hermes-default` entre qwen (local) et claude (API), puis restart litellm + - `/usr/local/bin/funk-cluster` — orchestration start/stop/status du homelab + +## Variables principales + +| Variable | Défaut | +|---|---| +| `litellm_host` / `litellm_port` | `0.0.0.0` / 4000 | +| `litellm_master_key` | `lm-studio` (clé API attendue par les clients) | +| `litellm_local_llm_base_url` | `http://192.168.10.20:1234/v1` | +| `litellm_anthropic_api_key` | `{{ vault_anthropic_api_key }}` | + +## Caveat + +- L'alias `hermes-default` est **ce que Hermes consomme** — changer le modèle derrière l'alias ne nécessite pas de reconfigurer Hermes +- `hermes-switch` modifie `/etc/litellm/config.yaml` **en dehors d'Ansible** : un `--tags litellm` réécrit la config et peut écraser un switch manuel vers claude diff --git a/ansible/roles/llama_server/README.md b/ansible/roles/llama_server/README.md new file mode 100644 index 0000000..a84d18d --- /dev/null +++ b/ansible/roles/llama_server/README.md @@ -0,0 +1,38 @@ +# llama_server + +Compile llama.cpp (backend HIP/ROCm) et déploie le service **llama-server GPU** (`:1234`) sur gpu-01. Sert aussi les embeddings du RAG. + +## Ce que fait le rôle + +- Clone + build llama.cpp (cmake HIP, `gfx1030`) — build seulement si le binaire est absent +- Service systemd `llama-server` : modèle GGUF depuis `/mnt/models` (NFS), `:1234` +- Embeddings activés (`--embeddings`, pooling mean) +- Ouvre le port dans le firewall, désactive l'ancien service lm-studio + +## Variables principales + +| Variable | Défaut | Surcharge gpu-01 | +|---|---|---| +| `llama_server_port` | 1234 | — | +| `llama_model_path` | `""` | `/mnt/models/bartowski/Qwen3-8B-GGUF/Qwen3-8B-Q4_K_M.gguf` | +| `llama_model_alias` | `""` | `qwen3-8b` | +| `llama_ctx_size` | 32768 | 32768 | +| `llama_n_gpu_layers` | 99 | — | +| `hsa_override_gfx_version` | `10.3.0` | — | + +## ⚠️ Gap IaC — instances CPU hors Ansible + +Le rôle ne gère que l'instance **GPU** (`llama-server.service`). Les services suivants existent sur gpu-01 mais ont été créés **à la main** (non reproductibles par `make apply-gpu`) : + +| Service | Port | Modèle | Usage | +|---|---|---|---| +| `llama-server-system.service` | 1236 | Qwen3-1.7B CPU | profil `system` | +| `llama-server-monitor.service` | 1237 | Qwen3-1.7B CPU | profil `monitor` | +| `llama-server-dev.service` | — | (non documenté, présent dans /etc/systemd/system) | ? | + +**À faire** : templatiser ces instances dans le rôle (liste d'instances avec port/modèle/threads) pour qu'une réinstallation de gpu-01 restaure les 3 serveurs. + +## Caveats + +- Rebuild forcé : supprimer le binaire (`/opt/llama.cpp/build/bin/llama-server`) puis relancer le rôle +- Le modèle vient du NFS — voir caveat du rôle `nfs_client` diff --git a/ansible/roles/lm_studio/README.md b/ansible/roles/lm_studio/README.md new file mode 100644 index 0000000..54e9a06 --- /dev/null +++ b/ansible/roles/lm_studio/README.md @@ -0,0 +1,14 @@ +# lm_studio — ❌ DÉPRÉCIÉ (mai 2026) + +Rôle d'installation de LM Studio headless sur gpu-01. **Remplacé par le rôle `llama_server`** (llama.cpp compilé avec ROCm) — n'est plus référencé par aucun playbook. + +Le rôle `llama_server` désactive d'ailleurs explicitement le service `lm-studio` lors de son déploiement. + +## Pourquoi conservé (temporairement) + +- `TROUBLESHOOTING.md` documente le fix GLIBCXX (patchelf) qui pourrait resservir +- Historique de la première approche d'inférence (progress 2026-05-10) + +## À faire + +Candidat à la **suppression** : l'essentiel du troubleshooting est déjà dans l'historique git et `progress/2026-05-10.md`. diff --git a/ansible/roles/minio/README.md b/ansible/roles/minio/README.md new file mode 100644 index 0000000..017c35b --- /dev/null +++ b/ansible/roles/minio/README.md @@ -0,0 +1,16 @@ +# minio — ⚠️ rôle vide (placeholder) + +Ce rôle est référencé par `playbooks/storage-01.yml` mais son `tasks/main.yml` est **vide** : il ne déploie rien. + +## État réel + +- Aucune tâche, aucun template, aucun default +- Le service `minio` est **inactif** sur storage-01 +- Seuls vestiges : `minio_data_dir: /srv/data/minio` dans host_vars, ports 9000/9001 ouverts dans le template nftables du rôle `gateway` + +## Décision à prendre + +- **Soit** implémenter le rôle (binaire MinIO, user, service, console `:9001`) si un besoin S3 apparaît (backups, artefacts) +- **Soit** retirer le rôle du playbook + fermer les ports 9000/9001 dans nftables + +En l'état, il est inoffensif mais donne une fausse impression de service déployé dans la doc. diff --git a/ansible/roles/nfs_client/README.md b/ansible/roles/nfs_client/README.md new file mode 100644 index 0000000..aa2e056 --- /dev/null +++ b/ansible/roles/nfs_client/README.md @@ -0,0 +1,20 @@ +# nfs_client + +Montages NFS sur gpu-01 depuis storage-01 — en particulier `/mnt/models` (modèles GGUF) consommé par llama-server. + +## Ce que fait le rôle + +- Installe nfs-utils, crée les points de montage +- Écrit les entrées fstab avec **automount systemd** (`x-systemd.automount`) — pas de montage bloquant au boot si storage-01 est éteint +- Active les unités `.automount` + +## Variables principales + +| Variable | Défaut | Surcharge gpu-01 | +|---|---|---| +| `nfs_server_ip` | 192.168.10.1 | — | +| `nfs_client_mounts` | `/mnt/nfs` → `/srv/data/nfs/k8s` | + `/mnt/models` → `/srv/data/models` | + +## Caveat + +llama-server dépend de `/mnt/models` : si storage-01 redémarre, l'automount se rétablit seul mais un llama-server déjà lancé avec un modèle en RAM continue de tourner. En revanche un **restart** de llama-server pendant que le NFS est indisponible échoue. diff --git a/ansible/roles/nfs_server/README.md b/ansible/roles/nfs_server/README.md new file mode 100644 index 0000000..e4ec1bc --- /dev/null +++ b/ansible/roles/nfs_server/README.md @@ -0,0 +1,25 @@ +# nfs_server + +Exports NFS de storage-01 : volumes k8s (`/srv/data/nfs/k8s`) et modèles LLM (`/srv/data/models`, montés par gpu-01). + +## Ce que fait le rôle + +- Installe nfs-utils + mdadm +- Capture l'état du RAID **sans le monter** (mdadm.conf de référence) +- Sanity check : `/srv/data` doit être un répertoire (bind-mount NVMe) +- Crée les répertoires d'export, déploie `/etc/exports`, démarre nfs-server + +## Variables principales + +| Variable | Où | Valeur | +|---|---|---| +| `nfs_exports` | host_vars/storage-01 | `/srv/data/nfs/k8s` + `/srv/data/models` (rw, no_root_squash, LAN cluster) | +| `raid_uuid`, `raid_mount` | host_vars/storage-01 | **Référence seulement** — RAID retiré de l'usage actif | + +## ⚠️ Contexte RAID → NVMe (2026-05-29) + +`/srv/data` est un **bind-mount NVMe** (`/home/data`), plus le RAID5. Les variables `raid_*` sont conservées pour référence (mdadm.conf, incidents) mais **ne doivent pas servir à monter un filesystem**. Voir `admin/incidents.md` (2026-05-27 et 2026-05-29). + +## Handlers + +- Reload des exports (`exportfs -ra`) diff --git a/ansible/roles/node_exporter/README.md b/ansible/roles/node_exporter/README.md new file mode 100644 index 0000000..548b292 --- /dev/null +++ b/ansible/roles/node_exporter/README.md @@ -0,0 +1,26 @@ +# node_exporter + +Métriques Prometheus sur les hôtes hors-cluster (storage-01, gpu-01) — scrapées par kube-prometheus-stack. Sur gpu-01, ajoute un scraper ROCm (consommation GPU). + +## Ce que fait le rôle + +- Binaire node_exporter (version pinnée) + user dédié + service systemd (`:9100`) +- Textfile collector (`/var/lib/node_exporter/textfile_collector`) +- Ouvre le port 9100 dans nftables (cluster + pods) +- **Si `rocm_exporter_enabled`** (gpu-01) : script + timer systemd qui dump `rocm-smi` (puissance GPU en watts) dans le textfile collector toutes les 30 s → métrique `rocm_gpu_power_watts` (utilisée par le rapport horaire n8n pour le bilan énergie) + +## Variables principales + +| Variable | Défaut | gpu-01 | +|---|---|---| +| `node_exporter_version` | `1.9.1` | — | +| `node_exporter_port` | 9100 | — | +| `rocm_exporter_enabled` | `false` | `true` | +| `rocm_scrape_interval` | 30 | — | + +## Vérifications + +```bash +curl -s http://192.168.10.1:9100/metrics | head +curl -s http://192.168.10.20:9100/metrics | grep rocm_gpu_power +``` diff --git a/ansible/roles/postfix_relay/README.md b/ansible/roles/postfix_relay/README.md new file mode 100644 index 0000000..8dc4dbc --- /dev/null +++ b/ansible/roles/postfix_relay/README.md @@ -0,0 +1,31 @@ +# postfix_relay + +SMTP sortant du homelab : Postfix sur storage-01 relaie vers Gmail (`smtp.gmail.com:587`, App Password). Utilisé par n8n (rapports/alertes) et tout service du LAN/pods. + +## Ce que fait le rôle + +- Installe postfix + cyrus-sasl +- `main.cf` : relayhost Gmail, TLS, SASL, réécriture de l'expéditeur (generic map) +- `sasl_passwd` (depuis le vault) + postmap +- Ouvre le port 25 depuis le LAN cluster et les pods k8s + +## Variables principales + +| Variable | Défaut / source | +|---|---| +| `postfix_relay_host` / `port` | `smtp.gmail.com` / 587 | +| `postfix_relay_user` | group_vars (adresse Gmail) | +| `postfix_relay_password` | `vault_postfix_relay_password` (App Password Gmail) | +| `postfix_sender_email` | group_vars — tous les mails sortants sont réécrits vers cette adresse | +| `postfix_mynetworks` | 127/8, LAN cluster, pods 10.42/16, services 10.43/16 | + +## Historique + +Relay initialement via **Brevo**, basculé vers **Gmail SMTP + App Password** (2026-06). La doc `admin/infra/email.md` couvre la création de l'App Password. + +## Test + +```bash +echo "test" | mail -s "[Funk] test relay" aliyesilkaya93@gmail.com +sudo postqueue -p # file d'attente si échec +``` diff --git a/ansible/roles/postgresql/README.md b/ansible/roles/postgresql/README.md new file mode 100644 index 0000000..cfaab2f --- /dev/null +++ b/ansible/roles/postgresql/README.md @@ -0,0 +1,26 @@ +# postgresql + +PostgreSQL 16 sur storage-01 — persistance de Hermes, LiteLLM, n8n, Open WebUI (et Grafana). + +## Ce que fait le rôle + +- Repo PGDG + désactivation du module AppStream +- PGDATA déplacé vers `/srv/data/postgres` (override systemd) +- Init du cluster si absent, `pg_hba.conf`, listen_addresses, port, shared_buffers +- Crée bases + users + privilèges (dont `GRANT ON SCHEMA public` requis en PG 15+) + +## Variables principales + +| Variable | Défaut | +|---|---| +| `postgresql_version` | 16 | +| `postgresql_data_dir` | `/srv/data/postgres` | +| `postgresql_listen_addresses` | `localhost, 192.168.10.1` (pods k8s via LAN) | +| `postgresql_databases` / `postgresql_users` | hermes, litellm, openwebui, n8n | + +Mots de passe dans le vault : `vault_pg_hermes_password`, `vault_pg_litellm_password`, `vault_pg_openwebui_password`, `vault_pg_n8n_password`. + +## Caveat + +- Les tasks de Grant utilisent `no_log` — en cas d'échec, relancer avec `-vvv` après avoir temporairement retiré `no_log` +- Les pods k8s (n8n, openwebui, grafana) se connectent via `192.168.10.1:5432` — port ouvert dans nftables (rôle gateway) diff --git a/ansible/roles/qdrant/README.md b/ansible/roles/qdrant/README.md new file mode 100644 index 0000000..29cbaf0 --- /dev/null +++ b/ansible/roles/qdrant/README.md @@ -0,0 +1,29 @@ +# qdrant + +Base vectorielle Qdrant sur storage-01 — mémoire sémantique du RAG (collection `funk-docs`). + +## Ce que fait le rôle + +- User système `qdrant`, données dans `/srv/data/qdrant` (NVMe) +- Télécharge le binaire (version pinnée), upgrade seulement si la version installée diffère +- Config `/opt/qdrant/config.yaml` + service systemd + +## Variables principales + +| Variable | Défaut | +|---|---| +| `qdrant_version` | `1.13.4` | +| `qdrant_http_port` / `qdrant_grpc_port` | 6333 / 6334 | +| `qdrant_host` | `0.0.0.0` (filtré par nftables — LAN cluster seulement) | +| `qdrant_data_dir` | `/srv/data/qdrant` | + +## Vérifications + +```bash +curl -s http://127.0.0.1:6333/collections +systemctl status qdrant +``` + +## Caveat — crash-loop silencieux + +Si un segment de collection est corrompu, Qdrant panique au boot et reste en `activating (auto-restart)` — **invisible dans `systemctl --state=failed`**. Symptôme : port 6333 fermé, `rag-query` en connection refused. Réparation : supprimer la collection corrompue et ré-ingérer (voir `admin/incidents.md` 2026-06-05). diff --git a/ansible/roles/rag/README.md b/ansible/roles/rag/README.md new file mode 100644 index 0000000..7b4f04f --- /dev/null +++ b/ansible/roles/rag/README.md @@ -0,0 +1,35 @@ +# rag + +Déploie la chaîne RAG sur storage-01 : scripts d'ingestion/requête + copie des docs + skill Hermes. + +## Ce que fait le rôle + +- Crée `/srv/data/rag/docs` +- Déploie `/usr/local/bin/rag-ingest` et `/usr/local/bin/rag-query` +- **Synchronise `admin/` du repo vers `/srv/data/rag/docs/`** (copie indexée) +- Déploie le skill `rag-docs` dans les skills globaux Hermes + le profil funk-ai + +## Variables principales + +| Variable | Défaut | +|---|---| +| `rag_docs_dir` | `/srv/data/rag/docs` | +| `qdrant_url` | `http://127.0.0.1:6333` | +| `embed_url` | `http://192.168.10.20:1234/v1/embeddings` (llama-server GPU) | +| `embed_model` | `qwen3-8b` | +| `rag_collection` | `funk-docs` | + +## Workflow après modification de `admin/` + +```bash +ansible-playbook -i inventory.yml playbooks/storage-01.yml --tags rag # re-sync les docs +ssh storage-01 /usr/local/bin/rag-ingest # ré-indexe dans Qdrant +``` + +(`hermes-auto-improve` fait aussi un rsync + rag-ingest en fin de run.) + +## Caveats + +- **Dépendances fortes** : Qdrant up (`:6333`) et llama-server GPU up (`:1234`) — sinon connection refused +- `rag-ingest` parcourt **tout** `/srv/data/rag/docs` y compris `hermes/builtin/` (rapports du bot) — pollution potentielle des résultats, exclusion à prévoir +- Les embeddings viennent du modèle de génération (Qwen3-8B) — un modèle d'embedding dédié (nomic-embed, bge-m3) est dans la roadmap diff --git a/ansible/roles/rocm/README.md b/ansible/roles/rocm/README.md new file mode 100644 index 0000000..9187c2a --- /dev/null +++ b/ansible/roles/rocm/README.md @@ -0,0 +1,27 @@ +# rocm + +Stack GPU AMD sur gpu-01 (AlmaLinux, RX 6700XT) : repos AMD, runtime ROCm, paquets de build pour llama.cpp. + +## Ce que fait le rôle + +- Clé GPG AMD + repos `amdgpu` et `rocm` (latest) +- kernel headers/devel (requis pour amdgpu-dkms) +- Runtime : rocminfo, rocm-hip-runtime, rocm-opencl-runtime, rocblas, hipblaslt +- Paquets build (pour le rôle `llama_server`) : hipblas-devel, rocblas-devel, hip-devel, hipcc, cmake, gcc-c++, patchelf +- `HSA_OVERRIDE_GFX_VERSION=10.3.0` **system-wide** (`/etc/environment`) et dans `profile.d` +- Ajoute le user ansible aux groupes `video` et `render` + +## Variables principales + +| Variable | Défaut | +|---|---| +| `rocm_rhel_version` | `9.4` | +| `hsa_override_gfx_version` | `10.3.0` | + +## ⚠️ Contrainte critique + +La RX 6700XT (**gfx1031**) n'est pas supportée officiellement par ROCm. Sans `HSA_OVERRIDE_GFX_VERSION=10.3.0` (la faire passer pour une gfx1030), **tout calcul GPU échoue**. La variable doit être présente dans l'environnement de chaque service GPU (le template systemd de llama_server la définit aussi). + +## Caveat + +Les repos pointent vers `latest` — un `make apply-gpu` peut tirer une nouvelle version majeure de ROCm. Après upgrade kernel ou ROCm, vérifier `rocm-smi` et un completion de test avant de considérer le nœud sain (voir `admin/ia/rocm.md`).