Aller au contenu

Chaos Engineering avec Chaos Mesh

Objectifs pédagogiques

  • Expliquer la démarche du Chaos Engineering et ses principes fondateurs
  • Formuler une hypothèse d'état stable mesurable à partir des SLO
  • Borner une expérience : rayon d'impact, conditions d'arrêt, retour arrière
  • Organiser un GameDay et analyser les points de défaillance uniques (SPOF)
  • Installer et sécuriser Chaos Mesh sur un cluster k3s
  • Écrire des expériences PodChaos, NetworkChaos, StressChaos, HTTPChaos et les enchaîner dans un Workflow

Chaos Engineering & Principes


Pourquoi casser volontairement ?

Les pannes arrivent de toute façon : la seule question est de savoir si on les découvre un mardi à 14h ou un samedi à 3h

  • Un nœud disparaît, un disque se remplit, un lien réseau se dégrade
  • Une dépendance répond lentement au lieu de répondre en erreur
  • Un déploiement introduit une régression invisible en test
  • Un composant « secondaire » (DNS, ingress) s'avère critique

Le Chaos Engineering remplace la confiance supposée par une confiance mesurée


Un peu d'histoire

  • 2010 : Netflix migre vers AWS et crée Chaos Monkey, qui termine des instances de production au hasard, aux heures ouvrées
  • 2011 : la Simian Army étend l'idée : Latency Monkey (latence), Chaos Gorilla (perte d'une zone entière)
  • 2012 : Chaos Monkey devient open source
  • Formalisation des principes sur principlesofchaos.org
  • Aujourd'hui : outils natifs Kubernetes (Chaos Mesh, LitmusChaos), services managés des cloud providers

L'idée clé de Netflix : si une panne est inévitable, autant la provoquer quand tout le monde est là pour apprendre


Définition

Le Chaos Engineering est la discipline qui consiste à expérimenter sur un système afin de bâtir la confiance dans sa capacité à résister à des conditions turbulentes en production

  • Ce n'est pas casser au hasard pour voir ce qui se passe
  • C'est une démarche scientifique : hypothèse, expérience, mesure, conclusion
  • Le résultat attendu est un apprentissage, pas une panne
  • Une hypothèse invalidée est un succès de l'expérience : une faiblesse a été trouvée avant les clients

Les principes avancés

  1. Bâtir une hypothèse autour de l'état stable : mesurer une sortie du système, pas son fonctionnement interne
  2. Faire varier des événements du monde réel : pannes matérielles, latence, pics de trafic, dépendances défaillantes
  3. Expérimenter en production : seul l'environnement réel a le vrai trafic et la vraie configuration
  4. Automatiser les expériences pour les jouer en continu : la résilience régresse comme le reste
  5. Minimiser le rayon d'impact : commencer petit et élargir progressivement

En formation, la « production » est votre plateforme : l'exigence de rigueur reste la même


La boucle d'expérimentation

 ┌──────────────────────────────────────────────────────────┐
 │ 1. Définir l'état stable   → SLI mesurés avant l'injection │
 │ 2. Formuler l'hypothèse    → « le SLO tient malgré X »    │
 │ 3. Borner l'expérience     → cible, durée, conditions     │
 │                              d'arrêt, retour arrière      │
 │ 4. Injecter la panne       → sous charge réaliste (k6)    │
 │ 5. Mesurer l'écart         → SLI pendant et après         │
 │ 6. Conclure                → validée ou invalidée         │
 │ 7. Corriger puis rejouer   → la correction est prouvée    │
 └──────────────────────────────────────────────────────────┘

L'hypothèse d'état stable

L'état stable se décrit avec des indicateurs orientés utilisateur, mesurés avant l'injection

  • Taux de succès des requêtes de paiement ≥ 99,5 %
  • Latence p95 < 300 ms
  • Débit k6 constant, sans erreur côté client
  • Aucune alerte critique active

Mauvais indicateurs d'état stable : « le CPU est à 30 % », « tous les pods sont Running »

Formulation type : « Pendant la perte d'un pod de l'API, le taux de succès reste ≥ 99,5 % et la p95 < 300 ms »


Rayon d'impact & conditions d'arrêt

Rayon d'impact : ce que l'expérience peut toucher

  • Un seul namespace, un sélecteur de labels précis
  • Un pod avant tous les pods, une minute avant une heure
  • Élargir uniquement quand l'itération précédente est comprise

