Amélioration de la résilience via l’ingénierie du chaos et l’injection d’erreurs

Publié le 27 juillet, 2020

Chief Technology Officer, Microsoft Azure

« Lorsque j’ai lancé, pour la première fois, cette série de blogs relative à l’amélioration de la fiabilité dans mon billet de blog de juillet, je mettais en évidence plusieurs initiatives prévues pour améliorer la disponibilité des plateformes, dans le cadre de notre engagement à fournir un ensemble de services cloud fiable. J’ai notamment mentionné l’injection d’erreurs, grâce à laquelle nous confirmons de plus en plus que les systèmes réagiront comme prévu aux défaillances. Aujourd’hui, j’ai demandé à notre responsable de programme principal dans cet espace, Chris Ashton, de nous faire part de ces concepts plus larges sur « l’ingénierie du chaos » et d’exposer les grandes lignes des exemples Azure montrant la manière dont nous les appliquons déjà, en plus des charges de travail synthétiques et des tests de contrainte. Tout cela a pour but d’améliorer la résilience des applications et des services. », Mark Russinovich, Directeur technique, Azure


 

Le développement d’applications distribuées à grande échelle n’a jamais été aussi simple, mais il y a un piège. Effectivement, l’infrastructure est fournie en quelques minutes grâce au cloud public, mettant à disposition de nombreuses options de langage, des échantillons de code en open source exploitables, ainsi que des composants et des services en abondance sur la marketplace à développer. Effectivement, il existe de bons guides de référence qui vous permettent de prendre une longueur d’avance sur l’architecture et la conception de votre solution, comme l’infrastructure Azure Well-Architected Framework et d’autres ressources dans le Centre des architectures Azure. Toutefois, si le développement des applications est plus facile, le risque d’impact des perturbations des dépendances est important. Bien que ce soit rare, des pannes hors de votre contrôle pourraient se produire à tout moment, vos dépendances pourraient être impactées ou le temps de réponse de vos services et systèmes clés pourrait être plus long. Des perturbations mineures dans un domaine pourraient s’amplifier et avoir des effets secondaires dans un autre domaine pendant longtemps. Ces interruptions de service peuvent éroder la productivité des développeurs, affecter négativement la confiance des clients, engendrer des pertes commerciales ou encore impacter le chiffre d’affaires d’une organisation.

Les applications modernes, ainsi que les plateformes cloud sur lesquelles elles sont générées, doivent être conçues et validées en continu pour éviter les défaillances. Les développeurs doivent tenir compte des conditions de défaillance connues et inconnues, les applications et les services doivent être conçus pour la redondance, les algorithmes nécessitent des mécanismes de nouvelle tentative et d’interruption. Les systèmes doivent être résilients aux scénarios et aux conditions provoqués par des interruptions et des pannes de production peu fréquentes, mais inévitables. Cette publication a pour but de vous amener à réfléchir sur la meilleure manière de valider des conditions de défaillance classiques, notamment des exemples de la manière dont nous validons nos propres systèmes chez Microsoft.

Résilience

La résilience est la capacité d’un système à échouer de manière appropriée face à des événements perturbateurs et de récupérer par la suite. La validation de la résilience d’une application, d’un service ou d’une plateforme est aussi importante que leur création en tenant compte des défaillances. Il est facile et tentant de valider la fiabilité des composants individuels de manière isolée et d’en déduire que l’ensemble du système est tout aussi fiable, mais cela peut s’avérer une erreur. La résilience est une propriété de l’ensemble du système, et pas seulement de ses composants. Pour déterminer si un système est véritablement résilient, il est préférable de mesurer et de comprendre la résilience de l’ensemble du système dans l’environnement où il s’exécutera. Mais comment procéder et par où commencer ?

Ingénierie du chaos et injection d’erreurs

L’ingénierie du chaos est la pratique qui consiste à soumettre un système aux défaillances réelles et aux perturbations de dépendances auxquelles il sera confronté en phase de production. L’injection d’erreurs est l’introduction délibérée de défaillances dans un système afin de valider sa robustesse et son aptitude à gérer les erreurs.

