urbanisation-si

urbanisation-si

Solutions sur étagère pour la gestion des défaillances des Micro-Services

L'objectif des micro-services est d'accélérer la mise en production des applications, en décomposant l'application en petits services autonomes, qui peuvent être déployés indépendamment les uns des autres. Une architecture de micro-services pose également certains problèmes. Voici une synthèse des patterns les plus utilisés afin d'éviter les pannes.

 

solutions-sur-etagere-defaillances-micro-service

 

 

Le génie logiciel emprunte de nombreuses idées au génie civil, où les ingénieurs s'occupent de la conception, de la construction et de l'entretien des routes, des ponts, des canaux, des barrages et des bâtiments.

Ces ingénieurs prévoient les défaillances des composants individuels et pour pallier cela, ils assurent plusieurs redondances pour des bâtiments plus sûrs et plus stables.

Le même “mindset” peut être appliqué au génie logiciel. Des défaillances isolées sont inévitables, mais l'objectif reste de concevoir des systèmes fonctionnant aussi longtemps que possible. 

Les pratiques d'ingénierie, telles que la modélisation et l’injection de défaillances, devraient être incluses dans le cadre d'un processus d’intégration et de livraison continue, afin de concevoir des systèmes plus fiables. 

 

Circuit Breaker (Disjoncteur)


Le modèle "Circuit Breaker" (disjoncteur) est couramment utilisé pour garantir que, en cas de défaillance d’un micro-service, l'ensemble du système ne soit pas affecté. 

Cela se produit si le volume des appels au service ayant échoué est élevé et, pour chaque appel, on doit attendre qu'un délai se produise avant de continuer.

Faire l'appel au service défaillant et attendre utiliserait des ressources, ce qui finirait par rendre le système global instable.

Le modèle de "Circuit Breaker" se comporte comme un simple disjoncteur, comme celui de votre circuit électrique domestique. Les appels à un micro-service sont enveloppés dans un objet "Circuit Breaker".

Lorsqu'un micro-service défaille, l'objet "Circuit Breaker" autorise les appels ultérieurs au service jusqu'à ce qu'un seuil de tentatives infructueuses soit atteint.

À ce moment-là, le "Circuit Breaker" pour ce micro-service se déclenche, et tout autre appel sera court-circuité et n'entraînera pas d'appels au micro-service en échec.

Ce pattern d'installation économise de précieuses ressources et maintient la stabilité globale du système.

 

 

micro-service-circuit-breaker

 

(Diagramme de séquence UML du "Cicuit Breaker")

 

Lorsque le "Circuit Breaker" se déclenche et que le circuit est ouvert, un traitement de secours peut être démarré à la place. La logique de secours effectue généralement peu ou pas de traitements et renvoie une valeur. Ces actions de secours doivent avoir peu de chances d'échouer, car ils s'exécutent à la suite d'un échec au départ.

 

Bulkheads (Cloisonnement) 


L’informatique a souvent repris à son compte des concepts appartenant à d’autres domaines comme pour la sécurité des navires avec les cloisons étanches des coques. Si l'une des cloisons est endommagée, cette défaillance est limitée à cette seule cloison et les autres cloisons permettent de garder le bateau à flot.

La même approche de partitionnement peut également être utilisée dans les logiciels pour isoler une partie du système. 

Ce pattern peut également être appliqué dans les micro-services eux-mêmes. Par exemple, un pool de threads utilisés pour atteindre deux systèmes existants. Si l'un des systèmes a commencé à connaître un ralentissement et a provoqué l'épuisement du pool de threads, l'accès à l'autre système existant sera également affecté.

Avoir des pools de threads séparés garantit qu'un ralentissement dans un système existant n'épuise que son propre pool de threads.

 

 

micro-service-bulkheads

 

(Exemple de pattern "bulkhead" : utilisation de pools de threads séparés)

 

Gestion décentralisée des données


Dans une application monolithique, il est facile de traiter les transactions, car tous les composants font partie du monolithe.

Lorsque vous passez à une architecture de micro-services distribuée, vous devez désormais potentiellement traiter des transactions réparties sur plusieurs services.

