Aller au contenu

GameDay SecurePay avec Chaos Mesh

Objectifs pédagogiques

À la fin de ce TP, vous devez être capables de :

  • installer Chaos Mesh sur un cluster k3s en confinant les fautes à un seul namespace
  • mesurer l'état stable de l'API avant toute injection
  • concevoir, borner et jouer au moins cinq expériences sous charge réaliste
  • identifier un point de défaillance unique (SPOF), le corriger puis rejouer l'expérience
  • orchestrer un GameDay rejouable avec un Workflow muni d'une condition d'arrêt automatique
  • produire un rapport de résilience appuyé sur des preuves horodatées

Prérequis

  • Le cluster k3d securepay (1 server + 2 agents) tourne dans la VM securepay-prod
  • L'API SecurePay est déployée dans le namespace securepay avec au moins 3 réplicas et des probes liveness / readiness configurées
  • La stack kube-prometheus-stack est installée dans monitoring et un dashboard Grafana affiche le taux de succès et la latence p95 de l'API
  • Un scénario k6 de charge nominale est disponible (par exemple tests/robustness/nominal.js)
  • kubectl, helm, k3d et k6 sont installés

Labels, ports et noms

Les exemples supposent que les pods de l'API portent le label app.kubernetes.io/name: securepay-api, que le conteneur écoute sur le port 3000 et que le Service securepay-api expose ce même port. Adaptez ces valeurs à votre chart Helm.

Consignes de sécurité

  • Ne ciblez que le namespace securepay, sauf expérience explicitement annoncée et bornée dans le temps
  • Chaque expérience a un bouton d'arrêt connu de toute l'équipe : kubectl delete -f <fichier>
  • Aucune injection sans observabilité : le dashboard Grafana doit être ouvert avant de commencer
  • N'exposez jamais le dashboard Chaos Mesh hors du cluster : accès par port-forward uniquement
  • Annoncez chaque injection à voix haute et notez l'heure exacte : elle servira à horodater les preuves

Contexte

SecurePay prépare sa mise en production et le comité de direction ne lèvera le gel qu'au vu de preuves de résilience. Vous organisez le premier GameDay de la plateforme : une série de pannes contrôlées, jouées sous charge, dont chaque résultat est mesuré, documenté et, si besoin, corrigé puis rejoué.

Les expériences sont versionnées dans le dossier chaos/ du dépôt securepay-platform, et les résultats consignés dans docs/resilience.md avec leurs preuves dans docs/preuves/.

Atelier 1 : Installer et confiner Chaos Mesh

Contexte

Chaos Mesh repose sur un DaemonSet privilégié capable d'agir dans n'importe quel conteneur du nœud. Avant la première expérience, son périmètre doit être restreint et son dashboard protégé.

Travail demandé

  1. Installez Chaos Mesh dans le namespace chaos-mesh avec le socket containerd propre à k3s et le filtrage par namespace activé
helm repo add chaos-mesh https://charts.chaos-mesh.org
helm repo update
helm show values chaos-mesh/chaos-mesh --version <version-verifiee> | less

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
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: chaos-mesh
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://charts.chaos-mesh.org
    chart: chaos-mesh
    targetRevision: <version-verifiee>
    helm:
      valuesObject:
        chaosDaemon:
          runtime: containerd
          socketPath: /run/k3s/containerd/containerd.sock
        controllerManager:
          enableFilterNamespace: true
        dashboard:
          securityMode: true
  destination:
    server: https://kubernetes.default.svc
    namespace: chaos-mesh
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
      - ServerSideApply=true

Version

Épinglez une version vérifiée du chart, postérieure aux correctifs de sécurité « Chaotic Deputy » (2.7.3), et contrôlez le nom des clés avec helm show values : elles peuvent évoluer d'une version à l'autre.

CRD volumineuses