Grâce à l’utilisation de l’injection d’erreurs et à l’application des pratiques d’ingénierie du chaos, les architectes renforcent la confiance que l’on peut avoir en leurs conceptions et les développeurs peuvent mesurer, comprendre et améliorer la résilience de leurs applications. De même, les ingénieurs de fiabilité de site (SRE) et toute personne qui tient ses plus grandes équipes pour responsables dans cet espace, peuvent s’assurer que leurs objectifs de niveau de service sont cohérents et peuvent surveiller l’intégrité du système en production. De la même manière, les équipes des opérations peuvent valider le nouveau matériel et les nouveaux centres de données avant de les déployer pour une utilisation par le client. L’incorporation de techniques du chaos dans la validation des versions permet à tout le monde, notamment aux gestionnaires, de faire confiance aux systèmes que leur organisation crée.

Tout au long du processus de développement, que, nous l’espérons, vous avez peut-être déjà commencé, testez tôt et testez souvent. Lorsque vous vous préparez à mettre votre application ou service en production, suivez les pratiques de test normales en ajoutant et en exécutant des tests unitaires et fonctionnels, ainsi que des tests de contrainte et d’intégration. Lorsque cela vous paraît judicieux, ajoutez une couverture de test pour les cas de défaillance et utilisez l’injection d’erreurs pour confirmer la gestion des erreurs et le comportement de l’algorithme. Pour un impact encore plus conséquent, et c’est ici que l’ingénierie du chaos rentre vraiment en jeu, augmentez les charges de travail de bout en bout (telles que les tests de contrainte, les tests de performances ou les charges de travail synthétiques) avec l’injection d’erreurs. Commencez dans un environnement de test de préproduction avant d’effectuer des expériences en phase de production et comprenez le comportement de votre solution dans un environnement sécurisé avec une charge de travail synthétique avant d’engendrer un impact potentiel sur le trafic client réel.

L’utilisation correcte de l’injection d’erreurs dans un processus de validation peut inclure un ou plusieurs des éléments suivants :

  • Validation ad hoc de nouvelles fonctionnalités dans un environnement de test :
    Un développeur peut mettre en place une machine virtuelle de test et exécuter du nouveau code de manière isolée. Lors de l’exécution de tests fonctionnels ou de tests de contrainte existants, des erreurs peuvent être injectées pour bloquer l’accès réseau à une dépendance distante (par exemple, SQL Server) pour prouver que le nouveau code gère correctement le scénario.
  • Couverture de l’injection d’erreurs automatisée dans un pipeline CI/CD, notamment les portes de déploiement ou de résilience :
    Les tests de scénarios de bout en bout existants (tels que les tests d’intégration ou de contrainte) peuvent être accentués avec l’injection d’erreurs. Insérez simplement une nouvelle étape après l’exécution normale pour prolonger l’exécution ou la relancer après l’application d’erreurs. Les erreurs ajoutées peuvent détecter des problèmes qui n’auraient, en temps normal, pas été détectés par les tests, ou accélérer la détection de problèmes qui auraient été détectés dans tous les cas.
  • Validation de correctif d’incident et test de régression d’incident :
    L’injection d’erreurs peut être utilisée conjointement avec une charge de travail ou une exécution manuelle pour induire des conditions similaires à celles responsables de l’incident, ce qui rend possible la validation du correctif d’un incident spécifique ou un test de régression d’un scénario d’incident.
  • BCDR explore un environnement de préproduction :
    Les erreurs responsables des basculements de base de données ou qui occupent de l’espace de stockage hors connexion peuvent être utilisées dans les exercices BCDR pour valider le comportement approprié des systèmes face à ces erreurs et confirmer que les données ne sont pas perdues lors des tests de basculement.
  • Jours de jeu en production :
    Un « jour de jeu » est une simulation coordonnée d’une panne ou d’un incident qui permet de vérifier que les systèmes gèrent correctement l’événement. Cela implique généralement la validation des systèmes de surveillance, ainsi que des processus humains qui entrent en jeu pendant un incident. Les équipes qui effectuent des jours de jeu peuvent tirer parti des outils d’injection d’erreurs pour orchestrer, de manière contrôlée, des erreurs constituant un scénario hypothétique.

Pipeline de mise en production classique

