Expert Chapitre 39-40 / 24

Drift detection et self-healing & Multi-cluster : réconcilier une flotte

Comprendre comment un contrôleur détecte la dérive entre état désiré et état réel puis la corrige seul (self-healing), et comment ce même principe s'étend à la réconciliation d'une flotte de clusters — avec du YAML et kubectl.

Drift detection et self-healing

L’idée en une phrase

La détection de dérive (drift detection) est l’opération par laquelle un contrôleur mesure l’écart entre l’état désiré et l’état réel, et le self-healing est l’action corrective qui referme cet écart sans intervention humaine : c’est exactement la boucle observe → diff → agit appliquée non plus à la création initiale, mais au maintien permanent de la conformité au fil du temps.

Analogie : Considérons un pilote automatique de navire. Le cap à suivre est l’état désiré, la position mesurée par le GPS est l’état réel. Un courant latéral écarte progressivement le navire de sa route : c’est la dérive. Le pilote automatique compare en continu cap voulu et cap réel, puis corrige la barre pour ramener le navire sur sa trajectoire. Il n’attend pas d’ordre : la correction est déclenchée par l’écart lui-même.

Points clés

  • La dérive (drift) désigne toute divergence de l’état réel par rapport à l’état désiré survenue après l’application initiale. Ses causes typiques sont une modification manuelle (kubectl edit), une action d’un autre contrôleur, une panne de nœud, ou une suppression accidentelle de ressource. La dérive est normale dans un système vivant : la réconciliation existe précisément pour la corriger.
  • La détection repose sur une comparaison périodique ou événementielle. Un reconciler level-based (vu au chapitre day 13-14) relit l’état réel complet à chaque cycle et recalcule le diff : il détecte donc la dérive quelle qu’en soit la cause, y compris un changement qu’il n’a pas observé en direct. C’est la propriété qui rend le self-healing fiable.
  • Le self-healing est la phase « agit » déclenchée par un diff non nul. Il n’est pas une fonctionnalité distincte : c’est le comportement par défaut d’une boucle de réconciliation qui tourne en continu. Kubernetes se répare ainsi nativement — un pod supprimé est recréé par le ReplicaSet, un nœud perdu voit ses pods reprogrammés.
  • Tout écart n’est pas toujours à corriger. Certains champs sont gérés par d’autres acteurs (un HPA ajuste replicas, un webhook injecte des annotations). Réconcilier aveuglément ces champs provoque une guerre de contrôleurs : deux boucles réécrivent sans cesse la même valeur. La conception d’un reconciler robuste distingue les champs qu’il possède de ceux qu’il ignore.
  • Le self-healing corrige la dérive mais ne la prévient pas. La prévention relève des admission webhooks (vus au chapitre day 35-36) et du RBAC, qui bloquent les modifications non conformes avant qu’elles n’atteignent etcd. Détection et prévention sont complémentaires : l’une referme les écarts, l’autre en réduit la fréquence.

Exemple concret

Un Deployment déclare 3 replicas d’un service api. À t₀, tout est conforme : 3 pods Running. À t₀+2 min, un opérateur supprime manuellement un pod avec kubectl delete pod api-x. L’état réel passe à 2 pods ; l’état désiré reste 3. Le ReplicaSet-controller, qui réconcilie en continu, observe 2 pods pour une cible de 3, calcule l’écart (3 − 2 = 1) et crée un pod de remplacement. En moins d’une seconde, l’état réel converge de nouveau vers 3. Aucune commande n’a été émise pour réparer : la correction découle du diff. Si un contrôleur GitOps supervise en plus ce Deployment avec self-heal, une modification du champ replicas dans le cluster (par exemple porté à 5 via kubectl scale) serait elle aussi annulée au cycle suivant, car la source de vérité reste le manifeste versionné.

Distinction — dérive corrigée vs dérive ignorée

CritèreSelf-healing (corriger)Champ délégué (ignorer)
Origine de l’écartPanne, suppression, édition non autoriséeAutre contrôleur légitime (HPA, webhook)
Décision du reconcilerRéappliquer l’état désiréNe pas toucher au champ
Risque si mal géréDérive persistante non corrigéeGuerre de contrôleurs, oscillation
Mécanisme KubernetesBoucle level-based en continuignoreDifferences, ownership de champ

Code YAML — laisser un champ hors du périmètre de réconciliation

# application.yaml — ArgoCD ignore volontairement le champ replicas,
# car un HPA en est le propriétaire légitime : sinon les deux boucles s'affrontent.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: api
  namespace: argocd