Les CRD de Chaos Mesh sont volumineuses. Si la synchronisation ArgoCD échoue sur la taille d'une annotation, l'option ServerSideApply=true résout le problème.

  1. Autorisez explicitement le namespace applicatif à recevoir des fautes
kubectl annotate ns securepay chaos-mesh.org/inject=enabled

Dans une démarche GitOps, portez plutôt cette annotation dans le manifest du namespace versionné dans securepay-platform.

  1. Vérifiez l'installation : un chaos-controller-manager, un chaos-daemon par nœud (3 au total), le chaos-dashboard et, selon la version, le chaos-dns-server
kubectl get pods -n chaos-mesh -o wide
  1. Prouvez le confinement : appliquez une expérience PodChaos (pod-kill, mode: one) ciblant le namespace monitoring, non annoté. L'expérience ne doit trouver aucune cible et aucun pod de monitoring ne doit redémarrer. Supprimez-la ensuite.

  2. Créez un accès au dashboard limité au namespace securepay

apiVersion: v1
kind: ServiceAccount
metadata:
  name: chaos-operator
  namespace: securepay
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: chaos-operator
  namespace: securepay
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["chaos-mesh.org"]
    resources: ["*"]
    verbs: ["get", "list", "watch", "create", "delete", "patch", "update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: chaos-operator
  namespace: securepay
subjects:
  - kind: ServiceAccount
    name: chaos-operator
    namespace: securepay
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: chaos-operator
kubectl create token chaos-operator -n securepay --duration=8h
kubectl -n chaos-mesh port-forward svc/chaos-dashboard 2333:2333

Connectez-vous sur http://localhost:2333 avec le jeton généré.

Ce que vous devez observer

  • un chaos-daemon sur chacun des trois nœuds k3d
  • une expérience visant un namespace non annoté reste sans effet
  • le dashboard refuse l'accès sans jeton et ne montre que ce que le RBAC autorise

Critère de réussite de l'atelier 1

L'atelier est réussi si vous pouvez démontrer que Chaos Mesh est opérationnel, que les fautes sont confinées au namespace securepay et que le dashboard n'est accessible qu'avec un jeton à droits restreints.

Atelier 2 : Mesurer l'état stable et cartographier les SPOF

Contexte

Une expérience sans état stable de référence ne prouve rien. Avant d'injecter quoi que ce soit, mesurez le comportement nominal de l'API et identifiez les composants dont la perte serait fatale.

Travail demandé

  1. Lancez la charge nominale pendant 10 minutes et relevez les indicateurs de référence

Si vous n'avez pas encore de scénario, partez de ce script minimal :

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  vus: 20,
  duration: '10m',
  thresholds: {
    http_req_failed: ['rate<0.005'],
    http_req_duration: ['p(95)<300'],
  },
};

const BASE_URL = __ENV.BASE_URL || 'http://api.securepay.local';
const payload = JSON.stringify({ amount: 42.5, currency: 'EUR', recipientId: 'merchant-001' });

export default function () {
  const res = http.post(`${BASE_URL}/api/payments/process`, payload, {
    headers: { 'Content-Type': 'application/json' },
  });
  check(res, { 'paiement accepté': (r) => r.status === 201 });
  sleep(0.5);
}

Requêtes PromQL de référence (adaptez le nom du label de code HTTP à votre instrumentation) :

# Taux de succès sur 1 minute
sum(rate(http_requests_total{namespace="securepay", code!~"5.."}[1m]))
/
sum(rate(http_requests_total{namespace="securepay"}[1m]))

# Latence p95
histogram_quantile(0.95,
  sum by (le) (rate(http_request_duration_seconds_bucket{namespace="securepay"}[1m])))
  1. Consignez l'état stable dans docs/resilience.md : taux de succès, p95, débit, nombre de pods prêts, alertes actives

  2. Cartographiez les SPOF de la plateforme

kubectl get pods -n securepay -o wide
kubectl -n kube-system get deploy coredns traefik
kubectl get nodes -o wide

