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

  1. Chaîne de défaillance
  2. Longhorn — staleReplicaTimeout trop court
  3. RabbitMQ — arrêt brutal et réplica unique
  4. PostgreSQL (vm-service) — absence totale de probes et de ressources
  5. VM-Service — init container sans timeout
  6. Tableau récapitulatif
  7. 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 sa restartPolicy, 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

Retour en haut