docs: réorganisation complète de admin/ en 4 domaines thématiques

Structure avant : 20 fichiers à plat dans admin/ — difficile à naviguer.
Structure après : 4 sous-répertoires thématiques + index clair.

Réorganisation :
  admin/ops/    → cluster.md, ansible.md, systeme.md
  admin/infra/  → reseau.md, nfs.md, dnsmasq.md, ssh.md
  admin/k8s/    → talos.md, argocd.md, monitoring.md
  admin/ia/     → llama_server.md, rocm.md, litellm.md, hermes.md

Suppressions :
  - ask-agent.md : contenu fusionné dans ia/hermes.md (section ask-agent)
  - lm_studio.md : obsolète (LM Studio remplacé par llama-server)

Mises à jour contenu :
  - ia/hermes.md : fusion complète avec ask-agent.md (profils, skills,
    SOUL.md, ask-agent CLI, dépannage) — doc unifiée sans redondance
  - ops/cluster.md : section GitOps réduite à 2 lignes + lien argocd.md
  - incidents.md : tableau de résumé en tête + 4 nouveaux incidents
    (Grafana OOMKilled, AlertManager null receiver, llama-server 501,
    nftables règle après drop)
  - README.md : réécrit — navigation rapide + index par domaine

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
alkatrazz 2026-05-14 00:47:58 +02:00
parent d552abd2ef
commit 5bcf95b82e
19 changed files with 633 additions and 677 deletions

120
admin/infra/dnsmasq.md Normal file
View file

@ -0,0 +1,120 @@
# dnsmasq — DNS local lab.local
dnsmasq tourne sur **storage-01** (`192.168.10.1:53`).
Il répond pour `*.lab.local` et forward le reste vers `8.8.8.8`.
---
## Statut et logs
```bash
# Statut du service
ssh s01 "sudo systemctl status dnsmasq"
# Logs en temps réel
ssh s01 "sudo tail -f /var/log/dnsmasq.log"
# Logs systemd
ssh s01 "sudo journalctl -u dnsmasq -f"
ssh s01 "sudo journalctl -u dnsmasq --since '10 minutes ago'"
```
---
## Tester la résolution DNS
```bash
# Depuis le poste perso — tester via storage-01 directement
dig @192.168.1.200 gpu-01.lab.local +short
dig @192.168.1.200 storage-01.lab.local +short
dig @192.168.1.200 google.com +short
# Depuis storage-01
ssh s01 "dig @192.168.10.1 gpu-01.lab.local +short"
ssh s01 "nslookup gpu-01.lab.local 192.168.10.1"
# Depuis gpu-01 (doit utiliser 192.168.10.1 automatiquement)
ssh g01 "dig gpu-01.lab.local +short"
ssh g01 "dig storage-01.lab.local +short"
ssh g01 "dig google.com +short"
# Tester un service k8s futur (ex: open-webui)
dig @192.168.10.1 open-webui.lab.local +short
```
---
## Config et rechargement
```bash
# Voir la config actuelle
ssh s01 "cat /etc/dnsmasq.conf"
# Tester la config sans redémarrer
ssh s01 "sudo dnsmasq --test"
# Recharger après modif manuelle
ssh s01 "sudo systemctl restart dnsmasq"
# Via Ansible (idempotent, recommandé)
cd /mnt/nvme0n1/nvme0n1p3/Projets/lab/ansible
../.venv/bin/ansible-playbook playbooks/storage-01.yml --tags dnsmasq
```
---
## Ajouter une entrée DNS (ex: nouveau service k8s)
Modifier `ansible/roles/dnsmasq/defaults/main.yml` :
```yaml
dns_cluster_hosts:
- name: storage-01
ip: 192.168.10.1
# ... ajouter ici
- name: mon-service
ip: 192.168.10.200 # IP MetalLB
```
Puis : `make apply-storage`
Pour les services k8s exposés via Traefik/MetalLB, une seule entrée wildcard suffira :
```
address=/.lab.local/192.168.10.200
```
(à ajouter dans le template quand MetalLB sera en place)
---
## Problème connu — crash au boot (interface USB ethernet)
L'interface cluster `enp6s0f3u2c2` est un adaptateur USB ethernet qui prend plus
de temps à apparaître que `network.target`. dnsmasq peut démarrer avant qu'elle
soit disponible et crasher avec `unknown interface enp6s0f3u2c2`.
**Fix permanent** : override systemd déployé via Ansible :
```ini
# /etc/systemd/system/dnsmasq.service.d/wait-for-network.conf
[Unit]
After=network-online.target
Wants=network-online.target
```
**Fix immédiat** (si dnsmasq est en état `failed` après un reboot) :
```bash
sudo systemctl reset-failed dnsmasq
sudo systemctl start dnsmasq
```
**Symptôme** : les machines du LAN cluster (gpu-01...) ne peuvent plus résoudre
de noms DNS → `yum makecache` bloque, `nslookup` timeout.
---
## Vérifier le DNS configuré sur chaque machine
```bash
# storage-01
ssh s01 "cat /etc/resolv.conf && nmcli dev show enp6s0f3u2c2 | grep DNS"
# gpu-01 (doit pointer sur 192.168.10.1)
ssh g01 "cat /etc/resolv.conf && nmcli dev show enp4s0 | grep DNS"
```