Complétez un tableau composant → réplicas → nœuds → dépendants → impact si perdu pour au moins l'API, CoreDNS, Traefik, le nœud k3d-securepay-server-0 et Prometheus.

  1. Préparez une fiche par expérience dans docs/resilience.md
### E<n> - <titre de l'expérience>

- Hypothèse :
- Cible et rayon d'impact :
- Durée :
- Condition d'arrêt :
- Bouton d'arrêt :
- Indicateurs observés :
- Résultat (validée / invalidée) :
- Impact sur le SLO :
- Preuves :
- Action corrective :

Ce que vous devez observer

  • un état stable chiffré qui respecte le SLO (≥ 99,5 % de succès, p95 < 300 ms)
  • la répartition réelle des pods de l'API sur les nœuds
  • au moins un composant à réplica unique ou concentré sur un seul nœud

Critère de réussite de l'atelier 2

L'atelier est réussi si l'état stable est mesuré et consigné, et si la cartographie des SPOF est documentée.

Atelier 3 : Le catalogue d'expériences du GameDay

Contexte

Chaque expérience se joue sous charge nominale, avec l'heure d'injection notée, le dashboard Grafana ouvert et le bouton d'arrêt prêt. Une hypothèse invalidée n'est pas un échec : elle révèle une faiblesse que vous corrigerez puis prouverez en rejouant l'expérience.

Travail demandé

Jouez au minimum les cinq expériences obligatoires E1 à E5. E6 est fortement recommandée, car elle prépare le TP d'auto-remédiation.

E1 - Perte d'un pod de l'API

  • Hypothèse : pendant la perte d'un pod, le taux de succès reste ≥ 99,5 % et la p95 < 300 ms
  • Condition d'arrêt : taux de succès < 99 % pendant plus d'une minute
apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata:
  name: api-pod-kill
  namespace: securepay
spec:
  action: pod-kill
  mode: one
  gracePeriod: 0
  selector:
    namespaces: [securepay]
    labelSelectors:
      app.kubernetes.io/name: securepay-api

Jouez l'expérience une première fois avec gracePeriod: 0 (suppression brutale), puis avec gracePeriod: 30. Comparez les erreurs observées par k6 : les requêtes en cours sur le pod tué sont-elles perdues ? L'application gère-t-elle SIGTERM proprement ?

E2 - Perte d'un nœud

Cette expérience se joue hors de Chaos Mesh, en arrêtant un nœud k3d depuis la VM.

  • Hypothèse : les réplicas restants absorbent la charge, le taux de succès reste ≥ 99,5 %
  • Condition d'arrêt : taux de succès < 95 % : redémarrez immédiatement le nœud
kubectl get pods -n securepay -o wide          # quels pods tournent sur agent-1 ?
date +%H:%M:%S && k3d node stop k3d-securepay-agent-1

kubectl get nodes -w                            # dans un autre terminal
kubectl get pods -n securepay -o wide -w        # dans un troisième terminal

k3d node start k3d-securepay-agent-1            # retour à la normale

Mesurez trois durées : apparition de l'état NotReady, éviction des pods du nœud perdu, retour au nombre de réplicas prêts attendu.

Plan de contrôle

N'arrêtez pas k3d-securepay-server-0 dans cette expérience : il porte le plan de contrôle (API server, ArgoCD ne pourra plus synchroniser, le HPA ne réagira plus). Sa perte peut faire l'objet d'une expérience avancée, annoncée et bornée.

Action corrective attendue si l'hypothèse est invalidée : topologySpreadConstraints sur kubernetes.io/hostname et tolérances node.kubernetes.io/unreachable / not-ready réduites (par exemple 30 s), puis nouvelle exécution de l'expérience.

E3 - Coupure de la résolution DNS

CoreDNS est un service critique partagé par toute la plateforme. Deux variantes sont possibles :

Le rayon d'impact est limité aux pods de l'API : elles ne peuvent plus résoudre aucun nom.

