Analyse de stabilité — Crash brutal Proxmox
| Version : 1.0 | Date : Juin 2026 | Statut : En cours de correction |
Après une extinction accidentelle de Proxmox, trois composants entrent en défaillance en cascade : Longhorn, RabbitMQ et VM-Service. Ce document en décrit les causes racines et les corrections à apporter.
Sommaire
- Chaîne de défaillance
- Longhorn — staleReplicaTimeout trop court
- RabbitMQ — arrêt brutal et réplica unique
- PostgreSQL (vm-service) — absence totale de probes et de ressources
- VM-Service — init container sans timeout
- Tableau récapitulatif
- Plan de corrections par priorité
1. Chaîne de défaillance
Lors d’un crash Proxmox, tous les nœuds tombent simultanément. La séquence suivante se déclenche alors automatiquement :
Proxmox crash brutal
│
▼
Tous les nœuds K8s inaccessibles d'un coup
│
▼ (après ~30 secondes)
Longhorn marque les réplicas comme "stale"
et tente une reconstruction ← CAUSE RACINE
│
▼
Les PVC de RabbitMQ et PostgreSQL restent en état "Degraded"
ou "Pending" le temps de la reconstruction
│
├──▶ RabbitMQ ne peut pas monter son volume → pod bloqué
│
└──▶ PostgreSQL ne peut pas monter son volume → pod bloqué
│
▼
L'init container de VM-Service boucle indéfiniment
en attendant le port 5432 (aucun timeout défini)
│
▼
VM-Service reste en CrashLoopBackOff
│
▼
Intervention manuelle obligatoire (30+ minutes)
2. Longhorn — staleReplicaTimeout trop court
Fichier concerné
apps_implementation/longhorn/policies/longhorn-storageclass.yaml
Configuration actuelle
parameters:
numberOfReplicas: "2"
staleReplicaTimeout: "30" # ← 30 secondes
fsType: "ext4"
Problème
Le paramètre staleReplicaTimeout définit le délai au-delà duquel Longhorn considère qu’un réplica est “trop vieux” et doit être reconstruit.
30 secondes est largement insuffisant dans un scénario de crash Proxmox global :
- Le temps de démarrage du BIOS + Proxmox + les VMs Kubernetes dépasse systématiquement 1 à 5 minutes.
- Quand tous les nœuds tombent simultanément, Longhorn ne reçoit plus de heartbeat d’aucun réplica.
- Au bout de 30 secondes, les deux réplicas sont marqués stale, et Longhorn cherche à reconstruire les volumes sur des nœuds qui ne sont pas encore disponibles.
- Les volumes restent en état Degraded ou les PVC en Pending le temps de la reconstruction, ce qui empêche RabbitMQ et PostgreSQL de démarrer.
Correction
Passer staleReplicaTimeout à 600 (10 minutes), ce qui laisse le temps au cluster de redémarrer proprement avant que Longhorn n’intervienne.
parameters:
numberOfReplicas: "2"
staleReplicaTimeout: "600" # 10 minutes
fsType: "ext4"
Note : Ce paramètre ne s’applique qu’aux nouveaux volumes créés après la modification. Pour les volumes existants, il faut les éditer manuellement dans l’UI Longhorn ou via
kubectl edit volume -n longhorn-system.
3. RabbitMQ — arrêt brutal et réplica unique
Fichier concerné
apps_implementation/rabbitmq/rabbitmq-cluster.yaml
Configuration actuelle
spec:
replicas: 1 # ← un seul réplica
persistence:
storageClassName: longhorn
storage: 5Gi
rabbitmq:
additionalConfig: |
default_user = guest
default_pass = guest # ← credentials par défaut
override:
statefulSet:
spec:
template:
spec:
containers:
- name: rabbitmq
readinessProbe:
exec:
command: [rabbitmq-diagnostics, -q, ping]
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 15
failureThreshold: 20
startupProbe:
# identique à readinessProbe
Problèmes
3.1 Absence de terminationGracePeriodSeconds
RabbitMQ persiste ses données dans une base Erlang Mnesia. Si le pod est tué sans délai de grâce suffisant, les fichiers Mnesia peuvent se retrouver dans un état incohérent au redémarrage, ce qui provoque un échec de démarrage du nœud.
Kubernetes applique par défaut un délai de 30 secondes avant d’envoyer SIGKILL, ce qui est insuffisant pour RabbitMQ. Un délai de 120 secondes est recommandé.
Ce paramètre doit être ajouté via override.statefulSet.spec.template.spec.terminationGracePeriodSeconds.
3.2 Réplica unique
Avec replicas: 1, RabbitMQ n’a aucune redondance. Si le pod ou son volume est indisponible (ce qui arrive systématiquement lors d’un crash Longhorn), le broker est totalement inaccessible. L’opérateur RabbitMQ supporte nativement les clusters à 3 nœuds avec quorum queues.
3.3 Credentials par défaut
guest/guest est une configuration de développement. Ces credentials sont exposés en clair dans le ConfigMap et doivent être remplacés par des secrets chiffrés via Sealed Secrets.
Corrections
Court terme — ajouter terminationGracePeriodSeconds :
override:
statefulSet:
spec:
template:
spec:
terminationGracePeriodSeconds: 120 # ← à ajouter
containers:
- name: rabbitmq
# ... probes existantes ...
Moyen terme — passer à 3 réplicas :
spec:
replicas: 3
L’opérateur RabbitMQ configure automatiquement le clustering et les quorum queues, ce qui garantit la disponibilité même si un nœud est indisponible.
4. PostgreSQL (vm-service) — absence totale de probes et de ressources
Fichier concerné
apps_implementation/vm-service/vm-svc-postgres-statefulset.yaml
Configuration actuelle
spec:
replicas: 1
template:
spec:
containers:
- name: postgres
image: harbor.int.edu-kit.fr:80/postgres/postgres:17.6
env:
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
# ← aucune readinessProbe
# ← aucune livenessProbe
# ← aucun resources (requests/limits)
# ← aucun terminationGracePeriodSeconds
Problèmes
4.1 Aucune readinessProbe
Sans readinessProbe, Kubernetes considère le pod PostgreSQL comme “prêt” dès que le conteneur démarre, sans attendre que le moteur accepte réellement des connexions. Or, après un crash, PostgreSQL exécute une récupération WAL qui peut durer plusieurs minutes. Durant ce temps, l’init container de VM-Service reçoit une réponse sur le port 5432 (le port est ouvert) mais les connexions sont refusées, causant des erreurs en cascade.
4.2 Aucune livenessProbe
Si PostgreSQL se retrouve dans un état bloqué (verrou mort, processus zombie), Kubernetes ne peut pas le détecter et ne redémarre pas le pod.
4.3 Aucune limite de ressources
Sans resources.requests et resources.limits, le pod peut être évincé par le scheduler sous pression mémoire, ou consommer toutes les ressources du nœud et déstabiliser les autres pods. C’est particulièrement critique pour une base de données, où une éviction en cours d’écriture peut corrompre les données.
4.4 terminationGracePeriodSeconds insuffisant
Par défaut à 30 secondes, ce délai ne laisse pas le temps à PostgreSQL de terminer les transactions en cours, flusher les buffers vers le disque et écrire un checkpoint propre. Le WAL peut être tronqué, et PostgreSQL doit rejouer des transactions au prochain démarrage, allongeant le temps de récupération.
Corrections
spec:
template:
spec:
terminationGracePeriodSeconds: 120 # ← à ajouter
containers:
- name: postgres
resources: # ← à ajouter
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
readinessProbe: # ← à ajouter
exec:
command:
- sh
- -c
- pg_isready -U vm-svc-user -d vm-svc-db
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
livenessProbe: # ← à ajouter
exec:
command:
- sh
- -c
- pg_isready -U vm-svc-user -d vm-svc-db
initialDelaySeconds: 30
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
5. VM-Service — init container sans timeout
Fichier concerné
apps_implementation/vm-service/vm-svc-deployment.yaml
Configuration actuelle
initContainers:
- name: wait-for-postgres
image: busybox
command:
- sh
- -c
- |
until nc -z vm-svc-postgres-service 5432; do
echo "Waiting for PostgreSQL..."
sleep 2
done
echo "PostgreSQL port is open, waiting 10s for auth readiness"
sleep 10
echo "Ready!"
Problèmes
5.1 Boucle infinie sans timeout
L’init container boucle indéfiniment jusqu’à ce que le port 5432 soit accessible. Si PostgreSQL met 10 minutes à récupérer (récupération WAL + montage du volume Longhorn), le pod VM-Service reste bloqué en Init:0/1 sans jamais échouer.
Kubernetes ne peut pas redémarrer l’init container s’il ne sort pas, et il n’y a aucune alerte visible. En pratique, il faut supprimer le pod manuellement pour déclencher une nouvelle tentative.
5.2 Test du port insuffisant
nc -z vérifie uniquement que le port TCP est ouvert, pas que PostgreSQL accepte des connexions authentifiées. Il est possible que le port soit ouvert pendant la phase de récupération WAL mais que les connexions soient refusées, ce qui laisse l’application échouer après l’init container.
5.3 readinessProbe à 300s sur le conteneur principal
Les deux conteneurs (dotnet-server et worker) ont un initialDelaySeconds: 300 sur leurs probes de readiness et de liveness. Ces 5 minutes de délai initial sont excessives et ralentissent inutilement la détection d’un démarrage raté.
Corrections
Init container — ajouter un timeout et un test plus robuste :
initContainers:
- name: wait-for-postgres
image: busybox
command:
- sh
- -c
- |
echo "Waiting for PostgreSQL to be ready..."
TIMEOUT=300
ELAPSED=0
until nc -z vm-svc-postgres-service 5432; do
if [ $ELAPSED -ge $TIMEOUT ]; then
echo "Timeout waiting for PostgreSQL after ${TIMEOUT}s"
exit 1
fi
echo "PostgreSQL not ready, retrying in 5s... (${ELAPSED}s elapsed)"
sleep 5
ELAPSED=$((ELAPSED + 5))
done
echo "PostgreSQL port is open."
Avec
exit 1, l’init container échoue proprement après 5 minutes et Kubernetes redémarre le pod selon sarestartPolicy, rendant le problème visible dans les events et les logs.
Réduire le délai initial des probes :
readinessProbe:
httpGet:
path: /vm-service/health
port: 8080
initialDelaySeconds: 60 # réduit de 300 à 60
periodSeconds: 20
timeoutSeconds: 10
failureThreshold: 5
livenessProbe:
httpGet:
path: /vm-service/health
port: 8080
initialDelaySeconds: 60 # réduit de 300 à 60
periodSeconds: 20
timeoutSeconds: 10
failureThreshold: 5
6. Tableau récapitulatif
| Composant | Problème | Sévérité | Impact |
|---|---|---|---|
| Longhorn | staleReplicaTimeout: 30 trop court |
CRITIQUE | Volumes indisponibles au redémarrage |
| RabbitMQ | Pas de terminationGracePeriodSeconds |
HAUTE | Corruption Mnesia, perte de messages |
| RabbitMQ | Réplica unique | HAUTE | Indisponibilité totale si le pod crash |
| RabbitMQ | Credentials guest/guest en clair |
MOYENNE | Risque de sécurité |
| PostgreSQL | Pas de readinessProbe |
HAUTE | Connexions refusées sans signal Kubernetes |
| PostgreSQL | Pas de livenessProbe |
HAUTE | État bloqué non détecté |
| PostgreSQL | Pas de resources |
HAUTE | Éviction ou OOMkill possible |
| PostgreSQL | terminationGracePeriodSeconds à 30s |
HAUTE | Corruption WAL possible |
| VM-Service | Init container sans timeout | HAUTE | Blocage silencieux indéfini |
| VM-Service | initialDelaySeconds: 300 sur les probes |
BASSE | Récupération inutilement lente |
7. Plan de corrections par priorité
Priorité 1 — Corrections immédiates (impact maximal, risque minimal)
Ces changements ne nécessitent pas de refonte architecturale et suffisent à éviter les blocages manuels post-crash.
| # | Action | Fichier |
|---|---|---|
| 1 | Passer staleReplicaTimeout de "30" à "600" |
longhorn/policies/longhorn-storageclass.yaml |
| 2 | Ajouter terminationGracePeriodSeconds: 120 à RabbitMQ |
rabbitmq/rabbitmq-cluster.yaml |
| 3 | Ajouter terminationGracePeriodSeconds: 120 à PostgreSQL vm-service |
vm-service/vm-svc-postgres-statefulset.yaml |
| 4 | Ajouter readinessProbe et livenessProbe à PostgreSQL vm-service |
vm-service/vm-svc-postgres-statefulset.yaml |
| 5 | Ajouter resources (requests + limits) à PostgreSQL vm-service |
vm-service/vm-svc-postgres-statefulset.yaml |
| 6 | Ajouter un timeout de 300s à l’init container wait-for-postgres |
vm-service/vm-svc-deployment.yaml |
| 7 | Réduire initialDelaySeconds des probes VM-Service de 300 à 60 |
vm-service/vm-svc-deployment.yaml |
Note Longhorn : après avoir modifié la StorageClass, éditer également les volumes existants dans l’UI Longhorn (Volume → Edit → Stale Replica Timeout).
Priorité 2 — Améliorations à moyen terme
| # | Action | Bénéfice |
|---|---|---|
| 1 | Passer RabbitMQ à 3 réplicas | Haute disponibilité, zéro perte de messages |
| 2 | Remplacer les credentials guest/guest par un SealedSecret |
Sécurité |
| 3 | Configurer des snapshots récurrents Longhorn | Récupération en cas de corruption |
| 4 | Ajouter une destination de backup Longhorn (NFS/S3) | Récupération hors-cluster |
| 5 | Vérifier le même pattern sur les PostgreSQL de keycloak et org-service | Cohérence |