- ops/ansible.md : tableau complet des rôles par playbook (15 storage-01, 6 gpu-01),
caveat hermes_agent, note lm_studio inutilisé
- ia/hermes-auto-improve.md : NOUVEAU — doc du worker déployé (trigger :9095,
modes du script, endpoints, PR squash, pièges connus)
- k8s/n8n.md : workflow 3 "Hermes Doc Worker" documenté (22h, /trigger/all,
boucle de polling), clé API → vault
- incidents.md : Qdrant crash-loop depuis 2026-06-05 (segment funk-docs corrompu,
invisible dans --state=failed) + procédure de réparation
- ia/rag.md : bandeau RAG HS
- CLAUDE.md : état 2026-06-10, rôle hermes_auto_improve, 3 workflows n8n,
vault complet (n8n_api_key, Gmail au lieu de Brevo), section auto-improve
- README.md : lien admin/ops/cluster.md corrigé, arborescence complète
(tools/, admin/, progress/, skills), roadmap à jour
- PROGRESS.md : entrée 2026-05-29 (migration NVMe) qui existait sans être référencée
- ansible : suppression effective des templates daily timer (cleanup b9be63c)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
10 KiB
n8n — Automatisation de workflows
n8n est le moteur d'automatisation du lab. Il orchestre les tâches récurrentes, connecte les services entre eux et envoie des rapports/alertes par email via LiteLLM + Postfix.
Accès
| URL | http://n8n.lab.local |
|---|---|
| Namespace k8s | ai |
| Port interne | 5678 |
| API key | générée dans Settings → n8n API — stockée dans le vault Ansible (vault_n8n_api_key) |
Architecture
Déclencheurs Pipeline Sortie
──────────── ──────── ──────
AlertManager webhook → Formatage JSON Email Gmail
Cron (toutes les heures) → Requêtes Prometheus → (via Postfix :25)
Webhook manuel → LiteLLM hermes-default
(résumé/analyse)
SMTP : n8n envoie via Postfix sur 192.168.10.1:25 (pas d'auth, réseau interne). Postfix relaie vers smtp.gmail.com:587 avec App Password.
LLM : n8n appelle LiteLLM sur http://192.168.10.1:4000/v1/chat/completions avec model: hermes-default.
Déploiement (ArgoCD)
Manifests dans k8s/apps/n8n/ :
deployment.yaml— pod n8nservice.yaml— ClusterIP :5678ingress.yaml— IngressRoute Traefikpvc.yaml— données applicatives (NFS)secret.yaml— secrets (non commité, à créer manuellement)
Application ArgoCD : k8s/apps-of-apps/apps/n8n.yaml
Créer le secret (première installation)
kubectl create secret generic n8n-secret -n ai \
--from-literal=db-password="PASSWORD" \
--from-literal=encryption-key="$(openssl rand -hex 32)"
Le mot de passe est dans le vault Ansible (vault_pg_n8n_password).
Credentials configurées
| Nom | Type | Détail |
|---|---|---|
Postfix local |
SMTP | 192.168.10.1:25, sans auth, STARTTLS désactivé |
Pour créer une credential OpenAI/LiteLLM manuellement : Settings → Credentials → New → OpenAI → Base URL:
http://192.168.10.1:4000/v1, API Key:lm-studio
Workflows actifs
1. Alertes Prometheus → Résumé LLM → Gmail
ID : 4lfZ0sHZqQ5HYEph
Déclencheur : Webhook POST sur http://n8n.lab.local/webhook/alerts
Configuré dans : k8s/infra/monitoring/values.yaml (receiver AlertManager)
Pipeline :
AlertManager POST
│
├── Respond to Webhook (répond OK immédiatement)
│
▼
Format Alertes (Code)
→ extrait alertname, instance, severity, summary, description
│
▼
Résumé LLM (HTTP Request → LiteLLM)
→ system: "assistant ops, résumé concis en français"
│
▼
Envoyer Email (SMTP)
→ Subject: [Funk] N alerte(s) — firing/resolved
→ Body: résumé LLM
Test manuel :
curl -X POST http://n8n.lab.local/webhook/alerts \
-H "Content-Type: application/json" \
-d '{
"version":"4","status":"firing","receiver":"n8n-webhook",
"alerts":[{
"status":"firing",
"labels":{"alertname":"HighMemory","instance":"192.168.10.12:9100","severity":"warning"},
"annotations":{"summary":"RAM élevée","description":"RAM > 85%"}
}]
}'
2. Rapport Horaire Système — Funk
ID : i2KAGxBFDps7IjIP
Déclencheur : Cron toutes les heures (:00)
Pipeline :
Schedule (toutes les heures)
│
├── CPU (Prometheus) avg 1h par nœud
├── Mémoire (Prometheus) avg_over_time 1h
├── Mémoire Max (Prometheus) max_over_time 1h
├── Disque (Prometheus) snapshot actuel
├── Services (Prometheus) up/down
└── Services Changes changements d'état sur 1h
│
▼
Format Rapport (Code)
→ tableau métriques par nœud (avg/max mémoire, CPU moyen 1h)
→ liste services stables/instables
│
▼
Analyse LLM (HTTP Request → LiteLLM)
→ system: "résumé état général, points d'attention, recommandation"
│
▼
Préparer Email (Code)
→ assemble : résumé LLM + séparateur + tableau métriques
│
▼
Envoyer Rapport (SMTP)
→ Subject: [Funk] Rapport systeme - DD/MM/YYYY HH:MM:SS
→ Body HTML (pre tag) : résumé + métriques
Queries Prometheus utilisées :
| Nœud | Query |
|---|---|
| CPU | 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[1h])) * 100) |
| Mémoire | avg_over_time((1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)[1h:5m]) * 100 |
| Mémoire Max | max_over_time((1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)[1h:5m]) * 100 |
| Disque | (filesystem_size - filesystem_free) / filesystem_size * 100 (mountpoint=/) |
| Services | up |
| Services Changes | changes(up[1h]) > 0 |
| GPU Power 1h | avg_over_time(rocm_gpu_power_watts{instance="gpu-01"}[1h:5m]) → Wh cette heure |
| GPU Power 24h | avg_over_time(rocm_gpu_power_watts{instance="gpu-01"}[24h:5m]) → kWh 24h glissants |
Section énergie GPU : affichée dans chaque rapport entre l'analyse Hermes et les métriques brutes. À minuit (exécution 00:00), un encart BILAN JOURNALIER met en avant la consommation totale en kWh des 24h écoulées.
3. Hermes Doc Worker — Funk-lab
ID : CX72UUOempE1YtNL
Déclencheurs : Schedule quotidien 22h00 · webhook manuel POST /webhook/hermes-doc-run · (un ancien Schedule 30min existe mais est désactivé)
Pipeline :
Schedule 22h (ou webhook manuel)
│
▼
Déclencher run (HTTP POST storage-01:9095/trigger/all)
→ lance hermes-auto-improve --all (28 docs, ~60-90 min)
│
▼
┌── Attendre run (5 min) ◄────────────┐
│ │ │
│ ▼ │
│ Status run (GET :9095/status) │
│ │ │
│ ▼ │
│ Run terminé ? (IF running=false) ─┘ false → reboucle
│ │ true
└───────▼
Format Run → LLM Run (résumé) → Email Run → Envoyer Run
│
▼
Est 22h ? (IF heure courante = 22)
│ true
▼
Déclencher PR (POST :9095/trigger/daily-pr)
→ crée ou met à jour la PR hermes/daily-work → main (squash-only)
│
▼
Attendre PR (5 min) → Status PR → Format PR → LLM PR → Email PR → Envoyer PR
Backend : trigger server hermes-auto-improve sur storage-01 (:9095), déployé par le rôle Ansible hermes_auto_improve. Voir admin/ia/hermes-auto-improve.md.
Gestion via API : la clé API n8n est dans le vault Ansible (vault_n8n_api_key) :
make vault-view | grep n8n_api_key
curl -s -H "X-N8N-API-KEY: $KEY" http://n8n.lab.local/api/v1/workflows
Workflows à ajouter
Court terme
Alerte disque > 80%
Cron toutes les 30 min → query Prometheus disk > 80 → si résultat non vide → email d'alerte.
Utile car AlertManager ne couvre pas toujours les seuils fins par nœud.
Redémarrage automatique pod crashé
Webhook AlertManager sur KubePodCrashLooping → kubectl rollout restart via HTTP Request vers l'API k8s (ou script SSH sur storage-01) → notification email.
Rapport hebdomadaire étendu
Cron dimanche 09h → même pipeline que le rapport horaire mais avec fenêtre 7 jours ([7d]) + statistiques min/max de la semaine.
Moyen terme
Budget Anthropic API
Cron quotidien → requête API Anthropic usage → si > seuil configuré → email d'alerte.
Évite les surprises de facturation Claude.
Notification nouveaux commits Funk-lab
GitHub webhook → n8n → email/notification résumé du commit.
Utile pour tracker les changements sur d'autres postes.
Monitor llama-server performance
Cron horaire → query Prometheus llamacpp_tokens_per_second → si tokens/s < seuil (GPU dégradé) → alerte email.
Rotation/nettoyage logs
Cron quotidien → SSH storage-01 → journalctl --vacuum-time=30d → rapport taille libérée.
Long terme
Auto-scaling modèle Hermes
Monitor charge LiteLLM → si charge élevée, switcher automatiquement vers claude-sonnet-4-6 via hermes-switch claude → revenir à qwen après N minutes.
RAG re-indexation automatique
Webhook Git (push sur admin/) → déclencher rag-ingest via SSH sur storage-01 → email confirmation nombre de chunks indexés.
Résumé quotidien Hermes
Cron 08h00 → interroger Hermes sur l'état du lab → recevoir un briefing matinal par email avec état services, alertes de la nuit, métriques clés.
Opérations courantes
# Logs n8n
kubectl logs deploy/n8n -n ai -f
# Redémarrer n8n
kubectl rollout restart deploy/n8n -n ai
# Lister les workflows via API
curl -s http://n8n.lab.local/api/v1/workflows \
-H "X-N8N-API-KEY: <clé>" | python3 -m json.tool
# Activer/désactiver un workflow
curl -s -X POST http://n8n.lab.local/api/v1/workflows/<id>/activate \
-H "X-N8N-API-KEY: <clé>"
curl -s -X POST http://n8n.lab.local/api/v1/workflows/<id>/deactivate \
-H "X-N8N-API-KEY: <clé>"
# Dernières exécutions
curl -s "http://n8n.lab.local/api/v1/executions?limit=5" \
-H "X-N8N-API-KEY: <clé>" | python3 -c \
"import sys,json; [print(e['id'], e['status'], e.get('stoppedAt','')) for e in json.load(sys.stdin)['data']]"
Dépannage
| Symptôme | Cause | Action |
|---|---|---|
| Email vide | Expression $json.xxx évaluée à vide dans emailSend |
Utiliser un nœud Code intermédiaire qui assemble le corps, puis emailType: "html" + html: "={{ $json.htmlBody }}" |
| SMTP ETIMEDOUT | Port 25 non ouvert pour les pods | Vérifier nftables : tcp dport 25 ip saddr { 192.168.10.0/24, 10.42.0.0/16 } |
| LLM timeout | hermes-default trop lent (> 2 min) | Passer en mode async : ajouter "Respond to Webhook" en branche parallèle avant le LLM |
| Prometheus query vide | Service DNS interne non résolu | Utiliser l'URL service k8s : http://kube-prometheus-stack-prometheus.monitoring.svc.cluster.local:9090 |
| Workflow ne se déclenche pas | Webhook AlertManager mal configuré | Tester : curl -X POST http://n8n.lab.local/webhook/alerts -d '{...}' |
Configuration Ansible / k8s
- Secrets k8s :
kubectl get secret n8n-secret -n ai - Base de données : PostgreSQL
n8nsur storage-01 (vault_pg_n8n_password) - PVC data : NFS, 5 Gi, claimName
n8n-data - nftables : port 25 ouvert
10.42.0.0/16(pods) dans le rôlegateway - AlertManager : receiver
hermes-monitordansk8s/infra/monitoring/values.yaml