spec:
  syncPolicy:
    automated:
      selfHeal: true            # corriger toute dérive NON déléguée
  ignoreDifferences:
    - group: apps
      kind: Deployment
      jsonPointers:
        - /spec/replicas        # ce champ appartient au HPA, pas à Git

Code bash — provoquer et observer le self-healing

# État désiré : 3 replicas
kubectl get deployment api -o jsonpath='{.spec.replicas}'
# 3

# Introduire une dérive : supprimer un pod à la main
kubectl delete pod -l app=api --field-selector status.phase=Running | head -1
# pod "api-7d4b9c-abc12" deleted

# Observer la correction quasi immédiate par le ReplicaSet-controller
kubectl get pods -l app=api
# NAME                READY   STATUS    RESTARTS   AGE
# api-7d4b9c-def34    1/1     Running   0          3s     <-- pod recréé
# api-7d4b9c-ghi56    1/1     Running   0          5m
# api-7d4b9c-jkl78    1/1     Running   0          5m

# Consulter l'événement qui trace la réconciliation
kubectl get events --field-selector reason=SuccessfulCreate | tail -1
# ... replicaset-controller  Created pod: api-7d4b9c-def34

Piège courant : « Le self-healing garantit que le cluster est toujours conforme » est inexact. Le self-healing garantit seulement que la boucle converge vers l’état désiré à chaque cycle ; entre deux cycles, l’état réel peut diverger. La conformité est une propriété éventuelle (convergence), pas instantanée. De plus, un self-healing appliqué à un champ possédé par un autre contrôleur ne rend pas le système conforme : il crée une oscillation permanente. La bonne conformité suppose de définir précisément quel acteur possède quel champ.


Multi-cluster : réconcilier une flotte

L’idée en une phrase

Réconcilier une flotte consiste à traiter un ensemble de clusters comme un seul état réel à faire converger vers un état désiré centralisé : un contrôleur de flotte observe l’inventaire des clusters, calcule pour chacun l’écart avec la configuration voulue, et propage les manifestes — la boucle observe → diff → agit s’applique à l’identique, en changeant seulement d’échelle et d’unité réconciliée (le cluster, non plus le pod).

Analogie : Considérons une chaîne de magasins pilotée par un siège. Le siège publie un planogramme unique décrivant l’agencement cible de chaque rayon. Un inspecteur régional visite les magasins, compare l’agencement réel au planogramme, et corrige les écarts localement. Le planogramme est l’état désiré centralisé, chaque magasin est un cluster, l’inspecteur est le contrôleur de flotte. Le siège n’agit jamais directement sur les rayons : il déclare, et la boucle propage.

Points clés

  • La réconciliation multi-cluster introduit deux niveaux : une boucle de flotte qui décide quel cluster reçoit quelle configuration, et les boucles locales de chaque cluster qui appliquent réellement les manifestes. Ce découplage reprend le principe de composition de boucles vu au chapitre day 37-38 avec Flux : chaque niveau réconcilie son propre écart.
  • Deux topologies dominent. En modèle hub-and-spoke (pull), chaque cluster exécute son propre agent (ArgoCD, Flux) qui tire l’état désiré depuis une source centrale ; le hub ne pousse rien. En modèle push, un contrôleur central applique les manifestes vers les API servers distants. Le modèle pull réduit la surface d’attaque : aucun credential cluster n’est stocké au centre.
  • La sélection de clusters remplace le selector de labels sur les pods par un selector sur les clusters. Un objet comme le Placement (Open Cluster Management) ou l’ApplicationSet (ArgoCD) déclare « déployer sur tous les clusters portant le label env=prod ». Ajouter un cluster labellisé env=prod déclenche automatiquement sa réconciliation : l’inventaire est lui-même un état réel observé.
  • Le status agrégé de la flotte reflète le diff global : combien de clusters sont Synced, combien OutOfSync, combien injoignables. Cet agrégat est la manifestation directe de la réconciliation à l’échelle de la flotte, comme le champ status d’un objet reflète le diff local (vu au chapitre day 5-6).
  • Un cluster injoignable ne doit pas être traité comme un cluster conforme. Un contrôleur de flotte robuste distingue « écart nul » (réconcilié) de « écart inconnu » (cluster non observable) et signale ce dernier plutôt que de le masquer. Confondre les deux revient à ignorer une partie de l’état réel.

Exemple concret