Conditions d'arrêt : décidées avant l'injection

  • Burn rate du SLO au-delà du seuil d'alerte rapide
  • Alerte critique hors périmètre de l'expérience
  • Échec d'un contrôle HTTP de santé sur plusieurs essais consécutifs
  • Un bouton d'arrêt connu de tous : kubectl delete de l'expérience

GameDay

Un exercice planifié où l'équipe joue des pannes ensemble

  • Rôles : maître du jeu, opérateurs, observateurs, scribe
  • Scénario : liste d'expériences, ordre, durée, conditions d'arrêt
  • Annoncé ou non : commencer annoncé, puis tester aussi la détection
  • Ce qu'on teste : la plateforme, mais aussi les alertes, les runbooks et la communication
  • Débrief : chronologie, écarts à l'hypothèse, actions correctives avec responsables

Le GameDay produit des preuves datées et une liste d'améliorations, pas seulement des captures


Analyse des SPOF

Un SPOF (Single Point Of Failure) est un composant dont la perte rend le service indisponible

Composant Réplicas Répartis sur plusieurs nœuds ? Impact si perdu
API SecurePay 3 À vérifier Faible si répartie
CoreDNS À vérifier À vérifier Résolution de noms cassée
Ingress (Traefik) À vérifier À vérifier Plus aucun trafic entrant
Nœud server k3d 1 Non Plan de contrôle perdu

Questions à poser : combien d'exemplaires, où tournent-ils, qui en dépend, que se passe-t-il s'il disparaît ?


Chaos Mesh


Présentation de Chaos Mesh

  • Plateforme de Chaos Engineering native Kubernetes, projet CNCF en incubation
  • Chaque expérience est une ressource personnalisée (CRD) : YAML versionnable et relisible en Merge Request
  • Large catalogue de fautes : pods, réseau, ressources, HTTP, I/O, DNS, horloge
  • Planification avec Schedule et orchestration de scénarios avec Workflow
  • Dashboard web avec gestion des droits par RBAC Kubernetes

Épinglez une version vérifiée du chart et suivez les avis de sécurité du projet


Architecture

            kubectl apply / Dashboard
                       │
                       ▼
   ┌───────────────────────────────────────┐
   │ chaos-controller-manager (Deployment) │  réconcilie les CRD,
   │                                       │  gère Schedule et Workflow
   └───────────────────┬───────────────────┘
                       │ gRPC
   ┌───────────────────▼───────────────────┐
   │ chaos-daemon (DaemonSet privilégié)   │  entre dans les namespaces
   │ un pod par nœud                       │  réseau et PID des conteneurs
   └───────────────────────────────────────┘  via le runtime (containerd)

   chaos-dashboard   : interface web, jetons RBAC
   chaos-dns-server  : réponses DNS falsifiées pour DNSChaos

Le catalogue des fautes

CRD Actions principales Cas d'usage
PodChaos pod-kill, pod-failure, container-kill Perte de réplicas
NetworkChaos delay, loss, duplicate, corrupt, partition, bandwidth Réseau dégradé
StressChaos stress CPU, stress mémoire Surcharge, OOMKill
HTTPChaos abort, delay, replace, patch Dépendance HTTP défaillante
IOChaos latence, erreurs, attributs de fichiers Disque lent ou en erreur
DNSChaos error, random Résolution de noms cassée
TimeChaos décalage d'horloge Expiration de jetons, certificats

Sélectionner les cibles

apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata:
  name: api-pod-kill
  namespace: securepay
spec:
  action: pod-kill
  mode: one                    # one, all, fixed, fixed-percent, random-max-percent
  selector:
    namespaces:
      - securepay
    labelSelectors:
      app.kubernetes.io/name: securepay-api
  • mode et value règlent le rayon d'impact : fixed-percent avec value: "50" touche la moitié des pods
  • duration borne les fautes persistantes (pod-failure, réseau, stress)

NetworkChaos : latence

apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
  name: api-latency-300ms
  namespace: securepay
spec:
  action: delay
  mode: all
  selector:
    namespaces: [securepay]
    labelSelectors:
      app.kubernetes.io/name: securepay-api
  delay:
    latency: 300ms
    jitter: 50ms
  duration: 5m
  • action: loss avec loss: { loss: "20" } pour 20 % de perte de paquets
  • direction et target limitent la faute au trafic vers une dépendance précise

StressChaos : mémoire

apiVersion: chaos-mesh.org/v1alpha1
kind: StressChaos
metadata:
  name: api-memory-stress
  namespace: securepay