Cette illustration montre un pipeline de mise en production classique et des opportunités d’inclusion de l’injection d’erreurs :

opportunités de chaos du pipeline de mise en production

 

Un investissement dans l’injection d’erreurs aura plus de succès s’il est basé sur quelques composants fondamentaux :

  • Pipeline de déploiement coordonné.
  • Déploiements ARM automatisés.
  • Exécuteurs et charges de travail de bout en bout synthétiques.
  • Tableaux de bord de surveillance, d’alerte et de site actif.

Une fois ces éléments mis en place, l’injection d’erreurs peut être intégrée au processus de déploiement avec peu de surcharge supplémentaire et elle peut servir à prendre en charge le flux de code en production.

Les pannes de courant localisées du rack et les défaillances de l’équipement ont sont considérées comme des points de défaillance uniques lors de l’analyse de la cause racine des incidents passés. Apprendre qu’un service est affecté par un de ces événements en phase de production et qu’il n’y est pas résilient est un long processus pénible et coûteux pour un ingénieur d’astreinte. En plusieurs occasions, l’injection d’erreurs permet de valider la résilience à ces défaillances à travers le pipeline de mise en production dans un environnement et un délai contrôlés, ce qui donne également à l’auteur du code davantage de possibilités de mener une enquête sur les problèmes non couverts. Un développeur qui détient des modifications du code ou un nouveau code peut créer un environnement de test, déployer le code et effectuer des expériences ad hoc à l’aide de tests et d’outils fonctionnels contenant des erreurs qui simulent la mise hors connexion des dépendances, telles que la suppression de machines virtuelles, le blocage de l’accès aux services ou simplement la modification des autorisations. Dans un environnement intermédiaire, l’injection d’erreurs similaires peut être ajoutée à des tests d’intégration et des tests de bout en bout automatisés ou encore à d’autres charges de travail synthétiques. Les résultats des tests et la télémétrie peuvent ensuite être utilisés pour déterminer l’impact des erreurs et être comparés aux performances de ligne de base pour bloquer le flux de code si nécessaire.

Dans un environnement de pré-production ou « Canaries », les exécuteurs automatisés peuvent être utilisés avec des erreurs qui bloquent à nouveau l’accès aux dépendances ou qui les déconnectent. Des tableaux de bord de surveillance, d’alerte et de site actif peuvent ensuite permettre de vérifier que des pannes ont été observées et que le système a réagi et réglé le problème : qu’il a ainsi prouvé sa résilience. Dans ce même environnement, les équipes de SRE ou des opérations peuvent également effectuer des explorations de continuité d’activité/récupération d’urgence (BCDR) en utilisant l’injection d’erreurs pour mettre en mode hors connexion le stockage ou les bases de données, puis réanalyser les mesures du système afin de vérifier la résilience et l’intégrité des données. Ces mêmes activités Canaries peuvent également être exécutées en production, où il existe un vrai trafic client, mais cela augmente le risque d’impact sur les clients. C’est pourquoi il est recommandé de les exécuter après exploitation de l’injection des erreurs plus tôt dans le pipeline. La mise en œuvre de ces pratiques et l’incorporation de l’injection d’erreurs dans un pipeline de déploiement permet une validation systématique et contrôlée de la résilience, ce qui permet aux équipes d’atténuer les problèmes et d’améliorer la fiabilité des applications, sans pour autant affecter les clients finaux.

Injection d’erreurs chez Microsoft

Chez Microsoft, certaines équipes intègrent l’injection d’erreurs tôt dans le pipeline de validation, ainsi que les tests automatisés. Les différentes équipes exécutent, comme d’habitude, des tests de contrainte, des tests de performances ou des charges de travail synthétiques dans leurs portes de validation automatisées et une ligne de base est établie. Ensuite, la charge de travail est réexécutée, cette fois avec des erreurs, telles que la sollicitation du processeur, l’instabilité des E/S de disque ou la latence du réseau. Afin d’évaluer l’impact, les résultats de la charge de travail sont surveillés, la télémétrie est analysée, les vidages sur incident sont vérifiés et les indicateurs de niveau de service (SLI) sont comparés aux objectifs de niveau de service (SLO). Si les résultats sont considérés comme un échec, le code peut ne pas passer à l’étape suivante dans le pipeline.