113
admin/infra/nfs.md Normal file
View file

@ -0,0 +1,113 @@
# NFS — Commandes d'administration
Le RAID5 `md127` (4×1 TB = 2.7 TB utiles) est monté sur **storage-01** à `/srv/data`
et exporté en NFS vers le LAN cluster (`192.168.10.0/24`).
---
## Côté serveur (storage-01)
```bash
# État du RAID
sudo mdadm --detail /dev/md127
cat /proc/mdstat
# Exports actifs
sudo exportfs -v
# Recharger les exports après modification de /etc/exports
sudo exportfs -ra
# Statut NFS
sudo systemctl status nfs-server
```
### Arborescence du RAID
```
/srv/data/
├── nfs/
│ └── k8s/ → export pour le NFS provisioner Kubernetes
├── models/ → modèles GGUF partagés (gpu-01 monte ce répertoire via NFS)
├── hermes/ → données persistantes Hermes Agent (à venir)
├── postgres/ → données PostgreSQL (à venir)
├── qdrant/ → données Qdrant (à venir)
└── minio/ → données MinIO (à venir)
```
### Exports (`/etc/exports`)
```
/srv/data/nfs/k8s 192.168.10.0/24(rw,sync,no_subtree_check,no_root_squash)
/srv/data/models 192.168.10.0/24(rw,sync,no_subtree_check,no_root_squash)
```
---
## Côté client (gpu-01)
```bash
# État des automounts
sudo systemctl status mnt-nfs.automount
sudo systemctl status mnt-models.automount
# Monter manuellement (debug)
sudo mount -t nfs -o nfsvers=4 192.168.10.1:/srv/data/nfs/k8s /mnt/nfs
# Vérifier les montages actifs
mount | grep nfs
df -h /mnt/nfs /mnt/models
```
### Comportement automount (extinctions fréquentes)
Les montages NFS sur gpu-01 utilisent `x-systemd.automount` :
- **Pas montés au boot** — montés uniquement au premier accès
- Si storage-01 est éteint au moment du boot → gpu-01 démarre normalement
- Au premier accès → montage déclenché (timeout 30 s)
- `soft` + `timeo=30` + `retrans=3` → jamais bloquant si le serveur est injoignable
---
## Modèles GGUF sur NFS
```bash
# Les modèles sont montés directement sur gpu-01
ls /mnt/models/
# → bartowski/
# → lmstudio-community/
# Ajouter un modèle (depuis gpu-01 — sera stocké directement sur NFS)
ssh g01 "lms get <modele>"
```
---
## Ajouter un nouveau répertoire partagé
1. Créer le répertoire sur storage-01 :
```bash
sudo mkdir -p /srv/data/<nom>
sudo chown <uid>:<gid> /srv/data/<nom>
```
2. Ajouter dans `host_vars/storage-01/vars.yml` :
```yaml
nfs_exports:
- path: /srv/data/<nom>
network: "{{ cluster_network }}"
options: "rw,sync,no_subtree_check,no_root_squash"
```
3. Ajouter le montage client dans `host_vars/<host>/vars.yml` :
```yaml
nfs_client_mounts:
- local_path: /mnt/<nom>
remote_export: /srv/data/<nom>
```
4. Appliquer :
```bash
ansible-playbook -i inventory.yml playbooks/storage-01.yml
ansible-playbook -i inventory.yml playbooks/<host>.yml
```

138
admin/infra/reseau.md Normal file
View file

