Architectures micro-services : objectifs, bénéfices et défis - Synthèse

Architectures micro-services : objectifs, bénéfices et défis - Synthèse

Après plusieurs années de développement logiciel et de maintenance, certaines applications d’entreprise s’avèrent laborieuses et trop coûteuses à faire évoluer.

Cela conduit souvent à faire table rase des développements passés, et à les reconstruire à partir de zéro. L’essor des architectures micro-services (micro-services architectures – MSA) répond à cette problématique, et propose des moyens de la résoudre.

Les architectures micro-services permettent de décomposer les systèmes en services indépendants. Elles se distinguent des architectures orientées services (SOA), répandues depuis environ une décennie pour plusieurs raisons:

  • Elles émergent de la recherche d’une agilité pratique et d’une efficacité accrue par des précurseurs du Cloud Computing, tels que Netflix, Amazon, Gilt ou Airbnb.
  • Elles sont naturellement adaptées au Cloud, et se déploient nativement sur les « Platforms as a Service » (PaaS). Il s’agit du type de plateformes sur lesquelles elles se sont développées et déployées historiquement, et elles en exploitent naturellement les bénéfices.
  • Elles ne dépendent aucunement d’une solution d’éditeur logiciel, tel que les Enterprise Service Bus (ESB), et minimisent les besoins d’intégration logicielle (EAI) mettant en œuvre des fonctionnalités sophistiquées (médiation, orchestration de services, etc.).

Objectifs des architectures micro-services

Les évolutions d’une application nécessitent un effort d’adaptation, et le coût qu’elles induisent s’accroît au fur et à mesure que le système se complexifie. L’évolution d’un système a ainsi naturellement tendance à ralentir au fil du temps, et ce d’autant plus vite que « ce qu’il est difficile de changer » s’accumule.

Paradoxalement, une bonne architecture est minimaliste, et permet de changer facilement n’importe lequel de ses composants.

Les principaux objectifs des architectures micro-services sont :

  • de réduire les obstacles au changement,
  • de libérer les développeurs et les opérationnels des contraintes de la complexité,
  • et finalement d’accroître la compétitivité de l’entreprise.

Quelles sont les caractéristiques des architectures micro-services ?

Les architectures micro-services ne font pas actuellement l’objet de standards, ou de définitions bien arrêtées. Cependant, des tendances se sont dégagées au fil du temps, et leurs principales caractéristiques sont maintenant bien identifiées.

Cohésion interne d’un micro-service

Un micro-service est censé bénéficier d’une forte cohésion interne: le périmètre fonctionnel des fonctionnalités qu’il implémente doit être réduit, et essentiellement dédié à une fonctionnalité élémentaire.

Le périmètre fonctionnel d’un micro-service peut être relié à la notion de contexte délimité (ou bounded context) issue de l’approche de conception pilotée par le domaine – Domain Driven Design (ou DDD).

Dans l’acronyme SOLID, désignant 5 principes de conception orientée objet, le « S » fait référence au principe de responsabilité unique (SRP), défini ainsi par Robert Martin: « Gather things that change for the same reson, and separate things that change for different reasons ». Ce principe trouve tout son sens dans un contexte de micro-services. cohesion(1)

Découplage entre les micro-services

Dans une architecture micro-service, le logiciel est décomposé en un ensemble de services hautement indépendants.

Chaque micro-service peut être: déployé indépendamment, conçu et développé indépendamment, testé indépendamment.

En conséquence, chaque micro-service peut évoluer de façon plus indépendante que dans une approche « monolithique ».

Les MSA permettent de répondre d’autant mieux aux objectifs de réactivité et d’adaptabilité au marché des approches Agiles.

couplage

Distribution des micro-services

Afin d’atteindre ce niveau de découplage, chaque micro-service doit être un processus système indépendant, isolé sur une machine distincte ou déployé dans un conteneur.

La communication entre les clients et les micro-services, ou entre les micro-services eux-mêmes, est implémentée par des services web ou un protocole d’échanges de messages avec ou sans modèle d’acteurs.