apiVersion: chaos-mesh.org/v1alpha1
kind: DNSChaos
metadata:
  name: api-dns-error
  namespace: securepay
spec:
  action: error
  mode: all
  selector:
    namespaces: [securepay]
    labelSelectors:
      app.kubernetes.io/name: securepay-api
  duration: 3m

Sans champ patterns, la faute s'applique à tous les noms de domaine. Le serveur DNS de chaos est déployé par défaut depuis Chaos Mesh 2.6 ; sur une version antérieure, activez-le avec --set dnsServer.create=true.

Expérience annoncée, limitée à 2 minutes, hors Chaos Mesh, car elle touche kube-system :

kubectl -n kube-system get deploy coredns        # notez le nombre de réplicas
kubectl -n kube-system scale deploy coredns --replicas=0
# ... observation pendant 2 minutes maximum ...
kubectl -n kube-system scale deploy coredns --replicas=<valeur-initiale>

Observez ce qui casse au-delà de l'API : Grafana interroge Prometheus par son nom DNS, ArgoCD résout l'adresse de la forge Git.

  • Hypothèse à formuler vous-mêmes : selon que l'API appelle ou non des dépendances par leur nom, l'impact sur son SLO peut être nul alors que l'observabilité ou le GitOps sont touchés. Documentez-le : c'est un résultat
  • Action corrective si CoreDNS est un SPOF : plusieurs réplicas répartis et un PDB

Composants packagés de k3s

k3s réapplique ses manifests packagés (CoreDNS, Traefik) au redémarrage du serveur : une modification faite avec kubectl peut être écrasée. Documentez la correction pérenne retenue, par exemple la désactivation du composant packagé au profit d'un chart géré en GitOps.

E4 - Dégradation réseau : latence puis perte de paquets

  • Hypothèse : la latence ajoutée se répercute sur la p95 sans provoquer d'erreurs en cascade ; l'alerte de latence se déclenche
  • Condition d'arrêt : taux de succès < 99 %

Jouez d'abord 300 ms de latence pendant 5 minutes (manifest NetworkChaos avec action: delay vu en cours), puis 20 % de perte de paquets pendant 5 minutes :

apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
  name: api-packet-loss
  namespace: securepay
spec:
  action: loss
  mode: all
  selector:
    namespaces: [securepay]
    labelSelectors:
      app.kubernetes.io/name: securepay-api
  loss:
    loss: "20"
    correlation: "0"
  duration: 5m

Surveillez aussi les probes : la faute réseau s'applique au trafic du pod, y compris aux réponses aux probes du kubelet. Un pod dont la readinessProbe échoue est retiré du trafic, ce qui peut transformer une dégradation partielle en perte de capacité.

E5 - Stress mémoire jusqu'à l'OOMKill

  • Prérequis : une limite mémoire est définie sur le conteneur de l'API (par exemple 256Mi)
  • Hypothèse : un pod tué par dépassement mémoire est redémarré sans impact sur le SLO, les autres réplicas absorbant la charge
  • Condition d'arrêt : plus d'un pod en redémarrage simultanément
apiVersion: chaos-mesh.org/v1alpha1
kind: StressChaos
metadata:
  name: api-memory-oom
  namespace: securepay
spec:
  mode: one
  selector:
    namespaces: [securepay]
    labelSelectors:
      app.kubernetes.io/name: securepay-api
  stressors:
    memory:
      workers: 2
      size: 512MB
  duration: 3m

Vérifiez la cause du redémarrage :

kubectl get pods -n securepay -w
kubectl describe pod <pod> -n securepay | grep -A3 "Last State"

La section Last State doit indiquer Terminated avec la raison OOMKilled. Observez également si le stress est réinjecté après le redémarrage du conteneur pendant la durée de l'expérience.

