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.workloadRefvers 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
componentetcause: le diagnostic voyage avec l'alerterunbook_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
--previousaffiche 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 --explainajoute 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 auxsecrets - 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
selfHealcorrige la dérive, Argo Rollouts annule une release défectueuse, Git garde la trace- Les labels
componentetcauseet l'annotationrunbook_urltransforment 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