Ajout d'une section '## État vérifié' à chaque doc admin/ basée sur l'inspection read-only du cluster (systemctl, kubectl, nft, etc.). Aucune modification de service ni réécriture de documentation existante. Co-Authored-By: Hermes <hermes@funk.lab>
21 KiB
Incidents — Historique et résolutions
Index
| Date | Composant | Résumé | Sévérité |
|---|---|---|---|
| 2026-05-29 | RAID5 / sdb + storage-01 | sdb ATA bus errors sous I/O — migration /srv/data du RAID vers NVMe | Important |
| 2026-05-27 | RAID5 / sdd | Disque sdd défaillant (44 secteurs morts) — corruptions multiples en cascade | Critique |
| 2026-05-14 | Grafana | OOMKilled — limite 256Mi trop serrée pour Grafana 13 | Warning |
| 2026-05-13 | AlertManager | Receiver "null" non défini — ReconciliationFailed | Warning |
| 2026-05-13 | llama-server | /metrics retourne 501 Not Implemented — flag --metrics absent |
Warning |
| 2026-05-13 | nftables storage-01 | Règle monitoring ajoutée après le drop — paquets bloqués | Warning |
| 2026-05-13 | storage-01 | Corruption EXT4 + réinstallation hermes-agent | Critique |
| 2026-05-11 | storage-01 | Boucle fsck après coupure de courant — XFS corrompu | Critique |
2026-05-29 — Migration /srv/data : RAID5 → NVMe (sdb ATA bus errors)
Contexte
Suite à l'incident du 2026-05-27 (sdd retiré), le RAID5 tournait déjà en dégradé [4/3] [UUU_]. Décision prise de migrer /srv/data du RAID vers le LV NVMe (almalinux-home, 845 GB XFS) pour sortir les données d'un RAID en mauvais état.
Symptômes déclencheurs
Lors du lancement du rsync de migration, le processus se bloque en état D (uninterruptible sleep) :
dmesg:
[ 1341.231068] sd 1:0:0:0: [sdb] FAILED Result: hostbyte=DID_OK
[ 1341.231082] I/O error, dev sdb, sector 582656 op 0x0:(READ)
[ 1358.639065] ata2.00: res 40/00:ff:ff:00:00/... Emask 0x50 (ATA bus error)
[ 1451.541751] md/raid:md0: Disk failure on sdb2, disabling device.
sdb génère des ATA bus errors sous charge I/O soutenue (câble SATA ou port instable). SMART passe (PASSED, aucun secteur défaillant) mais Raw_Read_Error_Rate = 124014439 — problème d'interface, pas de plateau.
Résolution — migration complète
Étape 1 — Stopper les services
sudo systemctl stop hermes-agent hermes-dashboard litellm qdrant postgresql-16 alertmanager-webhook
Étape 2 — Préparer la destination NVMe
sudo mkdir -p /home/data
sudo mount --bind /home/data /srv/data-new # vérification préalable
Étape 3 — Gérer sdb (blocage rsync)
Le premier rsync (avec sdb actif) se bloque en état D. Solution : marquer sdb comme défaillant pour que le RAID lise depuis sda/sdc/sdd uniquement.
sudo kill <pid_rsync_bloqué>
sudo mdadm --manage /dev/md0 --fail /dev/sdb2
# → [4/3] [U_UU] — sdb hors circuit
Étape 4 — Rsync (RAID → NVMe)
sudo nohup rsync -aHX --info=progress2 /srv/data/ /home/data/ > /tmp/rsync-migration.log 2>&1 &
# Durée : ~35s — 205 MB/s — 5106 fichiers — exit code 23 (xattr SELinux non critiques)
Étape 5 — Corriger la corruption Qdrant
Un version.info de segment RocksDB copié depuis le RAID avec sdb défaillant était corrompu :
sudo cat /srv/data/qdrant/storage/collections/funk-docs/0/segments/784eab54-.../version.info
# → 0.D\0 (corrompu — devrait être 0.6.0)
printf '0.6.0' | sudo tee /srv/data/qdrant/storage/collections/funk-docs/0/segments/784eab54-.../version.info
Étape 6 — Basculer /srv/data vers NVMe
sudo umount /srv/data # démonte le RAID
sudo umount /srv/data-new # démonte le mount intermédiaire
sudo mount --bind /home/data /srv/data
Étape 7 — Mettre à jour fstab
# Remplacer l'entrée RAID par le bind mount
# Avant : UUID=3add8360-... /srv/data ext4 defaults,noatime,nodiratime 0 0
# Après :
echo '/home/data /srv/data none bind 0 0' | sudo tee -a /etc/fstab
# (commenter l'ancienne ligne UUID=3add8360)
Étape 8 — Redémarrer les services
sudo systemctl start postgresql-16 qdrant litellm hermes-agent hermes-dashboard alertmanager-webhook
État post-migration
| Composant | Avant | Après |
|---|---|---|
/srv/data |
RAID5 ext4 /dev/md0 (dégradé) |
NVMe XFS bind-mount /home/data |
| Espace disponible | 2.6 TB RAID (dégradé, risqué) | 801 GB NVMe (sûr) |
| RAID5 md0 | [4/3] [UUU_] actif |
[4/3] [U_UU] — sdb (F), plus monté |
| Tous les services | Stoppés | ✅ Actifs |
Actions restantes
- Retirer sdb physiquement (ou le supprimer du RAID) :
sudo mdadm --manage /dev/md0 --remove /dev/sdb2 - Décider du sort du RAID : les données sont sur NVMe. Le RAID peut être démonté définitivement ou conservé pour une future migration vers un NAS.
- Vérifier câble SATA de sdb (ATA bus errors = câble ou port, pas plateau).
2026-05-27 — RAID5 : défaillance sdd — corruptions multiples en cascade
Symptômes
Au démarrage du cluster (funk-start --k8s) :
- Qdrant en
CrashLoopBackOff:RocksDB open error: Corruption: no log_file_number ... MANIFEST-000033 may be corrupted - Grafana en
CrashLoopBackOff:pq: relation mapping file "base/24737/pg_filenode.map" contains invalid data - LiteLLM DB :
ERROR: invalid page in block 0 of relation base/16385/2838 - Prometheus en
CrashLoopBackOff:corrupted block 01KSGFC9WP7STGTXWDNECY2FS0: read symbols: invalid checksum - Docker registry retournant 500 sur toutes les requêtes :
filesystem: stat ... data: input/output error sacrifice-webenImagePullBackOff(registry inaccessible)
Pattern observé : corruptions récurrentes tous les 3-4 démarrages depuis ~7 semaines.
Cause racine
Disque sdd (WDC WD10EALX-009BA0, S/N WD-WCATR9001223) en défaillance progressive :
Current_Pending_Sector : 44 ← secteurs illisibles, non réalloués
Offline_Uncorrectable : 44 ← secteurs confirmés irrécupérables
Power_On_Hours : 52816 ← 6 ans d'utilisation (MTBF constructeur = 5 ans)
Historique des self-tests SMART (tous en échec) :
#1 Short LBA 1555355176 read failure (nouveau secteur mort)
#2 Short LBA 1413976229 read failure
#4 Extended LBA 1413976229 read failure (premier secteur mort, découvert il y a ~49 jours)
Mécanisme de corruption silencieuse :
Le RAID dit [4/4] [UUUU] et State: clean car sdd ne retourne pas d'erreur au moment de l'écriture — son write-back cache (activé) acquitte l'écriture avant de la commettre sur le plateau. La donnée qui tombait sur les secteurs défaillants était silencieusement perdue, la parité RAID5 devenait incohérente avec les données réelles. Le RAID n'en savait rien.
À chaque session d'utilisation (~10-12h), les services NFS/k8s écrivaient sur /srv/data. Une fraction des writes tombait sur les zones corrompues de sdd et accumulait de la corruption dans les métadonnées ext4 (directory blocks, btree). Au bout de 3-4 sessions, assez de structures critiques étaient corrompues pour faire crasher les services.
Problèmes secondaires identifiés :
sda:UDMA_CRC_Error_Count = 6→ câble SATA défaillant ou port SATA instablesdb:Start_Stop_Count = 17780pour 8847h → spin-down APM trop agressif (~49 cycles/jour)- Tous les disques : write-back cache activé sans onduleur → risque de corruption sur coupure de courant
Résolution — corruptions de données
Qdrant : collection funk-docs supprimée et réingérée.
sudo systemctl stop qdrant
sudo rm -rf /srv/data/qdrant/storage/collections/funk-docs
sudo systemctl start qdrant
rag-ingest # → 308 chunks réindexés
PostgreSQL — bases grafana et litellm :
sudo systemctl stop litellm
sudo -u postgres psql -c "DROP DATABASE litellm;"
sudo -u postgres psql -c "CREATE DATABASE litellm OWNER litellm;"
sudo -u postgres psql -c "DROP DATABASE grafana;"
sudo -u postgres psql -c "CREATE DATABASE grafana OWNER grafana;"
sudo systemctl start litellm
kubectl rollout restart deployment/kube-prometheus-stack-grafana -n monitoring
# → Grafana rejoue 709 migrations, recrée le schéma
Prometheus TSDB : deux blocs corrompus supprimés du NFS.
PROM_NFS="/srv/data/nfs/k8s/monitoring-prometheus-...-pvc-aa1a187f-.../prometheus-db"
sudo rm -rf "$PROM_NFS/01KSGF0DXNXJ4HC5CEZX9THT5B"
sudo rm -rf "$PROM_NFS/01KSGFC9WP7STGTXWDNECY2FS0"
kubectl delete pod prometheus-kube-prometheus-stack-prometheus-0 -n monitoring
Docker registry : PVC corrompu recréé (NFS provisioner génère un répertoire propre).
kubectl scale deployment registry -n infra --replicas=0
kubectl delete pvc registry-pvc -n infra
kubectl delete pv pvc-c5e1e86e-da56-4b31-bf52-07d5d619c9ea
# Recréer la PVC — le NFS provisioner provisionne un nouveau répertoire
kubectl apply -f - <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: registry-pvc
namespace: infra
spec:
accessModes: [ReadWriteOnce]
storageClassName: nfs
resources:
requests:
storage: 10Gi
EOF
kubectl scale deployment registry -n infra --replicas=1
sacrifice-web : image rebuildée et pushée via tunnel SSH.
# Tunnel SSH pour atteindre le registry depuis le poste perso
ssh -f -N -L 31500:192.168.10.11:31500 storage-01
cd /path/to/Sacrifice/sacrifice-web
podman build -t localhost:31500/sacrifice-web:latest .
podman push localhost:31500/sacrifice-web:latest --tls-verify=false
kubectl rollout restart deployment/sacrifice-web -n sacrifice
Résolution — cause racine (sdd retiré du RAID)
# Marquer sdd comme défaillant et le retirer
sudo mdadm --manage /dev/md0 --fail /dev/sdd1
sudo mdadm --manage /dev/md0 --remove /dev/sdd1
# → State: clean, degraded [4/3] [UUU_]
# Persister la configuration
sudo mdadm --detail --scan | sudo tee /etc/mdadm.conf
sudo dracut --force # régénérer l'initramfs
État post-incident
| Service | État |
|---|---|
| RAID5 md0 | [4/3] [UUU_] — dégradé, zéro redondance jusqu'au remplacement de sdd |
| Qdrant | ✅ 308 chunks réindexés |
| PostgreSQL grafana | ✅ Schéma recréé, dashboards reprovisionnés |
| PostgreSQL litellm | ✅ Schéma recréé, config modèles intacte (perte de l'historique spend) |
| Prometheus | ✅ Running — perte des ~2 derniers blocs TSDB (quelques heures de métriques) |
| Docker registry | ✅ PVC propre, image sacrifice-web repushée |
| sacrifice-web | ✅ Running, HTTP 200 |
Actions requises (non résolues)
- Remplacer sdd (1TB+, ex. WD Red ou Seagate IronWolf) et relancer la reconstruction :
sudo mdadm --manage /dev/md0 --add /dev/sdNEW1 # Reconstruction automatique ~4h, visible dans /proc/mdstat - Câble SATA sda : remplacer le câble (UDMA_CRC_Error_Count=6)
- APM sdb : désactiver le spin-down (
sudo hdparm -B 254 /dev/sdb) et persister via udev - Onduleur (UPS) : indispensable — write-back cache activé sur tous les disques
- e2fsck : planifier une fenêtre de maintenance pour vérifier les métadonnées ext4 restantes :
sudo systemctl stop nfs-server qdrant postgresql-16 litellm hermes-agent sudo umount /srv/data sudo e2fsck -f /dev/md0 sudo mount /srv/data && sudo systemctl start nfs-server qdrant postgresql-16 litellm hermes-agent
2026-05-14 — Grafana : OOMKilled (limite mémoire trop serrée)
Symptôme
Erreurs "No available server" intermittentes sur grafana.lab.local. Pod en CrashLoop.
kubectl describe pod -n monitoring -l app.kubernetes.io/name=grafana
# → Last State: Terminated, Reason: OOMKilled, Exit Code: 137
# → RESTARTS: 5
Cause
Limite mémoire à 256Mi dans k8s/infra/monitoring/values.yaml. Grafana 13 consomme plus que ses prédécesseurs — OOM systématique. Traefik perd le backend pendant chaque redémarrage.
Résolution
Augmenter les limites dans k8s/infra/monitoring/values.yaml :
grafana:
resources:
requests:
memory: 256Mi
limits:
memory: 1Gi # était 256Mi
git push → ArgoCD applique → pod redémarre proprement.
2026-05-13 — AlertManager : ReconciliationFailed — receiver "null" non défini
Symptôme
AlertManager en erreur dans ArgoCD. Logs :
level=error msg="loading configuration" err="undefined receiver \"null\" used in route"
Cause
kube-prometheus-stack génère en interne une sous-route référençant le receiver "null" pour le heartbeat Watchdog. Une config AlertManager custom qui ne définissait pas ce receiver cassait la validation.
Résolution
Ajouter explicitement le receiver et la route dans values.yaml :
alertmanager:
config:
route:
routes:
- matchers:
- alertname = "Watchdog"
receiver: "null"
repeat_interval: 24h
receivers:
- name: "null" # obligatoire même si vide
2026-05-13 — llama-server : /metrics retourne 501 Not Implemented
Symptôme
Prometheus ne scrappe rien sur les jobs llama-server-*. Targets en erreur avec 501 Not Implemented.
Cause
Le flag --metrics doit être passé explicitement à llama-server pour activer l'endpoint /metrics. Sans lui, la route existe mais répond 501.
Résolution
Ajouter --metrics dans le service systemd (template Ansible llama-server.service.j2) :
ExecStart=/opt/llama.cpp/build/bin/llama-server \
...
--metrics \
--log-disable
Fix live sur les 3 instances + daemon-reload + restart :
# Sur gpu-01
for svc in llama-server llama-server-system llama-server-monitor; do
sudo sed -i 's/--log-disable/--metrics \\\n --log-disable/' /etc/systemd/system/$svc.service
done
sudo systemctl daemon-reload
sudo systemctl restart llama-server llama-server-system llama-server-monitor
2026-05-13 — nftables storage-01 : règle monitoring bloquée par le drop
Symptôme
Prometheus ne pouvait pas scrapper storage-01:9100 (node_exporter) ni recevoir les webhooks AlertManager sur :9093. Targets en connection refused dans Prometheus.
Cause
nft add rule ajoute la règle en fin de chaîne, après la règle terminale log prefix "nft-drop:" drop. Les paquets étaient droppés avant d'atteindre la nouvelle règle.
# Vérifier l'ordre
sudo nft -n -a list chain inet filter input
# → handle 22 : log drop (terminal)
# → handle 27 : tcp dport { 9093, 9100 } accept (ajouté APRÈS → jamais atteint)
Résolution
Supprimer la règle mal placée et la réinsérer avant le drop avec insert rule position :
sudo nft delete rule inet filter input handle 27
sudo nft insert rule inet filter input position 22 \
tcp dport { 9093, 9100 } ip saddr 192.168.10.0/24 accept
Fix permanent dans le template Ansible nftables.conf.j2 : placer la règle avant la règle drop.
2026-05-13 — storage-01 : corruption EXT4 après arrêt brutal + reinstall hermes-agent
Symptômes
hermes-agenten boucle de crash :SyntaxError: source code string cannot contain null bytes- Erreurs EXT4 au démarrage des services :
Directory block failed checksum,deleted inode referenced dmesg:EXT4-fs (md0): error count since last fsck: 655sdb: erreurs ATA bus (SError: { RecovComm HostInt PHYRdyChg PHYInt }) et I/O error sector 13405184
Cause racine
Arrêt brutal du cluster la veille (funk-stop --k8s) → journal ext4 non rejoué correctement → 655 erreurs de corruption sur /dev/md0. sdb avait des erreurs transitoires mais SMART PASSED (pas de défaillance matérielle).
Résolution étape par étape
1. Vérifier SMART sdb
smartctl -H /dev/sdb
# SMART overall-health self-assessment test result: PASSED → purement logiciel
2. Arrêter les services et démonter
sudo systemctl stop hermes-agent litellm qdrant postgresql-16 nfs-server
sudo umount /srv/data
3. Réparer le filesystem
sudo e2fsck -f -y /dev/md0
# 655 erreurs corrigées : HTREE invalides, blocs multiplement revendiqués,
# checksums de répertoires incorrects, compteurs d'inodes wronge
4. Remonter et redémarrer les services
sudo mount /srv/data
sudo systemctl start postgresql-16 qdrant litellm hermes-agent nfs-server
5. Hermes-agent toujours en erreur après fsck
Le e2fsck a zéroïsé des blocs appartenant à plusieurs inodes simultanément → des fichiers du venv Python et du code source hermes_cli/ ont été corrompus (null bytes).
Tentatives de réparation du venv échouées (pip install -e . crash, uv.lock corrompu).
6. Réinstallation complète de hermes-agent
# Supprimer le code source corrompu (les profils dans /srv/data/hermes/profiles/ ne sont PAS touchés)
sudo rm -rf /srv/data/hermes/hermes-agent
sudo rm /opt/hermes/.local/bin/hermes
# Réinstaller depuis le script officiel (depuis un répertoire valide)
cd /srv/data/hermes
sudo -u hermes bash -c 'cd /srv/data/hermes && HOME=/opt/hermes HERMES_HOME=/srv/data/hermes curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash'
Lors du
sudo rm -rf, ne pas rester dans le répertoire supprimé — le shell perd le CWD et le script d'installation échoue avecgetcwd: cannot access parent directories.
Résultat : hermes-agent réinstallé dans /opt/hermes/.hermes/hermes-agent/ (SSD OS, plus de RAID5). Tous les profils (funk-ai, brain, system, monitor) intacts dans /srv/data/hermes/profiles/.
Changement d'architecture post-incident
| Avant | Après |
|---|---|
Code hermes-agent : /srv/data/hermes/hermes-agent/ (RAID5) |
Code hermes-agent : /opt/hermes/.hermes/hermes-agent/ (SSD OS) |
Venv : /srv/data/hermes/hermes-agent/venv/ |
Venv : /opt/hermes/.hermes/hermes-agent/venv/ |
Profils/données : /srv/data/hermes/ |
Profils/données : /srv/data/hermes/ (inchangé) |
Le code étant désormais sur le SSD, une corruption future du RAID5 n'impactera plus l'installation — seulement les profils et données.
Surveillance post-incident
# Santé RAID et disques
cat /proc/mdstat
smartctl -H /dev/sdb # à surveiller
# Vérifier que e2fsck n'a pas schedulé un check au prochain boot
sudo tune2fs -l /dev/md0 | grep -E "check|count|error"
2026-05-11 — storage-01 : boucle fsck après coupure de courant
Symptômes
- Boucle infinie de
fsckau démarrage sur/dev/md0 You are in emergency mode. Give root password for maintenance— mot de passe impossible à saisir (messages défilants)- Après résolution :
hermes-agenten boucle d'erreurs (cause :/srv/datanon monté)
Architecture concernée
| Composant | Détail |
|---|---|
| Disque système | NVMe nvme0n1 — LVM (almalinux-root XFS 70G, swap 14.7G, home 845G) |
| RAID données | RAID5 software md0 — 4 disques SATA 931.5G (sda1, sdb2, sdc1, sdd1) |
| Point de montage | /srv/data (ext4) |
Cause racine
La coupure de courant a corrompu le système de fichiers XFS sur almalinux-root. Au redémarrage, systemd tentait de vérifier /srv/data avant que le RAID md0 soit assemblé → boucle bloquante.
Résolution étape par étape
1. Shell de secours via GRUB
Au démarrage, appuyer sur Shift pour afficher le menu GRUB. Appuyer sur e pour éditer la ligne linux :
- Remplacer
roparrw - Ajouter
init=/bin/bashen fin de ligne
Démarrer avec Ctrl+X.
2. Activer le LVM
lvm vgchange -ay
# 3 logical volume(s) in volume group "almalinux" now active
3. Réparer le XFS (pas fsck)
XFS nécessite xfs_repair, pas fsck :
umount /dev/mapper/almalinux-root
xfs_repair /dev/mapper/almalinux-root
# Phases 1 à 7 — succès
4. Désactiver temporairement /srv/data dans fstab
vi /etc/fstab
# Commenter la ligne /srv/data pour permettre le boot sans RAID
5. Reboot → système normal, RAID intact [4/4] [UUUU].
Fix permanent — fstab
Modifier la ligne /srv/data dans /etc/fstab :
# Avant
UUID=3add8360-fa01-47eb-0aa1-0e84bacbbc15 /srv/data ext4 defaults,noatime,nodiratime 0 2
# Après
UUID=3add8360-fa01-47eb-0aa1-0e84bacbbc15 /srv/data ext4 defaults,noatime,nodiratime,_netdev 0 0
| Changement | Effet |
|---|---|
0 2 → 0 0 |
Désactive la vérification fsck automatique au boot |
_netdev |
Force systemd à attendre que tous les périphériques (RAID) soient prêts avant de monter |
Surveillance post-incident
# État du RAID
cat /proc/mdstat
# Santé des disques (sdb a généré des erreurs I/O — à surveiller)
smartctl -a /dev/sda
smartctl -a /dev/sdb
smartctl -a /dev/sdc
smartctl -a /dev/sdd
# Monter /srv/data manuellement si besoin
mount /srv/data
# Logs de boot
journalctl -xb | grep -i "error\|fail"
Points d'attention
sdba généré des erreurs I/O pendant l'incident — il est revenu opérationnel mais doit être surveillé régulièrement avecsmartctl.- Envisager un onduleur (UPS) pour prévenir ce type d'incident sur coupure de courant.
- Le fix fstab (
_netdev,0 0) est permanent — ne pas revenir aux valeurs précédentes.
État vérifié — 2026-06-05
Tous les services critiques (LiteLLM, Qdrant, PostgreSQL, llama-server) sont actifs et sans incident. Aucune divergence avec la documentation. Aucun pod crashé détecté dans le cluster Kubernetes. Export NFS sur /srv/data fonctionnel. Aucune configuration inattendue observée.