@ -0,0 +1,138 @@
# Réseau — nftables, routage, NAT
Tout ce qui suit s'exécute sur **storage-01** sauf mention contraire.
---
## Interfaces réseau storage-01
| Interface | IP | Rôle |
|---|---|---|
| `enp4s0` | `192.168.1.200` | WAN — vers Freebox (LAN domestique) |
| `enp6s0f3u2c2` | `192.168.10.1` | LAN cluster — vers machines |
> Noms contre-intuitifs : `enp6s0f3u2c2` est une carte USB-Ethernet (cluster),
> `enp4s0` est la carte PCIe (WAN).
---
## Route statique poste perso
Pour atteindre le LAN cluster depuis le poste (`192.168.1.10`) :
```bash
# Route permanente (NetworkManager)
nmcli connection modify enp12s0 +ipv4.routes "192.168.10.0/24 192.168.1.200"
nmcli connection up enp12s0
# Vérifier
ip route | grep 192.168.10
ping 192.168.10.20 # gpu-01
```
La route statique Freebox (`192.168.10.0/24 → 192.168.1.200`) est aussi configurée
dans l'interface Freebox pour que tous les équipements du LAN domestique puissent
atteindre le cluster.
---
## nftables
Géré par Ansible — rôle `gateway`, template `nftables.conf.j2`.
**Ne jamais modifier manuellement** — les changements seront écrasés au prochain `ansible-playbook`.
```bash
# Voir toutes les règles actives
sudo nft list ruleset
# Voir les paquets bloqués (logs kernel)
sudo journalctl -k | grep nft-drop
# Recharger après modification manuelle d'urgence
sudo systemctl reload nftables
# Appliquer via Ansible (méthode correcte)
ansible-playbook -i inventory.yml playbooks/storage-01.yml --tags gateway
```
### Ports ouverts
| Port | Proto | Source | Service |
|---|---|---|---|
| 22 | TCP | 192.168.1.0/24, 192.168.10.0/24 | SSH |
| 53 | TCP/UDP | 192.168.10.0/24 | DNS (dnsmasq) |
| 111, 2049 | TCP/UDP | 192.168.10.0/24 | NFS |
| 5432 | TCP | 192.168.10.0/24 | PostgreSQL |
| 6333, 6334 | TCP | 192.168.10.0/24 | Qdrant |
| 8080 | TCP | 192.168.1.0/24, 192.168.10.0/24 | Hermes gateway |
| 9000, 9001 | TCP | 192.168.10.0/24 | MinIO |
| 9093 | TCP | 192.168.10.0/24 | AlertManager webhook (réception alertes Prometheus) |
| 9100 | TCP | 192.168.10.0/24 | node_exporter (métriques Prometheus — inclut ROCm via textfile_collector) |
| 9119 | TCP | 192.168.1.10 uniquement | Hermes dashboard |
> **Règle nftables importante** : les règles doivent être insérées **avant** la règle `drop` terminale.
> Utiliser `nft insert rule inet filter input position <handle_avant_drop>` et non `nft add rule`
> (qui ajoute en fin de chaîne, après le drop).
> Vérifier l'ordre : `sudo nft -n -a list chain inet filter input`
### Forwarding
```
LAN domestique (192.168.1.0/24) → LAN cluster (192.168.10.0/24) : new+established (admin)
LAN cluster → WAN : new+established (NAT sortant pour les nœuds)
WAN → LAN cluster : established+related uniquement (réponses NAT)
```
---
## IP Forwarding
```bash
# Vérifier (doit être = 1)
sysctl net.ipv4.ip_forward
```
---
## DNS — /etc/hosts storage-01
```bash
cat /etc/hosts
# 192.168.10.20 gpu-01 ← ajouté manuellement (dnsmasq non utilisé par storage-01 lui-même)
```
> storage-01 utilise 1.1.1.1 / 9.9.9.9 comme resolvers, pas son propre dnsmasq.
> Les entrées critiques doivent être dans `/etc/hosts`.
---
## Debug NAT / forwarding
```bash
# Depuis gpu-01 — vérifier que le trafic sort par storage-01
ssh gpu-01 "curl -s https://ifconfig.me" # doit retourner l'IP publique Freebox
ssh gpu-01 "traceroute 8.8.8.8"
# Paquets passant par le NAT
sudo nft list table ip nat
# Tester le routage depuis le poste perso
traceroute 192.168.10.20
ping 192.168.10.11 # compute-01
```
---
## Commandes utiles
```bash
# Table de routage storage-01
ip route show
# Stats trafic interfaces
ip -s link show enp4s0
ip -s link show enp6s0f3u2c2
# Connexions actives
ss -tnp
```

70
admin/infra/ssh.md Normal file
View file

@ -0,0 +1,70 @@
# SSH — Connexions et transferts
Les alias sont définis dans `~/.ssh/config`.
---
## Connexions rapides
```bash
ssh s01 # storage-01 (192.168.1.200) — user ansible
ssh g01 # gpu-01 (192.168.10.20) — via ProxyJump s01 automatique
ssh c01 # compute-01 — via ProxyJump s01 (quand installé)
ssh c02 # compute-02 — idem
ssh c03 # compute-03 — idem
```
## Connexion en root (uniquement si nécessaire, avant durcissement)
```bash
ssh -o PasswordAuthentication=yes root@192.168.1.200
```
## Connexion manuelle avec jump host (sans alias)
```bash
ssh -J ansible@192.168.1.200 ansible@192.168.10.20
```
---
## Transferts de fichiers
```bash
# Copier un fichier vers storage-01
scp fichier.txt s01:/tmp/
# Copier depuis gpu-01
scp g01:/var/log/dnsmasq.log /tmp/
# Copier via jump host (sans alias SSH)
scp -o ProxyJump=s01 fichier.txt ansible@192.168.10.20:/tmp/
```
---
## Debug SSH
```bash
# Connexion avec verbosité max (voir pourquoi ça échoue)
ssh -vvv s01
# Tester sans utiliser ~/.ssh/config
ssh -F /dev/null -i ~/.ssh/id_ed25519 ansible@192.168.1.200
# Vérifier quelle clé est utilisée
ssh -v s01 2>&1 | grep "Offering"
```
---
## Clés SSH
```bash
# Voir la clé publique utilisée
cat ~/.ssh/id_ed25519.pub
# Vérifier les clés autorisées sur storage-01
ssh s01 "cat ~/.ssh/authorized_keys"
# Vérifier les clés autorisées sur gpu-01
ssh g01 "cat ~/.ssh/authorized_keys"
```