spec:
  mode: one
  selector:
    namespaces: [securepay]
    labelSelectors:
      app.kubernetes.io/name: securepay-api
  stressors:
    memory:
      workers: 2
      size: 512MB              # taille totale, répartie entre les workers
  duration: 3m
  • Le stresseur s'exécute dans le cgroup du conteneur ciblé : au-delà de la limite mémoire, le conteneur est tué (OOMKilled)
  • stressors.cpu avec workers et load pour une surcharge CPU (throttling)

HTTPChaos : réponses en erreur

apiVersion: chaos-mesh.org/v1alpha1
kind: HTTPChaos
metadata:
  name: api-http-500
  namespace: securepay
spec:
  mode: fixed
  value: "1"                   # un seul pod sur les réplicas
  selector:
    namespaces: [securepay]
    labelSelectors:
      app.kubernetes.io/name: securepay-api
  target: Response
  port: 3000
  path: /api/*
  replace:
    code: 500
  duration: 5m
  • La « fraction du trafic » touchée dépend du nombre de pods ciblés
  • HTTP en clair uniquement, et les connexions TCP déjà ouvertes ne sont pas affectées

Schedule : rejouer en continu

apiVersion: chaos-mesh.org/v1alpha1
kind: Schedule
metadata:
  name: api-pod-kill-hourly
  namespace: securepay
spec:
  schedule: "0 * * * *"
  historyLimit: 5
  concurrencyPolicy: Forbid
  type: PodChaos
  podChaos:
    action: pod-kill
    mode: one
    selector:
      namespaces: [securepay]
      labelSelectors:
        app.kubernetes.io/name: securepay-api
  • Met en œuvre le principe « automatiser pour jouer en continu »
  • À réserver aux expériences déjà maîtrisées en mode manuel

Workflow : un GameDay en YAML

apiVersion: chaos-mesh.org/v1alpha1
kind: Workflow
metadata:
  name: gameday-securepay
  namespace: securepay
spec:
  entry: gameday
  templates:
    - name: gameday
      templateType: Serial
      deadline: 20m
      children: [kill-one-pod, pause, add-latency]
    - name: kill-one-pod
      templateType: PodChaos
      deadline: 1m
      podChaos: { action: pod-kill, mode: one, selector: { labelSelectors: { app.kubernetes.io/name: securepay-api } } }
    - name: pause
      templateType: Suspend
      deadline: 3m
    - name: add-latency
      templateType: NetworkChaos
      deadline: 5m
      networkChaos: { action: delay, mode: all, selector: { labelSelectors: { app.kubernetes.io/name: securepay-api } }, delay: { latency: 300ms } }

Workflow : condition d'arrêt automatique

    - name: api-health-guard
      templateType: StatusCheck
      deadline: 20m
      abortWithStatusCheck: true      # arrête tout le Workflow en cas d'échec
      statusCheck:
        mode: Continuous
        type: HTTP
        intervalSeconds: 5
        timeoutSeconds: 2
        failureThreshold: 3            # trois échecs consécutifs
        successThreshold: 1
        http:
          url: http://securepay-api.securepay.svc:3000/ready
          method: GET
          criteria:
            statusCode: "200"
  • Placé dans un nœud Parallel à côté de la séquence d'expériences
  • Selon la documentation, les nœuds StatusCheck s'écrivent en YAML : le dashboard ne permet pas de les créer

Installation sur k3s

k3s embarque son propre containerd : le socket n'est pas au chemin standard

helm repo add chaos-mesh https://charts.chaos-mesh.org
helm upgrade --install chaos-mesh chaos-mesh/chaos-mesh \
  --namespace chaos-mesh --create-namespace \
  --version <version-verifiee> \
  --set chaosDaemon.runtime=containerd \
  --set chaosDaemon.socketPath=/run/k3s/containerd/containerd.sock \
  --set controllerManager.enableFilterNamespace=true \
  --set dashboard.securityMode=true

# Autoriser explicitement le namespace cible
kubectl annotate ns securepay chaos-mesh.org/inject=enabled
  • Avec enableFilterNamespace, seuls les namespaces annotés peuvent recevoir des fautes
  • Vérifiez les clés avec helm show values chaos-mesh/chaos-mesh pour la version choisie

Sécuriser le chaos

Un outil capable de tuer des processus partout est une cible de choix

  • Périmètre : enableFilterNamespace et annotation des seuls namespaces autorisés
  • RBAC : rôles distincts pour créer des expériences et pour les consulter, jetons dédiés au dashboard
  • Dashboard : securityMode actif, jamais exposé publiquement, accès par port-forward
  • chaos-daemon : DaemonSet privilégié, à traiter comme un composant sensible
  • Mises à jour : les failles « Chaotic Deputy » (2025) exposaient une API de debug non authentifiée du controller-manager, corrigées en 2.7.3
  • Traçabilité : expériences versionnées, appliquées par une personne identifiée

Chaos et GitOps

  • Versionnez les expériences dans chaos/ : elles sont relues en Merge Request comme le reste
  • Ne les synchronisez pas automatiquement avec ArgoCD : avec selfHeal, une expérience supprimée serait recréée et relancée
  • Appliquez-les explicitement, au moment choisi, par une personne identifiée
  • Exception : les Schedule validées, volontairement déclarées comme état désiré
kubectl apply -f chaos/01-api-pod-kill.yaml
kubectl get podchaos -n securepay
kubectl delete -f chaos/01-api-pod-kill.yaml   # bouton d'arrêt

Perte de nœud & Répartition


Que se passe-t-il quand un nœud disparaît ?

T0          Le nœud ne répond plus (k3d node stop k3d-securepay-agent-1)
T0 + ~50 s  node-monitor-grace-period dépassé : nœud NotReady / Unknown
            taint node.kubernetes.io/unreachable:NoExecute posé
            pods du nœud marqués non prêts, retirés des endpoints
T0 + ~6 min tolérance par défaut de 300 s expirée : éviction des pods
            le contrôleur recrée les pods sur les nœuds restants
  • Les tolérances not-ready et unreachable de 300 s sont ajoutées par le plugin d'admission DefaultTolerationSeconds
  • Entre T0 et le retrait des endpoints, les requêtes routées vers le nœud perdu échouent
  • Pendant six minutes, le service tourne avec une capacité réduite

Réduire l'impact de la perte d'un nœud

spec:
  topologySpreadConstraints:
    - maxSkew: 1
      topologyKey: kubernetes.io/hostname
      whenUnsatisfiable: DoNotSchedule
      labelSelector:
        matchLabels:
          app.kubernetes.io/name: securepay-api
  tolerations:
    - key: node.kubernetes.io/unreachable
      operator: Exists
      effect: NoExecute
      tolerationSeconds: 30
    - key: node.kubernetes.io/not-ready
      operator: Exists
      effect: NoExecute
      tolerationSeconds: 30
  • Répartir : une partie du service survit immédiatement
  • Raccourcir la tolérance : la capacité se reconstitue plus vite, au prix d'évictions lors de micro-coupures

LitmusChaos : l'alternative

Chaos Mesh LitmusChaos
Statut CNCF Incubation Incubation
Modèle Une CRD par type de faute Expériences issues du ChaosHub, orchestrées par ChaosCenter
Validation de l'état stable StatusCheck HTTP dans les Workflows Probes intégrées (HTTP, commande, Prometheus, Kubernetes)
Interface Dashboard ChaosCenter (multi-clusters, multi-équipes)
Prise en main Rapide, YAML direct Plus riche, plus de composants

Le choix d'outil compte moins que la rigueur de la démarche : hypothèse, bornes, mesure


Documenter : le rapport de résilience (exemple)

Expérience Hypothèse Résultat Impact SLO Preuve Action corrective
Pod-kill API SLO tenu Validée Aucun Capture Grafana 14h02 Aucune
Perte de nœud SLO tenu Invalidée 4 % d'erreurs pendant 50 s Résumé k6 Répartition + tolérance 30 s
Latence 300 ms p95 < 300 ms Invalidée p95 à 420 ms Capture Grafana Timeouts et retries revus
  • Une ligne par expérience, rejouée après correction
  • Les preuves sont horodatées et rangées avec le rapport

Synthèse


À retenir

  • Le Chaos Engineering est une démarche expérimentale, pas une destruction aléatoire
  • Pas d'expérience sans état stable mesuré, rayon d'impact borné et conditions d'arrêt
  • Chaos Mesh décrit chaque faute en YAML : versionnable, relisible, rejouable
  • Sur k3s, configurez le socket containerd /run/k3s/containerd/containerd.sock
  • Restreignez le chaos aux namespaces annotés et protégez le dashboard
  • La perte d'un nœud coûte jusqu'à six minutes de capacité par défaut : répartissez et réglez les tolérances
  • Une hypothèse invalidée, corrigée puis rejouée, est le meilleur résultat d'un GameDay

Pour aller plus loin