# Installation storage-01 Guide d'installation complet de storage-01 — de l'ISO AlmaLinux à la stack IA complète (Hermes + LiteLLM + Qdrant + PostgreSQL). Écrit après installation réelle. --- ## Sommaire 1. [Rôle et architecture](#1-rôle-et-architecture) 2. [Installation AlmaLinux 9.7 (base OS)](#2-installation-almalinux-97-base-os) 3. [Configuration réseau initiale (manuelle)](#3-configuration-réseau-initiale-manuelle) 4. [Préparer le poste perso pour Ansible](#4-préparer-le-poste-perso-pour-ansible) 5. [Cloner le repo et initialiser Ansible](#5-cloner-le-repo-et-initialiser-ansible) 6. [Configurer le vault Ansible (secrets)](#6-configurer-le-vault-ansible-secrets) 7. [Jouer le playbook — phase par phase](#7-jouer-le-playbook--phase-par-phase) 8. [Vérifications post-install](#8-vérifications-post-install) 9. [Pièges et incidents rencontrés](#9-pièges-et-incidents-rencontrés) --- ## 1. Rôle et architecture storage-01 est la machine centrale du lab. Elle n'est **pas dans le cluster Kubernetes** — elle est la fondation sur laquelle tout le reste repose. ### Ce qu'elle fait | Fonction | Détail | |---|---| | Passerelle NAT | Forward LAN cluster → internet, LAN domestique → LAN cluster | | DHCP + DNS | dnsmasq pour le LAN cluster (`192.168.10.0/24`) et le wildcard `*.lab.local` | | Stockage RAID5 | 4 disques SATA 1 TB → 2,7 TB utiles, montés sur `/srv/data` | | NFS | Exports vers gpu-01 et le cluster k8s (`/srv/data/nfs/k8s`, `/srv/data/models`) | | Hermes Agent | Agent IA autonome avec mémoire vectorielle | | LiteLLM Proxy | Routage LLM : Qwen local (gpu-01) ↔ Claude API (Anthropic) | | Qdrant | Base vectorielle pour la mémoire de Hermes (`hermes_memory`) | | PostgreSQL 16 | Bases pour Hermes et LiteLLM | ### Interfaces réseau | Interface | IP | Rôle | |---|---|---| | `enp4s0` | `192.168.1.200` | WAN — vers Freebox (LAN domestique `192.168.1.0/24`) | | `enp6s0f3u2c2` | `192.168.10.1` | LAN cluster (`192.168.10.0/24`) — carte USB-Ethernet | > `enp6s0f3u2c2` est une carte USB-Ethernet branchée en permanence — les noms d'interface sont contre-intuitifs. ### RAID5 4 disques SATA 1 TB (Seagate/WD) assemblés en RAID5 software Linux (`md0` ou `md127`). - Capacité utile : ~2,7 TB - Tolérance : 1 disque mort - Point de montage : `/srv/data` - ⚠️ `sdb` a généré des erreurs I/O après une coupure de courant — surveiller avec `smartctl` --- ## 2. Installation AlmaLinux 9.7 (base OS) ### Télécharger l'ISO AlmaLinux 9.7 minimal (pas de desktop) : `https://repo.almalinux.org/almalinux/9/isos/x86_64/` Flasher sur clé USB : ```bash sudo dd if=AlmaLinux-9.7-x86_64-minimal.iso of=/dev/sdX bs=4M status=progress conv=fsync ``` ### Paramètres d'installation (Anaconda) Dans l'installeur graphique Anaconda : **Langue / clavier** : Français ou English — peu importe, Ansible configure le reste. **Disque système** : sélectionner le NVMe interne uniquement. - Ne pas sélectionner les disques SATA (RAID5) — ils sont gérés manuellement. - Partitionnement automatique convient (LVM sur NVMe). **Réseau** : configurer uniquement l'interface WAN (`enp4s0`) pendant l'install : - IP statique : `192.168.1.200/24` - Gateway : `192.168.1.254` (Freebox) - DNS : `1.1.1.1` - Hostname : `storage-01` L'interface LAN (`enp6s0f3u2c2`) sera configurée par Ansible. **Utilisateurs** : - Root : définir un mot de passe temporaire (sera désactivé par Ansible) - Créer un premier utilisateur `ansible` (ou laisser Ansible le créer — dans ce cas se connecter en root pour la première exécution) ### Premier boot Après l'installation, vérifier que la machine est joignable depuis le poste perso : ```bash ping 192.168.1.200 ssh root@192.168.1.200 ``` --- ## 3. Configuration réseau initiale (manuelle) Avant de lancer Ansible, il faut que storage-01 soit SSH-accessible et que les deux interfaces réseau soient opérationnelles. Ces opérations sont faites une seule fois depuis la console ou root SSH. ### Configurer l'interface LAN (USB-Ethernet) ```bash # Se connecter en root ssh root@192.168.1.200 # Identifier la carte USB-Ethernet (généralement enp6s0f3u2c2 ou similaire) nmcli device status ip link show # Créer la connexion NetworkManager nmcli connection add type ethernet \ ifname enp6s0f3u2c2 \ con-name lan-cluster \ ipv4.method manual \ ipv4.addresses 192.168.10.1/24 \ ipv4.never-default yes \ connection.autoconnect yes nmcli connection up lan-cluster ``` Vérifier : ```bash ip addr show enp6s0f3u2c2 # → inet 192.168.10.1/24 ``` ### Activer l'IP forwarding ```bash echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.d/99-forwarding.conf sysctl -p /etc/sysctl.d/99-forwarding.conf ``` > Ansible gère aussi l'IP forwarding, mais l'activer maintenant permet aux nœuds > Talos de sortir sur internet dès que dnsmasq et nftables seront en place. ### Ajouter gpu-01 dans /etc/hosts (si gpu-01 sera installé avant dnsmasq) ```bash echo '192.168.10.20 gpu-01' >> /etc/hosts ``` --- ## 4. Préparer le poste perso pour Ansible Tout Ansible se lance depuis le **poste perso** (ou storage-01 une fois qu'il est opérationnel — voir section suivante). ### Installer Python et créer le venv ```bash # Python 3 doit être disponible python3 --version # Créer le venv à la racine du repo cd /mnt/nvme0n1/nvme0n1p3/Projets/lab python3 -m venv .venv source .venv/bin/activate # Installer ansible-core pip install -r requirements.txt # ansible-core>=2.17,<2.18 ``` ### Installer les collections Ansible ```bash .venv/bin/ansible-galaxy collection install -r ansible/requirements.yml ``` ### Générer une clé SSH pour Ansible ```bash # Si pas encore de clé ed25519 ssh-keygen -t ed25519 -C "ansible@poste-perso" -f ~/.ssh/id_ed25519 # Copier la clé publique vers storage-01 ssh-copy-id -i ~/.ssh/id_ed25519.pub root@192.168.1.200 ``` Ansible utilisera le compte `ansible` (créé par le rôle `common`) — mais pour la première exécution, il se connecte en root. ### Configurer ~/.ssh/config ``` Host s01 HostName 192.168.1.200 User ansible IdentityFile ~/.ssh/id_ed25519 Host g01 HostName 192.168.10.20 User ansible ProxyJump s01 IdentityFile ~/.ssh/id_ed25519 StrictHostKeyChecking no ``` --- ## 5. Cloner le repo et initialiser Ansible ```bash git clone git@github.com:Alkatrazz24/Funk-lab.git cd Funk-lab python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt .venv/bin/ansible-galaxy collection install -r ansible/requirements.yml ``` ### Structure Ansible du repo ``` ansible/ ├── inventory.yml # hosts + groupes ├── playbooks/ │ ├── site.yml # import storage-01.yml + gpu-01.yml │ ├── storage-01.yml # playbook storage-01 │ └── gpu-01.yml # playbook gpu-01 ├── group_vars/ │ ├── all/ │ │ ├── vars.yml # variables globales (dns_domain, cluster_network...) │ │ └── vault.yml # secrets chiffrés (Ansible Vault) │ └── gateway/ │ └── vars.yml # interfaces réseau storage-01 ├── host_vars/ │ ├── storage-01/ │ │ └── vars.yml # RAID uuid, NFS exports, chemins services │ └── gpu-01/ │ └── vars.yml # modèle llama-server, montages NFS └── roles/ ├── common/ # hostname, timezone, EPEL, user ansible, SSH hardenin ├── gateway/ # nftables + NAT + IP forwarding ├── dnsmasq/ # DNS lab.local + DHCP cluster ├── nfs_server/ # RAID5 fstab + exports NFS ├── postgresql/ # PostgreSQL 16 sur RAID5 ├── qdrant/ # Qdrant vectordb sur RAID5 ├── litellm/ # LiteLLM proxy + hermes-switch ├── hermes_agent/ # Hermes Agent + Dashboard ├── rocm/ # ROCm AMD pour gpu-01 ├── llama_server/ # llama-server sur gpu-01 └── nfs_client/ # montages NFS automatiques (gpu-01) ``` ### Variables globales (`group_vars/all/vars.yml`) ```yaml dns_domain: lab.local ntp_servers: - 0.fr.pool.ntp.org - 1.fr.pool.ntp.org cluster_network: 192.168.10.0/24 wan_gateway: 192.168.1.254 ``` ### Variables gateway (`group_vars/gateway/vars.yml`) ```yaml wan_interface: enp4s0 # NIC PCIe — vers Freebox lan_interface: enp6s0f3u2c2 # NIC USB — vers LAN cluster wan_ip: 192.168.1.200 lan_ip: 192.168.10.1 ``` ### Variables storage-01 (`host_vars/storage-01/vars.yml`) ```yaml ansible_host: 192.168.1.200 raid_uuid: "3add8360-fa01-47eb-8aa1-0e84bacbbc15" raid_mount: /srv/data nfs_exports: - path: /srv/data/nfs/k8s network: "{{ cluster_network }}" options: "rw,sync,no_subtree_check,no_root_squash" - path: /srv/data/models network: "{{ cluster_network }}" options: "rw,sync,no_subtree_check,no_root_squash" hermes_data_dir: /srv/data/hermes postgres_data_dir: /srv/data/postgres qdrant_data_dir: /srv/data/qdrant ``` --- ## 6. Configurer le vault Ansible (secrets) Le vault contient les mots de passe et l'API key Anthropic. Il est chiffré dans git. ### Fichier mot de passe vault ```bash # Créer le fichier de mot de passe (ne jamais le committer — dans .gitignore) echo "ton-mot-de-passe-vault" > .vault_pass chmod 600 .vault_pass ``` `ansible.cfg` pointe automatiquement dessus : ```ini [defaults] vault_password_file = .vault_pass ``` ### Créer le vault avec les secrets ```bash cd ansible/ ../.venv/bin/ansible-vault create group_vars/all/vault.yml ``` Contenu du vault : ```yaml vault_anthropic_api_key: "sk-ant-..." vault_pg_hermes_password: "mot-de-passe-fort" vault_pg_litellm_password: "mot-de-passe-fort" ``` Pour modifier plus tard : ```bash make vault-edit # ou cd ansible && ../.venv/bin/ansible-vault edit group_vars/all/vault.yml ``` --- ## 7. Jouer le playbook — phase par phase Il est recommandé d'appliquer les rôles dans l'ordre ci-dessous, par petits blocs, pour valider chaque couche avant de passer à la suivante. ### Test de connectivité ```bash make ping # ou cd ansible && ../.venv/bin/ansible all -m ping ``` Sortie attendue : ``` storage-01 | SUCCESS => {"ping": "pong"} ``` > gpu-01 échouera si pas encore installé — normal. ### Phase 1 — Base OS ```bash cd ansible && ../.venv/bin/ansible-playbook playbooks/storage-01.yml --tags common ``` Le rôle `common` fait : - Hostname → `storage-01` - Timezone → Europe/Paris - EPEL activé - `dnf update -y` (tous les packages à jour) - Packages de base : `vim`, `curl`, `wget`, `git`, `htop`, `tmux`, `rsync`, `lsof`, `net-tools`, `bind-utils` - Création user `ansible` avec sudo NOPASSWD - Clé SSH publique du poste perso déposée dans `~/.ssh/authorized_keys` - SSH durci : `PermitRootLogin no`, `PasswordAuthentication no`, `X11Forwarding no` Après cette étape, les connexions root ne fonctionnent plus — utiliser `ssh s01`. ### Phase 2 — Passerelle (nftables + NAT) ```bash cd ansible && ../.venv/bin/ansible-playbook playbooks/storage-01.yml --tags gateway ``` Le rôle `gateway` fait : - Déploie `/etc/nftables.conf` depuis le template (flush + rebuild complet) - Active IP forwarding (`sysctl net.ipv4.ip_forward=1`) - Active et redémarre `nftables` Vérifier que le NAT fonctionne depuis le LAN cluster : ```bash # Depuis une machine du cluster (si déjà installée) ssh 192.168.10.11 "curl -s https://ifconfig.me" ``` ### Phase 3 — DNS + DHCP (dnsmasq) ```bash cd ansible && ../.venv/bin/ansible-playbook playbooks/storage-01.yml --tags dnsmasq ``` Le rôle `dnsmasq` fait : - Installe `dnsmasq` - Déploie la config depuis template : DNS `lab.local`, DHCP `192.168.10.50-99`, baux statiques MACs - Wildcard `*.lab.local → 192.168.10.200` (Traefik) - Override systemd : dnsmasq attend que l'interface USB-Ethernet soit prête avant de démarrer Tester depuis le poste perso : ```bash dig @192.168.10.1 storage-01.lab.local +short # → 192.168.10.1 dig @192.168.10.1 test.lab.local +short # → 192.168.10.200 (wildcard) ``` > Sans l'override systemd, dnsmasq démarre avant que `enp6s0f3u2c2` (USB-Ethernet) > soit disponible → `bind(): Address not available` → service en échec au boot. ### Phase 4 — NFS + RAID5 ```bash cd ansible && ../.venv/bin/ansible-playbook playbooks/storage-01.yml --tags nfs_server ``` Le rôle `nfs_server` fait : - Installe `nfs-utils` et `mdadm` - Persiste la config RAID dans `/etc/mdadm.conf` (pour survie aux reboots) - Ajoute `/srv/data` dans `/etc/fstab` avec `_netdev 0 0` - Crée les répertoires d'export (`/srv/data/nfs/k8s`, `/srv/data/models`) - Déploie `/etc/exports` et recharge `nfs-server` Vérifier : ```bash ssh s01 "cat /proc/mdstat" # RAID doit être [4/4] [UUUU] ssh s01 "sudo exportfs -v" # doit lister les deux exports ssh s01 "df -h /srv/data" # doit afficher ~2.7 TB ``` > **UUID du RAID** : récupérer avec `sudo blkid | grep md` sur storage-01 et > mettre à jour `host_vars/storage-01/vars.yml` avant de jouer ce rôle. ### Phase 5 — PostgreSQL 16 ```bash cd ansible && ../.venv/bin/ansible-playbook playbooks/storage-01.yml --tags postgresql ``` Le rôle `postgresql` fait : - Installe le repo officiel PGDG (EL9) - Désactive le module AppStream par défaut (`dnf module disable postgresql`) - Installe `postgresql16-server` et `python3-psycopg2` - Crée `/srv/data/postgres` sur le RAID5 et le configure comme `PGDATA` - Override systemd pour pointer sur le bon `PGDATA` - `initdb` si la base n'existe pas encore - Crée les bases `hermes` et `litellm` et leurs users avec les mots de passe du vault Vérifier : ```bash ssh s01 "sudo -u postgres psql -c '\l'" # doit lister : hermes, litellm, postgres, template0, template1 ``` ### Phase 6 — Qdrant ```bash cd ansible && ../.venv/bin/ansible-playbook playbooks/storage-01.yml --tags qdrant ``` Le rôle `qdrant` fait : - Télécharge le binaire Qdrant (release GitHub) - Crée un user système `qdrant` - Configure le stockage dans `/srv/data/qdrant` - Déploie `qdrant.service` (bind `0.0.0.0:6333`, gRPC `6334`) - Active et démarre le service Vérifier : ```bash ssh s01 "curl -s http://localhost:6333/healthz" # → {"result":"ok","status":"ok","time":0.00...} ``` ### Phase 7 — LiteLLM Proxy ```bash cd ansible && ../.venv/bin/ansible-playbook playbooks/storage-01.yml --tags litellm ``` Le rôle `litellm` fait : - Installe `litellm` via pip dans un venv dédié - Déploie `/etc/litellm/config.yaml` avec les modèles (Qwen local + Claude) - Configure `master_key: lm-studio` (aligné avec `LM_API_KEY=lm-studio` dans Hermes) - Déploie `litellm.service` (bind `127.0.0.1:4000` — jamais exposé sur le réseau) - Déploie le script `hermes-switch` dans `/usr/local/bin/` Config LiteLLM (résumé) : ```yaml model_list: - model_name: hermes-default litellm_params: model: openai/qwen3-8b api_base: http://192.168.10.20:1234/v1 api_key: lm-studio - model_name: claude-sonnet-4-6 litellm_params: model: anthropic/claude-sonnet-4-6 api_key: "{{ vault_anthropic_api_key }}" general_settings: master_key: lm-studio ``` Vérifier : ```bash ssh s01 "curl -s http://localhost:4000/health -H 'Authorization: Bearer lm-studio'" ``` ### Phase 8 — Hermes Agent ```bash cd ansible && ../.venv/bin/ansible-playbook playbooks/storage-01.yml --tags hermes_agent ``` Le rôle `hermes_agent` fait : - Crée un user système `hermes` avec home sur le RAID5 (`/srv/data/hermes`) - Installe Hermes via son installer officiel dans `/opt/hermes` - Crée le venv Python (`/srv/data/hermes/hermes-agent/venv/`) avec `qdrant-client` - Déploie `~hermes/.config/hermes/config.yaml` : - `model.provider: lmstudio` - `model.base_url: http://127.0.0.1:4000/v1` - `model.name: hermes-default` - Déploie `hermes-agent.service` et `hermes-dashboard.service` - Déploie `/usr/local/bin/hermes-tui` (wrapper pour TUI interactif) - Permissions `/opt/hermes` → `711` (traversable, non listable) Vérifier : ```bash ssh s01 "sudo systemctl status hermes-agent hermes-dashboard" ``` Dashboard accessible depuis le poste perso (restreint à `192.168.1.10`) : ``` http://192.168.1.200:9119 ``` TUI interactif (doit être lancé sous le compte hermes) : ```bash ssh storage-01 sudo -i -u hermes cd /srv/data/hermes hermes --tui ``` --- ## 8. Vérifications post-install ### Checklist complète ```bash # 1. Réseau ping 192.168.10.20 # gpu-01 joignable ssh g01 "curl -s https://ifconfig.me" # NAT fonctionnel dig @192.168.10.1 storage-01.lab.local +short # DNS lab.local # 2. RAID ssh s01 "cat /proc/mdstat" # [4/4] [UUUU] ssh s01 "df -h /srv/data" # ~2.7 TB monté # 3. NFS ssh s01 "sudo exportfs -v" # exports actifs # 4. PostgreSQL ssh s01 "sudo -u postgres psql -c '\l'" # bases hermes + litellm présentes # 5. Qdrant ssh s01 "curl -s http://localhost:6333/healthz" | python3 -m json.tool # 6. LiteLLM ssh s01 "curl -s http://localhost:4000/health -H 'Authorization: Bearer lm-studio'" # 7. Hermes ssh s01 "sudo systemctl is-active hermes-agent hermes-dashboard" # 8. funk-cluster (script de gestion global) ssh s01 "sudo /usr/local/bin/funk-cluster status" ``` ### Services actifs attendus ```bash ssh s01 "sudo systemctl list-units --type=service --state=running | grep -E 'dnsmasq|nfs|postgres|qdrant|litellm|hermes|nftables'" ``` --- ## 9. Pièges et incidents rencontrés --- ### ❌ dnsmasq ne démarre pas au boot — "Address not available" **Symptôme** : `systemctl status dnsmasq` → `bind(): Address not available`. Storage-01 rebooté → dnsmasq en `failed`, plus de DHCP pour le cluster. **Cause** : dnsmasq tentait de binder sur `192.168.10.1` (`enp6s0f3u2c2`, USB-Ethernet) avant que cette interface soit disponible. L'USB-Ethernet est détectée plus tard que les NIC PCIe dans la séquence de boot. **Fix** : Un override systemd dans `/etc/systemd/system/dnsmasq.service.d/wait-lan.conf` : ```ini [Unit] After=network-online.target sys-subsystem-net-devices-enp6s0f3u2c2.device Wants=network-online.target sys-subsystem-net-devices-enp6s0f3u2c2.device ``` Géré par le rôle Ansible `dnsmasq` — ne pas modifier manuellement. --- ### ❌ Boucle fsck au boot après coupure de courant **Symptôme** : Boot bloqué sur `emergency mode`, impossible de saisir le mot de passe root (messages d'erreur défilants), le RAID `/dev/md0` ne monte plus. **Cause** : Coupure de courant → corruption du filesystem XFS sur `almalinux-root` (NVMe). systemd tentait de monter `/srv/data` (RAID5) avant que le RAID soit assemblé, créant une boucle bloquante. **Résolution** (procédure complète dans `admin/incidents.md`) : 1. Shell via GRUB (`init=/bin/bash` dans la ligne kernel) 2. `lvm vgchange -ay` pour activer le LVM 3. `xfs_repair /dev/mapper/almalinux-root` (pas `fsck` — XFS a son propre outil) 4. Commenter `/srv/data` dans `/etc/fstab` temporairement pour booter 5. Après boot : corriger fstab → ajouter `_netdev 0 0` pour `/srv/data` **Fix permanent dans fstab** : ``` UUID=3add8360-... /srv/data ext4 defaults,noatime,nodiratime,_netdev 0 0 ``` - `_netdev` : attend que les périphériques soient prêts (RAID assemblé) avant de monter - `0 0` : désactive le `fsck` automatique au boot (le RAID a sa propre intégrité) Géré par le rôle Ansible `nfs_server`. Vérifier que ces options sont bien présentes. --- ### ❌ hermes --tui "gateway error" si lancé sous le mauvais compte **Symptôme** : `hermes --tui` lancé depuis le compte `ansible` retourne des "gateway error" en boucle. TUI inutilisable. **Cause** : Hermes charge sa configuration depuis `$HERMES_HOME`, qui doit pointer vers `/srv/data/hermes`. Lancé sous `ansible`, les variables d'environnement ne sont pas correctes et `hermes` cherche sa config dans `~ansible/.config/hermes/` qui n'existe pas. **Fix** : Toujours lancer le TUI sous le compte `hermes` : ```bash ssh storage-01 sudo -i -u hermes cd /srv/data/hermes hermes --tui ``` Ou via le wrapper `/usr/local/bin/hermes-tui` qui gère automatiquement l'environnement. --- ### ❌ qdrant-client : méthode search() supprimée en v1.14+ **Symptôme** : Scripts Python Hermes qui appellent Qdrant échouent avec `AttributeError: 'AsyncQdrantClient' object has no attribute 'search'`. **Cause** : `qdrant-client` v1.14+ a supprimé la méthode `search()`. Elle est remplacée par `query_points()`. **Fix** : Mettre à jour les scripts pour utiliser `query_points()`. Le rôle Ansible `hermes_agent` installe la version compatible — ne pas faire `pip install qdrant-client` manuellement sans vérifier la version. --- ### ❌ LM_API_KEY absent → Hermes ne peut pas appeler LiteLLM **Symptôme** : Hermes démarre mais toutes les requêtes vers LiteLLM échouent avec 401. **Cause** : Hermes utilise `LM_API_KEY` comme Bearer token. LiteLLM attend `master_key`. Les deux doivent avoir la même valeur. Si `LM_API_KEY` n'est pas défini dans l'environnement du service systemd, Hermes envoie un token vide ou incorrect. **Fix** : Le service `hermes-agent.service` doit avoir dans sa section `[Service]` : ```ini Environment=LM_API_KEY=lm-studio ``` La valeur `lm-studio` est une valeur magique alignée avec `master_key: lm-studio` dans la config LiteLLM. Géré par le rôle Ansible. --- ### ❌ hermes -z ne fonctionne pas depuis n'importe où **Symptôme** : `hermes -z "question"` depuis `/home/ansible` → erreur de chemin, Hermes ne trouve pas son `.git`. **Cause** : Hermes remonte l'arborescence à la recherche d'un `.git` pour localiser sa config de projet. Si lancé depuis un répertoire sans `.git` en amont, il échoue. **Fix** : Toujours lancer depuis `/srv/data/hermes` : ```bash cd /srv/data/hermes && hermes -z "ta question" ``` --- ### Résumé des points d'attention | Sujet | Règle | |---|---| | dnsmasq au boot | Override systemd obligatoire pour USB-Ethernet | | fstab `/srv/data` | `_netdev 0 0` — jamais `0 2` | | TUI Hermes | Toujours sous le compte `hermes`, depuis `/srv/data/hermes` | | `hermes -z` | Toujours depuis `/srv/data/hermes` | | LM_API_KEY | Doit valoir `lm-studio` (= `master_key` LiteLLM) | | qdrant-client | Utiliser `query_points()` pas `search()` (supprimé en v1.14+) | | RAID sdb | Surveiller régulièrement avec `smartctl -a /dev/sdb` | | UUID RAID | Vérifier `host_vars/storage-01/vars.yml` avant de jouer `nfs_server` | | Vault | `.vault_pass` ne doit jamais être commité — dans `.gitignore` | --- ## État vérifié — 2026-06-05 - Services : tous actifs (aucun unité échoué) - OS : AlmaLinux 9.7, kernel 5.14.0-687.5.3.el9_8.x86_64 - Stockage : /srv/data (2.7T) monté, /home (845G), /boot (960M) - Mémoire : 29Gi totale, 2.5Gi utilisée - Divergences : Aucune divergence notée entre la documentation et l'état réel - Éléments opérationnels : Aucun pod crashé rapporté, export NFS non vérifié dans les commandes actuelles