E6 - Réponses HTTP 500 sur une fraction du trafic

  • Hypothèse : avec un pod sur trois renvoyant des erreurs 500, l'alerte de burn rate rapide se déclenche en moins de 5 minutes
  • Condition d'arrêt : durée de 5 minutes atteinte
apiVersion: chaos-mesh.org/v1alpha1
kind: HTTPChaos
metadata:
  name: api-http-500
  namespace: securepay
spec:
  mode: fixed
  value: "1"
  selector:
    namespaces: [securepay]
    labelSelectors:
      app.kubernetes.io/name: securepay-api
  target: Response
  port: 3000
  path: /api/*
  replace:
    code: 500
  duration: 5m

Réutilisation des connexions

HTTPChaos ne s'applique qu'aux nouvelles connexions TCP. Le trafic qui passe par Traefik réutilise des connexions déjà ouvertes vers les pods : appliquez l'expérience avant de lancer k6, et si aucune erreur n'apparaît, redémarrez Traefik (kubectl -n kube-system rollout restart deploy/traefik) ou générez la charge depuis un pod du cluster avec l'option k6 noConnectionReuse: true. Cette limite est en elle-même un enseignement à consigner.

Ce que vous devez observer

  • E1 : un nouveau pod remplace le pod tué ; l'écart entre gracePeriod: 0 et gracePeriod: 30 révèle la qualité de l'arrêt propre
  • E2 : un délai d'environ 50 s avant NotReady, puis environ 5 minutes supplémentaires avant l'éviction avec les tolérances par défaut
  • E3 : l'impact réel de la perte du DNS, souvent ailleurs que là où on l'attendait
  • E4 : une p95 dégradée, et potentiellement des pods retirés du trafic par leurs probes
  • E5 : un conteneur OOMKilled et son compteur de redémarrages qui augmente
  • E6 : un taux d'erreur proche d'un tiers et le déclenchement de l'alerte de burn rate

Critère de réussite de l'atelier 3

L'atelier est réussi si au moins cinq expériences ont été jouées sous charge, chacune avec sa fiche complète, et si au moins un SPOF ou une faiblesse a été corrigé puis validé par une seconde exécution.

Atelier 4 : Un GameDay rejouable avec un Workflow

Contexte

Un GameDay manuel est difficile à rejouer à l'identique. Un Workflow Chaos Mesh enchaîne les expériences, insère des pauses de récupération et s'arrête de lui-même si l'API devient indisponible.

Travail demandé

  1. Autorisez le contrôle de santé du Workflow à joindre l'API : il est émis depuis le namespace chaos-mesh, bloqué par une NetworkPolicy en refus par défaut
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-chaos-status-check
  namespace: securepay
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: securepay-api
  policyTypes: [Ingress]
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: chaos-mesh
      ports:
        - protocol: TCP
          port: 3000
  1. Écrivez chaos/gameday-workflow.yaml : un nœud Parallel qui exécute en même temps un garde-fou StatusCheck et la séquence d'expériences
apiVersion: chaos-mesh.org/v1alpha1
kind: Workflow
metadata:
  name: gameday-securepay
  namespace: securepay
spec:
  entry: gameday
  templates:
    - name: gameday
      templateType: Parallel
      deadline: 25m
      children: [api-health-guard, scenario]

    - name: api-health-guard
      templateType: StatusCheck
      deadline: 25m
      abortWithStatusCheck: true
      statusCheck:
        mode: Continuous
        type: HTTP
        intervalSeconds: 5
        timeoutSeconds: 2
        failureThreshold: 3
        successThreshold: 1
        http:
          url: http://securepay-api.securepay.svc:3000/ready
          method: GET
          criteria:
            statusCode: "200"

    - name: scenario
      templateType: Serial
      deadline: 24m
      children: [kill-one-pod, recover-1, add-latency, recover-2, http-errors]

    - name: kill-one-pod
      templateType: PodChaos
      deadline: 1m
      podChaos:
        action: pod-kill
        mode: one
        selector:
          namespaces: [securepay]
          labelSelectors:
            app.kubernetes.io/name: securepay-api

    - name: recover-1
      templateType: Suspend
      deadline: 3m

    - name: add-latency
      templateType: NetworkChaos
      deadline: 5m
      networkChaos:
        action: delay
        mode: all
        selector:
          namespaces: [securepay]
          labelSelectors:
            app.kubernetes.io/name: securepay-api
        delay:
          latency: 300ms
          jitter: 50ms

    - name: recover-2
      templateType: Suspend
      deadline: 3m

    - name: http-errors
      templateType: HTTPChaos
      deadline: 5m
      httpChaos:
        mode: fixed
        value: "1"
        selector:
          namespaces: [securepay]
          labelSelectors:
            app.kubernetes.io/name: securepay-api
        target: Response
        port: 3000
        path: /api/*
        replace:
          code: 500
  1. Lancez le Workflow sous charge k6 et suivez son exécution
kubectl apply -f chaos/gameday-workflow.yaml
kubectl get workflow -n securepay
kubectl get workflownode -n securepay -w
kubectl describe workflow gameday-securepay -n securepay
  1. Prouvez la condition d'arrêt : ajoutez temporairement à la séquence une expérience PodChaos pod-failure en mode: all sur l'API, rejouez le Workflow et vérifiez qu'il est interrompu après trois échecs consécutifs du contrôle de santé, et que l'expérience en cours est levée

Pas de synchronisation automatique

Versionnez le Workflow dans chaos/ mais ne le faites pas synchroniser automatiquement par ArgoCD : avec selfHeal, un Workflow supprimé serait recréé et rejoué sans que personne ne l'ait décidé.

Ce que vous devez observer

  • les expériences s'enchaînent avec leurs pauses de récupération
  • le garde-fou tourne pendant toute la durée du scénario
  • un Workflow qui rend l'API indisponible est interrompu de lui-même

Critère de réussite de l'atelier 4

L'atelier est réussi si le GameDay est rejouable par une seule commande et si l'arrêt automatique sur échec du contrôle de santé est démontré.

Atelier 5 : Rapport de résilience et preuves

Contexte

Pour le comité de direction comme pour un auditeur, seule compte une séquence horodatée et mesurée : avant, pendant et après chaque panne.

Travail demandé

  1. Pour chaque expérience, rangez dans docs/preuves/ au minimum une capture Grafana couvrant avant, pendant et après l'injection, ainsi que l'export JSON du résumé k6 (fonction handleSummary). Nommez chaque fichier AAAA-MM-JJ_HHMM_<scenario>.<ext>

  2. Complétez la synthèse de docs/resilience.md

Expérience Hypothèse Résultat Impact SLO Preuve Action corrective
E1 - Pod-kill
E2 - Perte de nœud
E3 - Coupure DNS
E4 - Latence / perte
E5 - OOMKill
E6 - HTTP 500
  1. Pour chaque hypothèse invalidée, décrivez la correction appliquée et joignez la preuve de la seconde exécution

Critère de réussite de l'atelier 5

L'atelier est réussi si chaque ligne du rapport renvoie à une preuve horodatée et si chaque hypothèse invalidée est suivie d'une correction prouvée.

Questions de synthèse

  • Pourquoi l'état stable doit-il s'exprimer en indicateurs orientés utilisateur plutôt qu'en consommation de ressources ?
  • Quelle expérience a invalidé votre hypothèse de départ, et qu'avez-vous appris sur la plateforme ?
  • Pourquoi la perte d'un nœud dégrade-t-elle le service pendant plusieurs minutes, et quels réglages réduisent cette fenêtre ?
  • En quoi le confinement par namespace et le RBAC de Chaos Mesh relèvent-ils de la sécurité, et pas seulement du confort ?
  • Quelles expériences de ce GameDay mériteraient d'être automatisées avec un Schedule, et à quelles conditions ?