Résilience, upgrade et gouvernance
Extrait du mémoire de Master 2 Cybersécurité et Cloud de Holali David GAVI — Partie 3/3.
3.1 Rolling updates
Le rolling update est la stratégie de déploiement par défaut dans Kubernetes. Le principe, décrit par Burns (2018), consiste à remplacer progressivement les anciennes instances (Pods) par les nouvelles, en maintenant un nombre minimum d’instances disponibles tout au long du processus.
Le Deployment Kubernetes contrôle ce processus via deux paramètres clés :
- maxUnavailable — Nombre (ou pourcentage) maximum de Pods pouvant être indisponibles pendant la mise à jour. Un
maxUnavailable: 1signifie qu’au plus 1 Pod sera arrêté à la fois. - maxSurge — Nombre (ou pourcentage) maximum de Pods supplémentaires pouvant être créés au-delà du nombre de réplicas désiré. Un
maxSurge: 1permet de créer 1 Pod de la nouvelle version avant de terminer un ancien.
Le mécanisme repose sur les readiness probes : un nouveau Pod n’est intégré au Service (et donc ne reçoit du trafic) que lorsque sa probe de readiness renvoie un succès. Cette approche garantit que les utilisateurs ne sont jamais routés vers une instance qui n’est pas prête à servir des requêtes. Les liveness probes complètent le dispositif en détectant les Pods dans un état dysfonctionnel (deadlock, corruption mémoire) pour déclencher leur redémarrage automatique (Hightower et al., 2019).
Luksa (2018) souligne l’importance du graceful shutdown dans les rolling updates. Lorsqu’un Pod reçoit un signal SIGTERM, il doit cesser d’accepter de nouvelles requêtes, terminer les requêtes en cours (draining), puis s’arrêter proprement. Le terminationGracePeriodSeconds (défaut : 30s) définit le délai maximum accordé. Les hooks preStop permettent d’exécuter des actions personnalisées avant l’arrêt, comme la désinscription du service discovery ou le flush des caches.
Les PodDisruptionBudgets (PDB) ajoutent une couche de protection supplémentaire en garantissant qu’un nombre minimum de Pods reste disponible lors d’opérations volontaires (drain de nœud, mise à jour du cluster). Un PDB avec minAvailable: 2 empêche Kubernetes de terminer des Pods si cela ferait passer le nombre de Pods disponibles sous 2 (Kubernetes Documentation, 2024).
3.2 Blue/Green et Canary
Les stratégies Blue/Green et Canary offrent un contrôle plus fin sur le déploiement que les rolling updates, au prix d’une complexité accrue.
Blue/Green deployment — Deux environnements identiques coexistent : Blue (version actuelle en production) et Green (nouvelle version). Le trafic est basculé instantanément de Blue vers Green une fois la validation terminée. Humble et Farley (2010) présentent cette stratégie comme « le Saint Graal du déploiement sans risque » : en cas de problème, le rollback est immédiat — il suffit de rerouter le trafic vers Blue.
Dans Kubernetes, le Blue/Green se réalise via la manipulation des labels de Service :
- Le Deployment Blue a le label
version: blue - Le Deployment Green a le label
version: green - Le Service pointe vers
version: blue - Après validation de Green, le selector du Service est mis à jour vers
version: green
L’inconvénient principal est le coût en ressources : deux environnements complets doivent coexister temporairement, doublant la consommation de CPU/mémoire. Pour les déploiements à grande échelle, cela peut représenter un surcoût significatif.
Canary deployment — Introduit par Humble et Farley (2010) par analogie avec les canaris utilisés dans les mines de charbon pour détecter les gaz toxiques, le déploiement canary consiste à exposer progressivement la nouvelle version à un sous-ensemble croissant d’utilisateurs : 1%, 5%, 25%, 50%, 100%.
Les outils de service mesh comme Istio et Linkerd permettent un contrôle granulaire du trafic via les VirtualServices et DestinationRules d’Istio, ou les TrafficSplit de SMI (Service Mesh Interface). Le routage peut être basé sur le poids (80/20), les headers HTTP (utilisateurs beta), les cookies, ou la géolocalisation (Posta et Maloku, 2022).
Flagger (Weaveworks/Flux) et Argo Rollouts automatisent le processus canary : ils analysent les métriques (taux d’erreur, latence P99, taux de succès) via Prometheus et augmentent ou rollbackent automatiquement le trafic. Stefan Prodan (2023) décrit cette approche comme du « progressive delivery » — un déploiement guidé par les données plutôt que par l’intuition humaine.
3.3 Migration de bases de données
La migration de schéma de base de données dans un contexte de déploiement continu représente l’un des défis les plus complexes de l’ingénierie logicielle. Sadalage et Fowler (2012), dans Refactoring Databases, établissent le principe de migrations évolutives : chaque changement de schéma doit être rétrocompatible avec la version applicative précédente, permettant ainsi des rollbacks sans perte de données.
Les outils de migration — Flyway (Redgate), Liquibase, Alembic (Python), Prisma Migrate — gèrent les migrations comme des scripts versionnés, exécutés séquentiellement. Chaque migration est identifiée par un numéro de version ou un timestamp, et l’outil maintient une table de suivi (flyway_schema_history) dans la base de données.
Le pattern Expand and Contract (Sadalage & Fowler, 2012) est essentiel pour les déploiements sans downtime :
- Phase Expand — Ajout de la nouvelle colonne/table sans supprimer l’ancienne. L’application v1 continue de fonctionner normalement. L’application v2 écrit dans les deux colonnes.
- Phase Migrate — Migration des données existantes vers le nouveau format (batch job, trigger SQL).
- Phase Contract — Suppression de l’ancienne colonne/table une fois que toutes les instances sont sur v2 et que le rollback vers v1 n’est plus nécessaire.
Dans Kubernetes, les migrations sont typiquement exécutées via des Helm hooks (pre-upgrade) ou des Init Containers. Le hook pre-upgrade lance un Job Kubernetes qui exécute les migrations avant le déploiement de la nouvelle version. Si la migration échoue, le déploiement est annulé. Il est crucial de séparer les migrations de schéma du déploiement applicatif pour éviter les situations de blocage (Kleppmann, 2017).
Pour les bases de données distribuées (Cassandra, CockroachDB, YugabyteDB), les migrations de schéma sont intrinsèquement plus complexes en raison de la réplication asynchrone. Le consensus doit être atteint entre tous les nœuds avant que le changement de schéma ne soit effectif, ce qui peut prendre plusieurs minutes dans les clusters géo-distribués.
3.4 Gestion des dépendances multi-services
Dans une architecture microservices, les services sont rarement indépendants : le service de commande dépend du service de catalogue, qui dépend du service d’inventaire, etc. Newman (2021) identifie cette gestion des dépendances comme « le problème fondamental des architectures distribuées » — un problème que le monolithe résolvait trivialement par les appels de méthode en mémoire.
Les dépendances inter-services se manifestent à plusieurs niveaux :
- Dépendances de déploiement — Le service A v2 nécessite le service B v3. Si A est déployé avant B, des erreurs surviennent. La solution est le versioning d’API (URI versioning
/api/v2/, header versioning, content negotiation) pour maintenir la compatibilité ascendante. - Dépendances runtime — Le service A appelle le service B de manière synchrone. Si B est lent ou indisponible, A est impacté. Les patterns Circuit Breaker, Timeout et Fallback (Nygard, 2018) atténuent ces risques.
- Dépendances de données — Les services partagent des concepts métier (Customer, Order, Product). Le Domain-Driven Design (Evans, 2003) et les Bounded Contexts délimitent la responsabilité de chaque service sur son propre modèle de données.
Le Consumer-Driven Contract Testing (CDCT), implémenté par des outils comme Pact (DiUS, 2013), vérifie automatiquement la compatibilité entre services : le consommateur définit un contrat (« j’attends un champ email de type string dans la réponse de /users/{id} »), et le pipeline CI du producteur valide ce contrat à chaque commit. Cette approche détecte les breaking changes avant le déploiement (Newman, 2021).
Pour l’orchestration des déploiements multi-services, ArgoCD propose les Sync Waves : chaque application reçoit un numéro de wave, et ArgoCD déploie séquentiellement les waves dans l’ordre croissant. La wave 0 contient les dépendances fondamentales (bases de données, caches), la wave 1 les services backend, la wave 2 les API gateways, et la wave 3 les frontends (ArgoCD Documentation, 2024).
3.5 Patterns de rollback
La capacité de rollback — revenir à un état antérieur stable — est une exigence non fonctionnelle critique pour tout système en production. Beyer et al. (2016), dans le Site Reliability Engineering (SRE) de Google, affirment que « la capacité de rollback rapidement est plus importante que la capacité de déployer rapidement ».
Kubernetes offre un mécanisme de rollback natif au niveau des Deployments :
kubectl rollout undo deployment/<name>— Revient à la révision précédentekubectl rollout undo deployment/<name> --to-revision=<n>— Revient à une révision spécifiquekubectl rollout history deployment/<name>— Liste l’historique des révisions
Le paramètre revisionHistoryLimit (défaut : 10) détermine le nombre de ReplicaSets conservés pour le rollback. Chaque ReplicaSet conserve le template de Pod complet (image, configuration, variables d’environnement), permettant un retour exact à l’état précédent.
Avec Helm, le rollback est encapsulé dans la gestion des releases : helm rollback <release> <revision> restaure l’ensemble des manifests Kubernetes (Deployment, Service, ConfigMap, etc.) à la version spécifiée. Les Helm hooks pre-rollback et post-rollback permettent d’exécuter des actions compensatoires, comme la restauration du schéma de base de données.
En GitOps, le rollback se résume à un git revert : le commit de revert déclenche automatiquement la synchronisation par ArgoCD/Flux, qui converge vers l’état précédent. Ce mécanisme offre un avantage majeur : le rollback est auditable, traçable et reproductible — contrairement à un kubectl rollout undo impératif (Limoncelli et al., 2020).
Pour les rollbacks de données, la complexité augmente considérablement. Si la migration de base de données est irréversible (suppression de colonne, transformation destructive), le rollback applicatif ne suffit pas. Les stratégies incluent :
- Snapshots pré-migration — Backup complet avant chaque migration, restaurable en cas de besoin. Le Volume Snapshot de Kubernetes (CSI driver) automatise ce processus.
- Migrations réversibles — Chaque migration possède un script
upet un scriptdown. Flyway et Liquibase supportent nativement les rollback scripts. - Event Sourcing — L’état est reconstruit à partir des événements. Le rollback consiste à ignorer les événements postérieurs à un timestamp donné (Betts et al., 2013).
3.6 Observabilité et monitoring
L’observabilité, concept emprunté à la théorie du contrôle par Kalman (1960), désigne la capacité à comprendre l’état interne d’un système à partir de ses sorties externes. Dans le contexte des systèmes distribués, Majors et al. (2022) définissent trois piliers de l’observabilité :
1. Les Métriques — Mesures numériques agrégées dans le temps. Prometheus, projet CNCF gradué, est le standard de facto pour la collecte de métriques dans Kubernetes. Son modèle pull (scraping HTTP), son langage de requêtes PromQL et son intégration native avec Kubernetes (ServiceMonitors, PodMonitors via Prometheus Operator) en font un composant incontournable.
Les métriques suivent typiquement la méthodologie RED (Rate, Errors, Duration) pour les services et USE (Utilization, Saturation, Errors) pour les ressources infrastructure, telles que formalisées par Brendan Gregg (2020). Les SLIs (Service Level Indicators), SLOs (Service Level Objectives) et SLAs (Service Level Agreements) structurent la relation entre l’observabilité et les objectifs métier (Beyer et al., 2016).
Grafana est l’outil de visualisation dominant, avec un écosystème de dashboards prêts à l’emploi pour Kubernetes (kube-state-metrics, node-exporter, cadvisor). L’alerte est gérée par Alertmanager (Prometheus) avec routage intelligent, regroupement (grouping), silencing et inhibition pour éviter la fatigue d’alerte (alert fatigue).
2. Les Logs — Enregistrements textuels des événements. Dans Kubernetes, la stack EFK (Elasticsearch, Fluentd/Fluent Bit, Kibana) ou la stack Loki + Grafana centralise les logs de tous les conteneurs. Fluent Bit, agent léger déployé en DaemonSet, collecte les logs des conteneurs via les fichiers de log de containerd (/var/log/containers/), les enrichit avec les métadonnées Kubernetes (namespace, pod name, labels) et les transmet au backend de stockage.
Loki (Grafana Labs) se distingue par son approche « like Prometheus, but for logs » : il n’indexe que les labels (metadata), pas le contenu des logs, réduisant drastiquement les coûts de stockage et la complexité opérationnelle par rapport à Elasticsearch. Le langage LogQL permet de requêter les logs de manière similaire à PromQL.
3. Les Traces distribuées — Suivi du parcours d’une requête à travers les microservices. OpenTelemetry (fusion d’OpenTracing et OpenCensus, projet CNCF) fournit un SDK multi-langage unifié pour l’instrumentation. Chaque requête se voit attribuer un trace ID unique, propagé via les headers HTTP (traceparent, standard W3C Trace Context). Chaque appel inter-service génère un span avec durée, status et attributs.
Les backends de traces — Jaeger (Uber, CNCF), Zipkin (Twitter), Tempo (Grafana Labs) — stockent et visualisent ces traces. Jaeger affiche les traces sous forme de graphe temporel (Gantt chart), permettant d’identifier instantanément le service responsable d’une latence excessive ou d’une erreur.
La convergence des trois piliers est une tendance majeure : Grafana propose une plateforme unifiée (Mimir pour les métriques, Loki pour les logs, Tempo pour les traces) avec des liens croisés entre piliers. Un pic de latence sur un dashboard Grafana peut être corrélé directement avec les traces correspondantes et les logs des Pods concernés — réduisant le Mean Time To Resolution (MTTR) de manière significative (Majors et al., 2022).
En matière de gouvernance, l’observabilité alimente les processus de revue post-incident (postmortem) et d’amélioration continue. Google (Beyer et al., 2016) préconise les blameless postmortems : l’analyse se concentre sur les causes systémiques (processus, outillage, documentation) plutôt que sur les responsabilités individuelles. Chaque incident doit aboutir à des action items concrets : ajout d’une alerte, modification d’un runbook, correction d’un bug, ou amélioration de l’architecture.
Références
- ArgoCD Documentation (2024). « Sync Waves and Resource Hooks ». argo-cd.readthedocs.io.
- Betts, D., Dominguez, J., Melnik, G. & Simonazzi, F. (2013). Exploring CQRS and Event Sourcing. Microsoft Patterns & Practices.
- Beyer, B., Jones, C., Petoff, J. & Murphy, N. R. (2016). Site Reliability Engineering. O’Reilly Media / Google.
- Burns, B. (2018). Designing Distributed Systems. O’Reilly Media.
- Evans, E. (2003). Domain-Driven Design. Addison-Wesley.
- Gregg, B. (2020). Systems Performance, 2nd Edition. Addison-Wesley.
- Hightower, K., Burns, B. & Beda, J. (2019). Kubernetes: Up and Running, 2nd Edition. O’Reilly Media.
- Humble, J. & Farley, D. (2010). Continuous Delivery. Addison-Wesley.
- Kalman, R. E. (1960). « On the General Theory of Control Systems ». IFAC Proceedings, 1(1), 491-502.
- Kleppmann, M. (2017). Designing Data-Intensive Applications. O’Reilly Media.
- Kubernetes Documentation (2024). « Pod Disruption Budgets ». kubernetes.io.
- Limoncelli, T. A., Chalup, S. R. & Hogan, C. J. (2020). The Practice of Cloud System Administration, 2nd Edition. Addison-Wesley.
- Luksa, M. (2018). Kubernetes in Action. Manning Publications.
- Majors, C., Fong-Jones, L. & Miranda, G. (2022). Observability Engineering. O’Reilly Media.
- Newman, S. (2021). Building Microservices, 2nd Edition. O’Reilly Media.
- Nygard, M. (2018). Release It!, 2nd Edition. Pragmatic Bookshelf.
- Posta, C. & Maloku, R. (2022). Istio in Action. Manning Publications.
- Prodan, S. (2023). « Progressive Delivery with Flagger ». flagger.app.
- Sadalage, P. & Fowler, M. (2012). Refactoring Databases. Addison-Wesley.