distributed

Bénéfices clés des architectures micro-services

La réduction du délai de mise en marché ou « time to market »

L’indépendance fonctionnelle et technique des micro-services trouve son pendant dans l’autonomie des équipes en charge sur toutes les phases du cycle de vie (développement, tests, déploiement et exploitation).

Le délai de mise en marché d’évolutions fonctionnelles se voit réduit au délai nécessaire à la mise en œuvre des évolutions du ou des micro-services concernés.

L’agilité technologique

Les micro-services étant indépendants et interopérables, ils ne sont pas contraints à une uniformité technologique. Les choix technologiques peuvent ainsi être adaptés aux besoins spécifiques des équipes et des micro-services.

Par exemple : adaptation de la base de données adaptée au service associé.

polyglot

Cette approche qui conduit à un modèle de persistance polyglotte est pertinente si le modèle de donnée sur lequel le micro-service opère est indépendant du reste de l’application.

La liberté et la flexibilité technologique obtenue permet d’introduire des innovations technologiques progressivement, en limitant le risque encouru: le périmètre impacté est réduit à celui du micro-service concerné.

L’extensibilité

Le découpage en unités fonctionnelles indépendantes permet de répliquer sélectivement les micro-services les plus utilisés, plutôt que de mettre en œuvre une redondance globale de l’application monolithique.

Afin d’y parvenir, il faut prendre en compte la nature bloquante des appels synchrones entre services et potentiellement mettre en œuvre une architecture orientée événement afin de rendre les appels entre services non bloquants et d’augmenter la capacité du système.

La factorisation et la réutilisation des micro-services

La nature distribuée des architectures micro-services permet la composition des fonctionnalités métiers et favorise la réutilisation à condition de créer des modèles de données fortement cohésifs.

Evolutivité

L’amélioration de l’évolutivité est un des principaux buts de l’approche micro-services.

La modernisation d’un micro-service est facilitée par l’étendue de son périmètre fonctionnel et sa complexité relativement faible. Les changements technologiques ne débordent pas du périmètre (que ce soit une réécriture totale ou partielle), et les contraintes d’interopérabilité concernent le respect du protocole de communication existant entre le micro-service et ses clients.

Résilience

La distribution des micro-services en processus systèmes techniquement indépendants permet une meilleure continuité de service lorsqu’une panne survient à condition de mettre en œuvre des techniques de mitigation des pannes (répartition de charge, registres de services dynamiques, circuit breaker…).

 

Défis soulevés par les micro-services

Les avantages liés à l’utilisation de micro-services sont nombreux. Cependant la distribution des micro-services et leur nombre induit une nouvelle forme de complexité.

En effet, les micro-services nécessitent

  • Une approche d’analyse fonctionnelle et de conception en contexte distribué, mettant en évidence des unités fonctionnelles autonomes et cohésives.
  • La prise en compte des contraintes techniques d’intégration de données (méthodes de communication réseau, découplage des modifications de données et transactions « éventuellement cohérentes » ( BASE )…).
  • Une gestion opérationnelle automatisée, notamment en ce qui concerne le déploiement, les tests et le monitoring.
  • Une organisation humaine compatible avec le système réalisé (cf. loi de Conway: “organizations which design systems are constrained to produce designs which are copies of the communication structures of these organizations”).

Intégration des micro-services

Voici quelques-unes des questions liées à l’intégration des services que ce type d’architecture soulève :

  1. La communication entre deux micro-services est-elle de nature synchrone ou asynchrone?
  2. Quel niveau de cohérence de modèles de données doit être assuré entre micro-services?
  3. Quel(s) protocole(s) d’échange faut-il préférer : SOAP, REST, Thrift, RPC, messaging, etc.
  4. Quel format utiliser pour le transport des informations : XML, JSON, binaire…
  5. Comment gérer les cas d’exceptions techniques ?

Au final, ces choix dépendent des fonctionnalités à réaliser, chaque cas d’utilisation induisant des besoins spécifiques.