Dans une architecture de micro-services, la préférence va à "BASE" (Basically Available, Soft state, Eventual consistency), plutôt que de type "ACID" (Atomicity, Consistency, Isolation, Durability).

Les transactions distribuées sont à éviter chaque fois que c'est possible.

Idéalement, chaque micro-service gère sa propre base de données. Cela permet d’être agnostique sur les moyens de persistance (SQL, NoSQL…).

Une contrainte de préservation du caractère ACID d’une transaction peut aboutir à ce que plusieurs micro-services utilisent la même base de données. Quelle qu'en soit la raison, une attention particulière doit être accordée au partage de la base de données.

Les avantages et les inconvénients de le faire doivent être pris en considération.

A l’opposé, le partage d'une base de données peut violer le principe de parfaites indépendance et autonomie de chaque micro-service. En effet dans ce cas, les micro-services pourraient dépendre des changements dans la base apportés par un autre micro-service.

Le niveau de partage entre les micro-services doit être limité autant que possible pour rendre les micro-services aussi faiblement couplés que possible.

 

Discoverability (Découverte)


L'architecture des micro-services nécessite de rendre les services fiables et tolérants aux pannes. Cela implique la construction de micro-services de telle manière que, si l'infrastructure sous-jacente est détruite, alors les micro-services peuvent être reconfigurés et communiquer avec d’autres micro-services pour les besoins de l’application globale.

L'utilisation du cloud computing et des conteneurs, pour le déploiement de micro-services, offre la possibilité d’une reconfiguration dynamique. Lorsque la nouvelle instance du micro-service est créée, les autres micro-services peuvent la trouver rapidement et commencer à communiquer avec elle grâce à un système de découverte des micro-services reposant sur un registre (ex. open source : Netflix Eureka), auxquels tous les micro-services doivent s’enregistrer explicitement.

Avec un tel système, l’équilibrage de charge (load balancing ; ex. open source : Netflix Ribbon) devient alors une réalité.

 

Conception de communication inter-services


Lorsque les micro-services sont répartis sur les différents serveurs (Tomcat), conteneurs (Docker) ou machines virtuelles (Oracle Virtual Box), on peut se poser la question : comment communiquent-ils ?

Une première solution est de mettre en place des files d'attente de messages qui suffisent pour les communications unidirectionnelles.

Avec un monolithe applicatif, les clients de l'application (navigateurs ou applications natives) font des requêtes HTTP (Hypertext Transfer Protocol) à un équilibreur de charge, ce qui propage la demande à plusieurs instances identiques de l'application. 
Mais dans l'architecture micro-services, le monolithe a été remplacé par une collection de micro-services. Une deuxième solution consiste à utiliser le protocole REST (REpresentational State Transfer), pour la communication des micro-services entre eux ainsi qu’avec le client.

 

 

micro-service-communication-rest

 

(Appels entre les clients et les micro-services en utilisant des API REST)


À première vue, cela peut sembler souhaitable. Cependant, certains clients peuvent être « bavards » ; d'autres peuvent nécessiter de nombreux appels pour obtenir les données. Étant donné que les services peuvent évoluer différemment, il y a d'autres défis à relever, comme le traitement des défaillances partielles.

Une meilleure approche pour le client est de faire peu de requêtes, peut-être juste une, en utilisant le pattern du GoF (Gang of Four) "façade", de type API Gateway (passerelle).

 

micro-service-api-gateway

 

