Aller au contenu

Auto-remédiation & Analyse des Causes Racines

Objectifs pédagogiques

  • Distinguer les couches de self-healing d'une plateforme Kubernetes et leurs limites
  • Configurer la réconciliation GitOps et un rollback automatique piloté par les métriques
  • Écrire des alertes qui identifient le composant défaillant et la cause principale
  • Concevoir une remédiation déclenchée par alerte sans créer de nouveau risque
  • Mesurer le temps de rétablissement et relier chaque alerte à son runbook

Self-healing : les couches


Pourquoi automatiser la remédiation ?

Le temps de rétablissement se décompose en quatre étapes

 Panne ──► Détection ──► Diagnostic ──► Remédiation ──► Vérification
           (alerte)      (quelle cause ?) (quelle action ?) (retour au SLO)
  • À 3h du matin, chaque étape humaine coûte des minutes
  • Le diagnostic est souvent l'étape la plus longue : « le pod redémarre » ne dit pas pourquoi
  • Automatiser la détection et le diagnostic accélère tout le reste
  • Automatiser l'action n'a de sens que si elle est sûre, bornée et tracée

Les cinq couches

 5. Humain           runbook, décision, postmortem
 4. Remédiation      webhook Alertmanager → action bornée
    par alerte
 3. Livraison        Argo Rollouts : analyse des métriques,
    progressive      abort et retour à la version stable
 2. Réconciliation   ArgoCD selfHeal / prune : la dérive
    GitOps           de configuration est annulée
 1. Contrôleurs      ReplicaSet, probes, HPA, PDB :
    Kubernetes       le cluster répare seul l'essentiel

Chaque couche traite ce que la précédente ne sait pas traiter


Couche 1 : ce que Kubernetes répare seul

Situation Mécanisme Résultat
Conteneur qui plante restartPolicy Redémarrage par le kubelet
Pod supprimé ou évincé ReplicaSet Pod recréé
Application bloquée livenessProbe Conteneur redémarré
Application pas prête readinessProbe Retirée du trafic
Hausse de charge HPA Réplicas ajoutés
Maintenance d'un nœud PDB Interruptions volontaires limitées

Limite : redémarrer ne corrige pas la cause : un OOMKill ou une configuration invalide produit un CrashLoopBackOff, pas une guérison


Couche 2 : réconciliation GitOps

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: securepay-api
  namespace: argocd
spec:
  # source et destination omises
  syncPolicy:
    automated:
      prune: true        # supprime ce qui a disparu de Git
      selfHeal: true     # annule toute modification manuelle
  ignoreDifferences:
    - group: argoproj.io
      kind: Rollout
      jsonPointers:
        - /spec/replicas # géré par le HPA, pas par Git
  • Une NetworkPolicy supprimée à la main réapparaît en quelques secondes
  • Piège classique : sans ignoreDifferences, ArgoCD et le HPA se disputent le nombre de réplicas

Couche 3 : livraison progressive

 v1 stable ████████████████████ 100 %
 v2 canary                        0 %
      │ setWeight 25
 v1 stable ███████████████        75 %
 v2 canary █████                  25 %   ◄── AnalysisRun interroge Prometheus
      │
      ├── analyse réussie  ──► 50 % ──► 100 % : promotion
      └── analyse en échec ──► Abort : canary retiré,
                               100 % sur v1, Rollout Degraded
  • Seule une fraction des utilisateurs voit la version défectueuse
  • Le retour arrière n'attend aucune intervention humaine

Argo Rollouts : le Rollout canary

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: securepay-api
  namespace: securepay
spec:
  replicas: 4
  selector:
    matchLabels:
      app.kubernetes.io/name: securepay-api
  template: {}               # le pod template du Deployment d'origine
  strategy:
    canary:
      steps:
        - setWeight: 25
        - analysis:
            templates:
              - templateName: success-rate
        - setWeight: 50
        - pause: { duration: 2m }
        - setWeight: 100
  • Sans contrôleur de trafic, le poids est approché par le nombre de réplicas
  • Migration sans réécrire le pod template : spec.workloadRef vers le Deployment existant

Argo Rollouts : l'AnalysisTemplate

apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: success-rate
  namespace: securepay
spec:
  metrics:
    - name: success-rate
      interval: 30s
      count: 6
      failureLimit: 1
      successCondition: len(result) > 0 && result[0] >= 0.995
      provider:
        prometheus:
          address: http://kube-prometheus-stack-prometheus.monitoring.svc:9090
          query: |
            sum(rate(http_requests_total{namespace="securepay", code!~"5.."}[1m]))
            /
            sum(rate(http_requests_total{namespace="securepay"}[1m]))

failureLimit fixe le nombre de mesures en échec tolérées avant l'abort