Les API REST, très flexibles et quasi-universellement supportées, rencontrent un grand succès. Elles semblent devenir un quasi-standard pour l’implémentation des interactions synchrones entre micro-services.

Gestion de données distribuées

Dans le cas d’applications de gestion développées selon un modèle monolithique, l’intégrité des données peut s’appuyer sur les mécanismes transactionnels internes aux bases de données relationnelles :

  • La portée des transactions est démarquée explicitement dans le code ou à l’aide d’annotations.
  • Le gestionnaire de transactions et la base de données garantissent que les opérations effectuées respectent les critères ACID avec divers niveaux d’isolation.

À contrario, dans le cas d’une application bâtie sur des micro-services, les données sont distribuées dans des magasins de données potentiellement hétérogènes.

Les données sont usuellement encapsulées et propres à un micro-service, et accédées indirectement via une API. De plus, les magasins de données peuvent être de natures diversifiées (par exemple : relationnel, clé-valeur non relationnel, plein texte…).

Certaines fonctionnalités peuvent demander des mises à jour de données distribuées, via des appels distants synchrones ou asynchrones. La coordination des modifications, ainsi que l’annulation en cas d’échec, ne sont pas assurées par un gestionnaire de transaction unique, et imposent des changements dans la gestion des modifications de données.

Plusieurs approches peuvent être considérées :

  1. Réunir toutes les opérations indissociables au sein d’un même micro-service de manière à bénéficier des caractéristiques ACID de la base de données sous-jacente.
  2. Garantir la transaction en s’appuyant sur un gestionnaire de transactions distribuées. Cette solution n’est en principe pas scalable et présente des risques pratiques de couplage technologique.
  3. Utiliser une architecture d’intégration “Event driven” en s’appuyant sur un broker de messages. Les mises à jour de données entre micro-services s’appuient alors sur la notion d’événement. Passer à un modèle de transactions « BASE ».
  4. Annuler les opérations qui produisent une incohérence à l’aide d’opérations de « compensation » éventuellement gérées par un administrateur ou par un programme « batch ».

Communications synchrones et asynchrones

Il est bien évidemment possible d’implémenter des micro-services selon un modèle de communication synchrone. Cependant, il est souvent nécessaire d’utiliser une communication asynchrone pour accroître le découplage et augmenter la scalabilité.

L’implémentation d’une architecture micro-services basée sur des communications synchrones peut devenir problématique dès lors que des micro-services invoquent récursivement un grand nombre de « sous-micro-services ».

Gestion des versions

Une architecture micro-services doit gérer les évolutions de versions :

  • Un client doit pouvoir évoluer tout en restant compatible avec un serveur qui n’a pas évolué.
  • Un serveur doit pouvoir évoluer tout en continuant à accepter les requêtes en provenance d’un client qui n’a pas évolué.

Le pattern des « clients tolérants » ou « tolerant readers » favorise cette évolutivité :

“Design the client or service to extract only what is needed, ignore unknown content, and expect variant data structures.”

La gestion de la compatibilité entre client et serveur passe également par les bonnes pratiques suivantes :

  • Eviter l’introduction de modifications ayant un impact sur les interfaces
  • Utiliser la convention de version sémantique : MAJOR.MINOR.PATCH. Cette information de version peut, par exemple, se communiquer dans les en-têtes HTTP d’une requête REST.
  • Mettre en pratique la loi de Postel, ou principe de robustesse côté serveur : “Be conservative in what you do, be liberal in what you accept from others.”

La technique du Consumer Driven Contract permet de détecter l’introduction d’une incompatibilité entre service et clients.

Le client réalise ainsi des tests d’intégration qui expriment ses attentes vis-à-vis de l’API de services. Ces tests sont incorporés dans la collection de tests du service lié.

servives API

Le « Consumer Driven Contract » est supporté par le framework Pact.

 

Tests des micro-services

Compte tenu du nombre de services déployés dans une architecture constituée de micro-services, il peut être difficile de recréer un environnement de production complet incluant le micro-service à tester, les micro-services liés ainsi que les n sources données liées.

