Paillasse/docs/runbooks/self-healing.md

94 lines
4.3 KiB
Markdown
Raw Normal View History

# Runbook — Self-healing des VM (bloc A, semaine 6)
## Principe
Chaque minute, le scheduler Laravel (compte `www-data`, déjà en place depuis
l'étape 10) exécute `php artisan labs:surveiller-vm`, implémenté par
`App\Services\Labs\SurveillanceVm`.
Pour chaque VM (stock chaud et sessions d'élèves actives), il demande au
service **correcteur** (sur l'hyperviseur) une **sonde de santé** :
état libvirt du domaine (`virsh domstate`) et `guest-ping` de l'agent invité
QEMU. Rien n'est exécuté dans la VM — c'est une requête distincte de la
correction d'un lab (`{instance, sante: true}` plutôt que
`{instance, lab, verification}`), refusée si elle porte les deux à la fois.
- **VM du stock muette 2 sondes de suite** (`stock:echecs` compteur Redis
`sante:muette:{instance}`) : sa stack est retirée du stock chaud et
supprimée. `labs:remplir-stock` la remplace à la prochaine minute. Aucun
élève ne reçoit jamais une VM défaillante.
- **VM d'élève muette 3 sondes de suite** : redémarrage forcé par Nova
(`reboot --hard`, ou démarrage si elle est éteinte), au plus **2 fois par
heure**. Au-delà, la VM est marquée **irréparable** : le prof la voit dans
la salle d'attente (« ne répond plus malgré deux redémarrages automatiques.
L'équipe technique est prévenue. »), et doit relancer le lab de cet élève
à la main.
## Garde-fous
- **Délai de grâce** de 3 minutes après la création d'une VM : elle démarre
encore, on ne la sonde pas (correspond au p95 de démarrage mesuré à
l'étape 13, 75 s, avec large marge).
- **Correcteur injoignable** : aucune action (on ne devine jamais l'état
d'une VM qu'on n'a pas pu sonder).
- **Panne générale** : si *toutes* les VM sondées sont muettes à la fois
(seuil : au moins 3 VM), c'est probablement l'hyperviseur qui est en
cause, pas les VM une par une — aucune relance n'est tentée (une rafale de
redémarrages ne répare pas un hôte en panne, et l'alourdirait). Couvert
par les alertes existantes (`DisqueSature`, `MemoireSaturee`,
`ServiceOpenStackArrete`), pas par ce mécanisme.
## Métriques (Prometheus, collecteur textfile)
Fichier `/var/lib/node_exporter/textfile/sante_vm.prom` (écrit par
`www-data`, lu par `node_exporter`) :
| Métrique | Sens |
|---|---|
| `syllabus_vm_surveillance_timestamp` | dernier passage |
| `syllabus_vm_sondees` / `syllabus_vm_muettes` | VM sondées / muettes à ce passage |
| `syllabus_vm_irreparables` | VM d'élèves toujours muettes après les relances |
| `syllabus_vm_panne_generale` | 1 si aucune action n'a été tentée (panne générale ou correcteur injoignable) |
| `syllabus_vm_reparations_total{type=...}` | compteur cumulé : `relance_session`, `retrait_stock`, `abandon` |
Alertes à ajouter avant le pilote (pas encore faites, voir `docs/dette.md`) :
`syllabus_vm_panne_generale == 1` pendant plus de 5 min, et
`syllabus_vm_irreparables > 0`.
## Lancer une vérification manuellement
```
ssh paillasse
cd /var/www/portail
sudo -u www-data HOME=/tmp php8.3 artisan labs:surveiller-vm
cat /var/lib/node_exporter/textfile/sante_vm.prom
```
## Provoquer une panne pour tester (ne jamais sur une vraie classe)
```
# 1. Prendre une VM réelle (une session de test, ou du stock) et la geler
# comme un noyau planté (le CPU virtuel s'arrête, l'agent ne répond plus) :
ssh paillasse "sudo docker exec nova_libvirt virsh suspend <uuid-instance>"
# 2. Observer (plusieurs minutes, ou plusieurs appels manuels) :
ssh paillasse "cd /var/www/portail && sudo -u www-data HOME=/tmp php8.3 artisan labs:surveiller-vm"
# 3. Vérifier le retour à l'état normal :
ssh paillasse "sudo docker exec nova_libvirt virsh domstate <uuid-instance>" # running
```
Compte-rendu du premier test réel : `docs/postmortems/2026-10-12-vm-figee-self-healing.md`.
## Limites connues
- Ne couvre pas la panne du serveur lui-même (un seul serveur, pas de sonde
extérieure — dette connue, `docs/dette.md`).
- Ne couvre pas un service Kolla arrêté ni un conteneur maison arrêté : ce
sont déjà `Restart=always`/`unless-stopped` côté systemd et Docker (voir
relevé du 2026-10-12 dans le journal), pas ce mécanisme.
- Une stack du stock chaud orpheline (ni muette, ni référencée) est déjà
traitée par `StockChaud::menage()` (étape 11) ; `SurveillanceVm` s'occupe
seulement des VM qui *répondent mal*, pas de celles qui n'existent plus
en Redis.