Abort, état Degraded et GitOps

  • L'abort retire le canary et remet 100 % du trafic sur la version stable
  • Le Rollout passe à l'état Degraded : la production est saine, la livraison a échoué
  • Git décrit toujours la version défectueuse : revert du commit pour retrouver un état désiré cohérent
  • Sans revert, la prochaine modification relancera la même release

Choisir le comportement sans données

  • Fail-open : isNaN(result[0]) || ... accepte une absence de métrique
  • Fail-closed : exiger un résultat présent et valide, une métrique cassée bloque la release
  • Pour un signal de sécurité de livraison, préférez le fail-closed

Couche 4 : remédiation déclenchée par alerte

 Prometheus ──► Alertmanager ──► route par label cause
                                    │
                    ┌───────────────┼──────────────────┐
                    ▼               ▼                  ▼
             Notification     Webhook             Webhook
             + diagnostic     Argo Events         remédiateur
                              → Argo Workflows    maison
  • Argo Events + Argo Workflows : un EventSource webhook déclenche un workflow de remédiation
  • Robusta : moteur open source de playbooks, enrichit les alertes (logs, événements) et peut agir
  • Remédiateur maison : un petit service HTTP, simple à auditer, avec des droits minimaux

Router les alertes par cause

route:
  receiver: equipe-securepay
  group_by: [alertname, namespace, component]
  routes:
    - matchers:
        - cause=~"OOMKilled|CrashLoopBackOff"
      receiver: diagnostic-automatique
      continue: true           # l'équipe reçoit aussi l'alerte
receivers:
  - name: equipe-securepay
    discord_configs:
      - webhook_url_file: /etc/alertmanager/secrets/discord-webhook
  - name: diagnostic-automatique
    webhook_configs:
      - url: http://remediator.securepay.svc:8080/alert
        send_resolved: false

Le label cause, posé par la règle d'alerte, devient une clé de routage


Identifier la cause principale


Symptôme vs cause

Alertes de symptôme : ce que voit l'utilisateur

  • Burn rate du SLO, taux d'erreur, latence p95
  • Elles réveillent l'astreinte : l'impact est réel

Alertes de cause : pourquoi ça se produit

  • OOMKill, CrashLoopBackOff, image introuvable, nœud perdu, HPA saturé
  • Elles qualifient l'incident et orientent le diagnostic

Objectif : quand le symptôme sonne, la cause est déjà nommée dans la même notification


Les signaux de kube-state-metrics

Métrique Label clé Cause identifiée
kube_pod_container_status_last_terminated_reason reason="OOMKilled" Dépassement mémoire
kube_pod_container_status_waiting_reason reason="CrashLoopBackOff" Redémarrages en boucle
kube_pod_container_status_waiting_reason reason="ImagePullBackOff" Image introuvable ou accès refusé
kube_pod_status_ready condition="false" Pod non prêt durablement
kube_node_status_condition condition="Ready" Nœud perdu
kube_horizontalpodautoscaler_status_current_replicas HPA au maximum

Combinées à kube_pod_container_status_restarts_total, ces métriques nomment la cause et le composant


Une alerte qui nomme la cause

- alert: SecurePayContainerOOMKilled
  expr: |
    increase(kube_pod_container_status_restarts_total{namespace="securepay"}[10m]) > 0
    and on (namespace, pod, container)
    kube_pod_container_status_last_terminated_reason{namespace="securepay", reason="OOMKilled"} == 1
  labels:
    severity: warning
    component: "{{ $labels.container }}"
    cause: OOMKilled
  annotations:
    summary: "{{ $labels.pod }} redémarré après dépassement de sa limite mémoire"
    runbook_url: https://gitlab.com/<groupe>/securepay-platform/-/blob/main/docs/runbooks/oomkilled.md
  • component et cause : le diagnostic voyage avec l'alerte
  • runbook_url : la procédure de remédiation est à un clic

Les autres causes de la matrice

- alert: SecurePayCrashLoop
  expr: kube_pod_container_status_waiting_reason{namespace="securepay", reason="CrashLoopBackOff"} == 1
  for: 2m
  labels: { severity: warning, cause: CrashLoopBackOff }

- alert: SecurePayImagePullFailure
  expr: kube_pod_container_status_waiting_reason{namespace="securepay", reason=~"ImagePullBackOff|ErrImagePull"} == 1
  for: 2m
  labels: { severity: warning, cause: ImagePullBackOff }

- alert: ClusterNodeNotReady
  expr: kube_node_status_condition{condition="Ready", status="true"} == 0
  for: 1m
  labels: { severity: critical, cause: NodeNotReady }

- alert: SecurePayHPAAtMax
  expr: |
    kube_horizontalpodautoscaler_status_current_replicas{namespace="securepay"}
    >= kube_horizontalpodautoscaler_spec_max_replicas{namespace="securepay"}
  for: 5m
  labels: { severity: warning, cause: HPAMaxReplicas }

Les événements Kubernetes

Les événements racontent ce que les métriques résument