De plus, les micro-services sont fréquemment intégrés via des communications asynchrones plus complexes à tester que les interactions locales entre composants.

Dans un tel contexte, il est difficile d’être certain que les tests couvrent effectivement toutes les subtilités de l’environnement de production.

Il est nécessaire de compléter les tests par un effort de monitoring visant à déceler rapidement des anomalies de production.

 

Gestion des déploiements

Automatisation du développement et du déploiement

La mise en œuvre d’une architecture micro-services multiplie le nombre de livrables à déployer. La gestion des livrables et des environnements devient une tâche plus complexe qui doit être intégralement automatisée.

Cette automatisation passe par la maîtrise de la chaîne de production logicielle, des tests et des déploiements. C’est objet de l’approche de « Production Continue » ou « Continuous delivery » qui inclut notamment :

  • La gestion de configuration
  • La construction des livrables
  • La virtualisation des environnements et le « provisionning » des infrastructures
  • L’automatisation des modifications de schéma
  • L’automatisation des tests
  • L’automatisation des déploiements
  • Le suivi de l’ensemble de la chaîne

La ligne de production logicielle doit être apte à gérer les incréments de version automatisés mais aussi les retours en arrière.

Une large palette d’outils existe pour y parvenir, dont le choix dépend du langage, du système et des technologies utilisés par le projet.

Technologies de virtualisation d’environnements

De nombreuses technologies très prometteuses ont récemment vu le jour dans le domaine de la virtualisation d’environnement.

Docker est un outil essentiel dans cet écosystème car il permet d’isoler efficacement les micro-services, de fiabiliser la gestion de l’environnement d’exécution des applications et de faciliter les tests et le déploiement.

Les autres technologies (Kubernetes, CoreOS, MesOS…) répondent à des besoins qui apparaissent au fur et à mesure que le nombre de services et la charge augmentent.

Monitoring des micro-services

Dans un contexte de micro-services, le monitoring a plusieurs objectifs :

  • Surveiller le bon fonctionnement de l’infrastructure ;
  • Surveiller le fonctionnement des appels entre micro-services ;
  • Surveiller le respect du niveau de service ;
  • Cartographier dynamiquement la répartition des micro-services sur l’infrastructure et les versions de services déployées.

Le monitoring de l’infrastructure, au sens large, peut s’implémenter en combinant Kibana, ElasticSearch, LogStash :

  • Kibana: Plateforme de visualisation graphique de données, que nous spécialisons dans notre cas particulier sur les logs systèmes (ou tout autres logs pertinents).
  • ElasticSearch: Moteur de recherche distribué accessible via REST/JSon.
  • Logstash: Outil d’agrégation de données ou de logs, extensible à des formats de logs personnalisés sur mesure.

Splunk est une solution commerciale concurrente.

Cas d’application

L’opportunité de mise en œuvre d’une architecture micro-services nécessite une analyse et une réflexion approfondie. C’est en effet la source d’une complexité opérationnelle accrue qui peut se justifier par l’efficacité que l’approche induit sur la durée.

Voici quelques exemples qui justifient l’adoption de ce style d’architecture :

  • La taille de l’équipe qui réalise le système est trop importante pour collaborer efficacement sur un monolithe;
  • Les métiers concernés doivent impérativement évoluer indépendamment ;
  • L’extensibilité (ou « scalabilité ») du système est primordiale et concerne certains services applicatifs plus fortement sollicités ;
  • Le système monolithique équivalent devient trop complexe pour continuer à évoluer raisonnablement vite.

 

Auteurs :

Eric Manuguerra
Directeur Technique, SQLI Suisse

Guillaume Yziquel
Développeur Java/Angular JS, SQLI Suisse

 

Eric Manuguerra

Directeur Technique, SQLI Suisse

0 commentaires

votre commentaire

Se joindre à la discussion ?
Vous êtes libre de contribuer !

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Inscription newsletter

Ne manquez plus nos derniers articles !