D’autres équipes Microsoft utilisent l’injection d’erreurs dans le cadre de la continuité des activités, des explorations de récupération d’urgence (BCDR) et des jours de jeu. Certaines équipes font des explorations BCDR mensuelles, trimestrielles ou semestrielles et utilisent l’injection d’erreurs pour induire un sinistre et valider le processus de récupération, ainsi que les alertes, la surveillance et les processus de site actif. Cette opération est souvent effectuée dans un environnement Canaries de préproduction avant d’être utilisée dans la production avec le trafic client réel. Certaines équipes effectuent également des jours de jeu, elles créent alors un scénario hypothétique, tel que la réplication d’un incident passé et utilisent l’injection d’erreurs pour faciliter leur orchestration. Dans ce cas, les erreurs peuvent être plus destructrices, par exemple, les pannes de machines virtuelles, la désactivation de l’accès réseau, le basculement de la base de données ou la simulation d’un centre de données entier mis hors connexion. Là encore, les alertes et la surveillance de site en direct normales sont utilisées afin que vos processus de gestion DevOps et des incidents soient également validés. Pour ne pas déranger les personnes concernées, ces activités sont généralement effectuées pendant les horaires de bureau et non pendant la nuit ou le week-end.

Nos équipes des opérations peuvent également utiliser l’injection d’erreurs pour valider du nouveau matériel avant son déploiement pour une utilisation par le client. Les explorations sont effectuées dans les racks mis hors tension ou dans les centres de données de façon à observer les systèmes de surveillance et de sauvegarde afin de s’assurer qu’ils se comportent comme prévu.

Chez Microsoft, nous utilisons des principes d’ingénierie du chaos et des techniques d’injection d’erreurs pour augmenter la résilience des produits que nous livrons et notre confiance en eux. Ils permettent de valider les applications que nous livrons aux clients et les services que nous mettons à la disposition des développeurs. Ils permettent de valider la plateforme Azure sous-jacente afin de tester le nouveau matériel avant son déploiement. Ils contribuent, conjointement et séparément, à améliorer la fiabilité globale de la plateforme Azure et la qualité de l’ensemble de nos services.

Conséquences inattendues

N’oubliez pas que l’injection d’erreurs est un outil puissant qui doit être utilisé avec précaution. Des dispositifs de protection doivent être mis en place pour garantir que les erreurs introduites dans un environnement de test ou de préproduction n’affecteront pas aussi la production. Le rayon d’impact d’un scénario d’erreur doit être contenu pour réduire son impact sur les autres composants et sur les clients finaux. La possibilité d’injecter des erreurs doit être faire l’objet d’un accès restreint afin d’éviter les accidents et d’empêcher son utilisation potentielle par des utilisateurs malveillants. L’injection d’erreurs peut être utilisée pendant la production, mais planifiez-la soigneusement, testez-la d’abord en préproduction, limitez le rayon d’impact et prévoyez une sécurité pour vous assurer qu’une expérience peut être terminée brusquement si nécessaire. L’accident nucléaire de Tchernobyl en 1986 est un triste exemple d’exploration d’injection d’erreurs qui tourne mal. Veillez à protéger votre système des conséquences inattendues.

Le chaos en tant que service ?

Comme Mark Russinovich l’a mentionné précédemment dans ce billet de blog, notre objectif est de mettre des services d’injection d’erreurs natives à la disposition des clients et des partenaires afin qu’ils puissent effectuer la même validation sur leurs propres applications et services. Il s’agit d’un espace passionnant avec de grandes possibilités d’améliorer la fiabilité du service cloud et de réduire l’impact des interruptions rares, mais inévitables. De nombreuses équipes font beaucoup de choses intéressantes dans cet espace et nous explorons la meilleure façon de rassembler tous ces outils et erreurs disparates pour faciliter notre vie et celle de nos développeurs internes qui créent des services Azure pour les services Azure intégrés, tels que Microsoft 365, Microsoft Teams et Dynamics, mais aussi pour que nos clients et partenaires utilisent les mêmes outils en vue d’endommager leurs propres applications et solutions dans le but d’améliorer leur résilience.