kubectl get events -n securepay --sort-by=.lastTimestamp
kubectl get events -n securepay --field-selector type=Warning
kubectl describe pod <pod> -n securepay   # Last State, Events
kubectl logs <pod> -n securepay --previous --tail=50
  • Raisons fréquentes : BackOff, Unhealthy, FailedScheduling, Evicted, FailedMount
  • Les événements expirent vite (une heure par défaut) : exportez-les si vous voulez les conserver
  • --previous affiche les logs du conteneur avant son redémarrage

k8sgpt : le diagnostic outillé

k8sgpt filters list
k8sgpt analyze --namespace securepay
k8sgpt analyze --namespace securepay --filter Pod --output json
  • Projet CNCF en sandbox, des analyseurs parcourent pods, ReplicaSets, Services, nœuds, HPA, PDB…
  • Sans l'option --explain, aucune IA n'est sollicitée : la liste des erreurs est produite localement
  • --explain ajoute une explication par un backend d'IA, éventuellement local
  • Une sortie JSON exploitable pour l'enrichissement automatique d'une notification
  • Un opérateur permet d'exécuter les analyses en continu dans le cluster

La matrice des causes racines

Scénario Signal Alerte (cause) Remédiation automatique Runbook
Fuite mémoire OOMKilled SecurePayContainerOOMKilled Redémarrage (kubelet) oomkilled.md
Release défectueuse Taux de succès Burn rate + analyse Abort Argo Rollouts release-defectueuse.md
Nœud perdu Ready = false ClusterNodeNotReady Éviction et replanification noeud-perdu.md
Dérive manuelle OutOfSync Notification ArgoCD selfHeal derive-gitops.md
Saturation HPA au max SecurePayHPAAtMax Aucune : décision humaine capacite.md

Une ligne vide dans la colonne « remédiation » est un choix assumé, pas un oubli


Garde-fous & Mesure


Les garde-fous de l'automatisation

Un automate qui agit sur la production est une surface d'attaque et une source d'incidents

  • Moindre privilège : ServiceAccount dédié, Role limité au namespace et aux verbes nécessaires
  • Bornes : nombre maximal d'actions par heure, délai entre deux actions sur la même cible
  • Idempotence : rejouer la même alerte ne doit pas empiler les actions
  • Journalisation : chaque action produit un log, un événement et une notification
  • Humain dans la boucle : les actions irréversibles attendent une validation
  • Interrupteur : désactiver le remédiateur en une commande pendant un incident

RBAC d'un remédiateur

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: remediator
  namespace: securepay
rules:
  - apiGroups: [""]
    resources: ["pods", "pods/log", "events"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["delete"]          # seule action autorisée
  • Pas de ClusterRole, pas d'accès aux secrets
  • Vérifiez avec kubectl auth can-i --list --as=system:serviceaccount:securepay:remediator -n securepay

Remédier sans contredire GitOps

  • Une action qui modifie la spec (image, réplicas, limites) est annulée par selfHeal
  • Agir au runtime reste compatible : supprimer un pod, interrompre un Rollout
  • Toute correction durable passe par Git : l'automate peut ouvrir une Merge Request
  • La décision de revert d'une release reste tracée dans l'historique Git

Règle simple : l'automate éteint l'incendie, Git répare durablement


Mesurer le temps de rétablissement

 T0  injection de la panne        (heure notée, annotation Grafana)
 T1  alerte reçue                 détection     = T1 - T0
 T2  cause identifiée             diagnostic    = T2 - T1
 T3  remédiation exécutée         remédiation   = T3 - T2
 T4  SLI revenus dans le SLO      vérification  = T4 - T3
                                  rétablissement = T4 - T0
  • Comparez une remédiation manuelle et automatique sur le même scénario
  • En production, l'indicateur DORA correspondant est le Failed Deployment Recovery Time

Runbooks & postmortems

  • Chaque alerte pointe vers un runbook versionné : symptôme, vérifications, actions, critère de retour à la normale
  • Un runbook doit pouvoir être suivi par quelqu'un d'extérieur à l'équipe
  • Chaque incident significatif donne lieu à un postmortem blameless : chronologie, cause racine, facteurs contributifs, actions
  • Une remédiation automatique ne dispense pas du postmortem : elle masque le symptôme, pas la cause

Voir le deck 05 de ce cours : runbooks, postmortems et ADR


Synthèse


À retenir

  • Le self-healing est une superposition de couches : contrôleurs, GitOps, livraison progressive, alertes, humain
  • Kubernetes redémarre, il ne diagnostique pas : nommez la cause dans l'alerte
  • selfHeal corrige la dérive, Argo Rollouts annule une release défectueuse, Git garde la trace
  • Les labels component et cause et l'annotation runbook_url transforment une alerte en diagnostic
  • Un remédiateur a des droits minimaux, des actions bornées et un interrupteur
  • Mesurez le temps de rétablissement étape par étape pour savoir quoi automatiser ensuite

Pour aller plus loin