Une organisation exploite 12 clusters : 3 en env=prod, 4 en env=staging, 5 en env=dev. Un ApplicationSet ArgoCD déclare le déploiement d’un service de monitoring sur tous les clusters portant env=prod. À t₀, les 3 clusters prod sont Synced. À t₀+1 h, un nouveau cluster prod est enregistré avec le label env=prod. Le générateur de l’ApplicationSet observe ce nouvel élément d’inventaire, crée une Application pour ce cluster, et la boucle GitOps locale y déploie le monitoring — sans qu’aucun manifeste supplémentaire n’ait été écrit à la main. À t₀+3 h, l’un des clusters prod devient injoignable (panne réseau). Son Application passe à Unknown ; l’agrégat de flotte affiche 3/4 Synced et 1 Unknown. L’écart n’est pas corrigé — il est signalé, car un cluster non observable n’est pas un cluster conforme.

Comparaison — modèles de propagation multi-cluster

CritèreHub-and-spoke (pull)Push centralisé
Qui appliqueL’agent local de chaque clusterLe contrôleur central
Stockage des credentialsAucun credential au centreCredentials de tous les clusters au centre
Résilience si le centre tombeLes agents continuent de réconcilierLa propagation s’arrête
Passage à l’échelleÉlevé — chaque cluster est autonomeLimité par le contrôleur central
Détection de dérive localeContinue, par l’agent embarquéDépend des cycles du centre

Code YAML — un ApplicationSet qui réconcilie une flotte

# applicationset.yaml — génère une Application par cluster sélectionné.
# L'inventaire des clusters EST un état réel : ajouter un cluster labellisé
# env=prod suffit à déclencher son déploiement.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: monitoring-fleet
  namespace: argocd
spec:
  generators:
    - clusters:
        selector:
          matchLabels:
            env: prod           # selector NON sur des pods, mais sur des clusters
  template:
    metadata:
      name: 'monitoring-{{name}}'   # une Application par cluster cible
    spec:
      project: default
      source:
        repoURL: https://github.com/acme/fleet-config.git
        targetRevision: main         # source unique de l'état désiré de la flotte
        path: monitoring
      destination:
        server: '{{server}}'         # l'API server du cluster sélectionné
        namespace: monitoring
      syncPolicy:
        automated:
          selfHeal: true             # self-healing appliqué sur CHAQUE cluster

Code bash — observer la réconciliation d’une flotte

# Lister les clusters enregistrés et leurs labels
kubectl get secrets -n argocd -l argocd.argoproj.io/secret-type=cluster \
  -o custom-columns=NAME:.metadata.name,ENV:.metadata.labels.env
# NAME          ENV
# prod-eu-1     prod
# prod-eu-2     prod
# prod-us-1     prod

# Une Application a été générée automatiquement par cluster prod
kubectl get applications -n argocd -l app.kubernetes.io/instance=monitoring-fleet
# NAME                    SYNC STATUS   HEALTH
# monitoring-prod-eu-1    Synced        Healthy
# monitoring-prod-eu-2    Synced        Healthy
# monitoring-prod-us-1    Unknown       Missing     <-- cluster injoignable, écart INCONNU

# Agréger le statut de synchronisation sur toute la flotte
kubectl get applications -n argocd -l app.kubernetes.io/instance=monitoring-fleet \
  -o jsonpath='{range .items[*]}{.status.sync.status}{"\n"}{end}' | sort | uniq -c
#   2 Synced
#   1 Unknown

Piège courant : « Réconcilier une flotte, c’est appliquer les mêmes manifestes partout depuis un point central » est une simplification trompeuse. D’une part, l’uniformité n’est pas toujours désirée : chaque cluster peut avoir des paramètres propres (région, taille, secrets), et la réconciliation doit composer un état désiré par cluster, pas un état unique dupliqué. D’autre part, un modèle purement centralisé (push) fait du hub un point de défaillance unique et concentre tous les credentials. Le modèle pull, où chaque cluster réconcilie localement à partir d’une source déclarative partagée, préserve l’autonomie de chaque boucle : une panne du centre n’interrompt pas le self-healing local. La flotte converge parce que chaque cluster converge, pas parce qu’un chef d’orchestre pousse en permanence.


Quiz — validation des acquis
Expert 7 questions Objectif : 5/7 minimum
0/7
bonnes reponses
Objectif non atteint (minimum 5/7 requis).
Relire la fiche memo ci-dessus en pretant attention aux points manques, puis cliquer sur « Recommencer » pour retenter.