(Intégration du pattern "façade" comme l'API Gateway)


L'API Gateway se situe entre les clients et les micro-services et fournit des API qui sont sur mesure pour les clients. Elle consiste principalement à masquer la complexité technologique (par exemple, la connectivité à un ordinateur central), par rapport à la complexité de l'interface.

Une API Gateway est donc une façade. Cependant, une façade offre une vue uniforme des éléments internes complexes aux clients externes, alors qu'une API Gateway fournit une vue uniforme des ressources externes aux composants internes d'une application.

Tout comme le modèle de conception de façade, l'API Gateway fournit une interface simplifiée aux clients, ce qui rend les services plus faciles à utiliser, à comprendre et à tester.

En effet, il peut fournir différents niveaux de granularité aux clients. L'API Gateway peut fournir des API à grosses granularités aux appareils mobiles et des API à fines granularités aux ordinateurs de bureau, qui pourraient utiliser un réseau haut débit. Dans ce scénario, l'API Gateway réduit la conversation en permettant aux clients d'agréger plusieurs demandes en une seule, optimisée pour un client donné (tel que le client mobile).

 

HTTP synchrone versus messagerie asynchrone

Il existe deux approches principales pour la communication interprocessus :

  • Les mécanismes synchrones basés sur HTTP, tels que REST, SOAP ou WebSocket, qui permettent de conserver un canal de communication entre les navigateurs et serveurs, pour les requêtes / réponses pilotées par les événements,
     
  • La communication asynchrone utilisant un "message broker" (négociateur de messages ; ActiveMQ, Kafka, RabbitMQ, Redis...).

 

La communication synchrone est un mécanisme simple, compatible avec le pare-feu. L'inconvénient est qu'elle ne prend pas en charge d'autres modèles, comme le modèle de publication / abonnement. Elle ne permet pas non plus la mise en file d'attente des demandes, ce qui peut ralentir les performances.

Avec les applications synchrones, le client et le serveur doivent être disponibles simultanément et les clients doivent toujours connaître l'hôte et le port du serveur, ce qui n'est pas toujours simple lorsque les services évoluent automatiquement lors d'un déploiement cloud.

 

La communication asynchrone dissocie les services les uns des autres dans le temps, permettant d’envoyer des messages au fil de l'eau, stockés dans une file d'attente, tandis que le système de messagerie prend la responsabilité du message, en le gardant en sécurité jusqu'à ce qu'il puisse être livré au destinataire.

 

Le style de messagerie de publication / abonnement est plus flexible que la mise en file d'attente point à point, car il permet à plusieurs destinataires de s'abonner au flux de messages. Cela permet d'ajouter plus facilement plusieurs micro-services à l'application. 

 

Alternativement, un mécanisme asynchrone utilise un "message broker", cet intermédiaire permet  de diminuer le couplage entre les producteurs et les consommateurs. Le "message broker" met les messages en mémoire tampon jusqu'à ce que le consommateur soit en mesure de les traiter. Ce type de scénario de répartition de la charge permet aux développeurs de créer des applications évolutives et réactives. L'application peut être découpée en threads s'exécutant en arrière-plan, ce qui lui permet de continuer sans attendre la fin d'exécution des threads.

 

Les deux approches présentent des avantages et des inconvénients. La plupart des applications utilisent une combinaison de ces deux approches, adaptée à leurs besoins.

 

Faire face à la complexité


Une architecture de micro-services crée une utilisation supplémentaire des ressources pour de nombreuses opérations. Le nombre de composants augmente et en même temps la complexité. Rationaliser le comportement de chaque composant individuel pourrait être plus simple, mais comprendre le comportement de l'ensemble du système est beaucoup plus difficile.

Le réglage d'un système basé sur des micro-services, qui peut consister en des dizaines de processus, en comptant les "load balancers" et les "messages brokers", nécessite une surveillance et une expertise accrues.

Tests 

Le test d'un système complexe nécessite des suites de tests unitaires et d'intégration, et un cadre de tests qui simule les composants défaillants, la dégradation des performances du service, la résistance et le comportement global du système au cours d'une journée classique.

Ces cadres de tests assurent l'exactitude et la résilience globale du système.

En raison de l'accent mis sur le bon fonctionnement du système lorsqu'un composant tombe en panne, dans une architecture de micro-services, il n'est pas rare de tester un échec en production.

Des tests automatisés, qui forcent un composant à échouer, permettent de tester la capacité du système à reprendre en cas d'erreurs, ainsi que les capacités de surveillance.

 

Surveillance 

En plus des systèmes de surveillance conventionnels, des services bien conçus peuvent fournir des informations supplémentaires sur l'état de santé d'une architecture en utilisant le modèle publication / abonnement.

De cette façon, les informations peuvent être collectées par le service de surveillance, offrant des indicateurs de configuration.

Ces indicateurs peuvent à leur tour être utilisés pour garantir que les services ont la capacité suffisante pour traiter le volume demandé et comparer les performances actuelles par rapport à celles attendues.

Les contrôles personnalisés peuvent être enregistrés pour recueillir des données supplémentaires.

Toutes les informations de collecte peuvent être agrégées et affichées dans le tableau de bord de surveillance.

 

DevOps 

Le défi de maintenir les micro-services opérationnels signifie que vous avez besoin d'expertise en DevOps dans votre équipe de développement.

 

Programmation polyglotte

Très souvent, une architecture micro-services utilise plusieurs langages, Java pour la partie back-end, Python pour l'IA… ce qui présente un défi pour maintenir un tel système hyper-polyglotte.

Des développeurs peuvent déployer, exécuter et optimiser les produits noSQL (MongoDB) et remplacer l'administrateur de base de données (DBA).

Maintenir un pipeline de développeurs et d'architectes techniques qualifiés est aussi critique que la maintenance du pipeline DevOps.

On doit garantir qu’il y a suffisamment de développeurs qualifiés pour maintenir la plate-forme et améliorer la conception du système à mesure que de nouvelles évolutions sont réalisées.

La plupart des organisations ont une liste restreinte de langages de programmation qu'ils utilisent. Cela présente des avantages du point de vue de la réutilisation du code et des compétences.

 

Conception évolutive

L'architecture des micro-services permet une approche de conception évolutive. L'idée centrale est que vous pouvez introduire de nouvelles fonctionnalités dans votre application en changeant un seul micro-service. Il vous suffit de déployer les micro-services que vous avez modifiés. Le changement n'affecte que les consommateurs de ce micro-service et, si l'interface n'a pas changé, tous les consommateurs continuent de fonctionner.

Étant donné que les micro-services sont faiblement couplés, petits et ciblés, et ont un contexte borné, des modifications peuvent y être apportées rapidement et fréquemment avec un risque minimal.

Cette facilité de l'évolutivité d'un micro-service permet d'évoluer rapidement.

Parce qu'un micro-service est petit et généralement conçu, développé et entretenu par une petite équipe « deux pizzas », il est typique de trouver des micro-services entièrement réécrits, par opposition à être mis à niveau ou maintenus. Un compromis courant réside dans la petite taille de votre micro-service.

Cependant, plus chaque micro-service est petit, plus il est probable qu'une mise à jour d'une application classique nécessite plus de micro-services à déployer, augmentant ainsi le risque de dysfonctionnement.

Les principes des micro-services, tels que la déployabilité indépendante et le couplage lâche, doivent être pris en compte pour déterminer quelle fonctionnalité métier devient un micro-service.

 

Conclusion

 

Les micro-services peuvent avoir un impact positif sur votre entreprise.

Ce nouveau paradigme qu'est l'architecture micro-service (MSA) nécessite l'utilisation de modèles de conception pour la fiabilité, exactement comme pour le paradigme objet, où il existe des design patterns pour l'évolutivité, la maintenabilité et la réutilisabilité.

 

 

Rhona Maxwel
@rhona_helena

 

 

"Au calme clair de lune triste et beau,
Qui fait rêver les oiseaux dans les arbres
Et sangloter d'extase les jets d'eau,
Les grands jets d'eau sveltes parmi les marbres."

 

Clair de lune, Paul Verlaine

 

 

En complément :

 

Comment éviter la loi de Conway et faciliter ainsi l’agilité avec l’approche Micro-Services ?

 

Estimation de la complexité d’une Architecture Micro-Services

 

Conseils pour réussir vos micro-services et éviter qu’ils ne se transforment en véritable pensum

 

Inconvénients de l'Architecture Micro-Services

 

L’Architecture Micro-Services expliquée à ma fille

 



11/02/2021
0 Poster un commentaire

A découvrir aussi


Inscrivez-vous au site

Soyez prévenu par email des prochaines mises à jour

Rejoignez les 431 autres membres