Les microservices, c’est quoi ?

Évolution naturelle des Architectures orientées services (SOA) à la suite des évolutions technologiques et méthodologiques de ces dernières années, les micro services tendent à devenir la norme de développement pour un large spectre du Système d’Information. Cet article vise à synthétiser les concepts sous-tendus par cette Architecture et à donner des pistes de mise en œuvre.

La version courte

Pour définir le concept en une phrase, on peut dire que c’est un modèle d’Architecture permettant de garantir qualité, scalabilité et Agilité du SI en isolant des domaines « fonctionnels » et en les rendant autonomes.

À travers cette définition, on retrouve, de manière transverse, deux sujets connexes aux micro services, qui sont les environnements conteneurisés et la méthodologie de mise en œuvre DevOps. Automatisation, tests et qualités sont en effet les maîtres mots d’une Architecture micro services pérenne.

Dans quels cas mettre en œuvre des microservices ? Pour quels résultats ?

L’apport couramment constaté lors de la mise en œuvre d’une Architecture micro services est le gain en termes de time to delivery. Sur le principe du « diviser pour mieux régner », l’évolution d’un micro service nécessite un effort moindre. Des services applicatifs autonomes et de taille réduite sont naturellement plus simples à prendre en main pour les développeurs, plus simples à faire évoluer techniquement, et plus facile à tester.

Autre élément important : qui dit service plus petit et plus simple, dit amélioration des temps de démarrage et donc des possibilités de scalabilité automatique.
Avec des services plus évolutifs et s’adaptant mieux à la charge, l’Architecture micro services est surtout bénéfique aux services à haute valeur concurrentielle et à ceux recevant le plus de charge de travail, qui sont bien souvent les mêmes.

Un découpage fonctionnel clair…

Un microservice est avant tout défini par son périmètre fonctionnel. Pour résumer, on va chercher à construire un ensemble fonctionnel à la fois minimaliste – pour garder le service le plus simple possible – et cohérent pour garantir son autonomie. La définition de chaque service est donc la recherche au cas par cas du meilleur compromis entre maintenabilité et simplicité de l’Architecture. Il n’y a pas, sur ce point, de méthode miracle et la recherche de la meilleure granularité du service repose sur l’expérience de l’équipe de mise en œuvre et sa maitrise du fonctionnel.

Par exemple, ajouter un contrôle de validité sur la création d’un contrat se fera sans doute en faisant évoluer le microservice existant, quand l’implémentation d’un système complexe de détection de fraude sur ce même type de contrat se fera surement par la création d’un microservice dédié à cette problématique.

Pour guider efficacement les équipes de réalisation, les architectes fonctionnels utilisent généralement la méthode Domain Driven Design (DDD). Cette méthode est utilisée pour cartographier les périmètres applicatifs, les domaines fonctionnels, et de les raffiner successivement, jusqu’à arriver à la granularité idéale qui permet la réutilisabilité des services et garantissant leur évolutivité.

… et une autonomie technique pour plus d’évolutivité

Un microservice possède son propre cycle de vie. Loin des principes de synchronisation des déploiements des composants et des planifications long terme des mises à jour d’une Architecture classique, chacun des services peut être déployé sans tenir compte de la présence de ses dépendances techniques… car il n’existe pas de telle dépendance !

Les points de dépendances habituels entre services sont : la consommation de donnée (accès à une même base de données), la consommation d’APIs et la consommation d’événements.
Pour commencer, chaque microservice est propriétaire de ses données, c’est-à-dire qu’il possède sa propre base de données. Surtout, il a un accès exclusif à cette base, que ce soit en lecture ou en écriture. Pourquoi ? Parce qu’une évolution majeure sur le stockage des données entrainerait une évolution de plusieurs services en même temps, et parce que ces services dépendraient tous les deux de la bonne disponibilité de la base de données, pouvant entrainer des disfonctionnements en cascade.
Il n’y a donc aucune adhérence possible entre les services autour de l’accès aux données.

Deuxièmement, il est fortement recommandé de réduire au maximum les appels synchrones (appels d’APIs) entre services. En effet, un appel synchrone dépend à la fois de la compatibilité entre le consommateur du service et celui qui l’expose, mais aussi et surtout de la disponibilité de ce service. Supposons que le consommateur ait besoin d’un champ de l’API apparu en v1.2 de celle-ci : il ne sera pas possible de mettre notre consommateur en production tant que l’API v1.2 n’est pas elle-même en production. Plus fréquemment, si notre service subit des lenteurs ou une indisponibilité, ce sont tous ces consommateurs qui deviennent également inutilisables.

En conclusion, seule une interaction entre services basée sur des événements est à même de garantir à la fois l’autonomie des services et leur capacité de communication. Sur le principe du publish/subscribe, il est possible de mettre en production producteur ou consommateur d’événement, sans tenir compte de la présence des uns ou des autres. C’est le bus d’événement qui sera en charge de faire transiter les messages selon leur disponibilité. Reste, pour finir, à porter une attention toute particulière à la compatibilité ascendante du format de ces messages

Comment les micro services sont-ils validés et déployés ?

Les micro services ont leur propre pipeline de déploiement, idéalement déclenché automatiquement lorsqu’un développeur fini sa tâche courante (merge/pull request sur l’environnement GIT). Ce pipeline entraine successivement le build, les tests unitaires, le packaging et le déploiement sur chacun des environnements de validation. C’est le concept de déploiement continu.

Ainsi chaque évolution ou correction d’anomalie peut être poussée en production indépendamment des autres microservices, éliminant le concept de mise à jour majeure du SI. Chacune de ces mini mises en production devient un non-événement ; minimisant l’impact d’une éventuelle régression, d’autant que cette régression pourra être rapidement détectée et corrigée (ou son déploiement annulé) grâce au pipeline de déploiement continu.
La validation automatique des microservices revêt donc une importance forte et comprend en particulier, au-delà des simples tests unitaires :

  • Le test du microservice seul, dont toutes les interactions avec l’extérieur sont simulées par des bouchons ou mocks. On valide ici que le microservice répond aux contrats de services définis en amont, à la fois au niveau de ses APIs, mais aussi de ses événements. On réduit donc fortement les risques de régression lors de l’intégration du microservice dans son écosystème.
  • Le test de tous les microservices rassemblés dans un même environnement de validation. L’objectif est de valider les interactions entre eux, et de simuler des cas d’utilisation complets en partant des actions des utilisateurs finaux.
  • Les tests de charge et de robustesse. Exécutés quotidiennement, ils garantissent la stabilité et les performances du système.

Techniquement, comment implémente-on les microservices ?

Le plus important dans l’implémentation des microservices, c’est de faire simple. Il faut déléguer le maximum d’exigences non fonctionnelles à l’infrastructure logicielle (conteneurs, Services Mesh, bus d’événements, etc…) pour réduire les risques futurs de dettes techniques, et garantir un fonctionnement homogène des services.
Pour cela, on se doit de respecter les règles des 12 factors (https://12factor.net/fr/ ). Les microservices sont idéalement sans état (stateless), avec un démarrage rapide ; ils écrivent toutes leurs traces sur la sortie standard, et sont capables d’informer la plateforme où ils sont déployés de leur état de fonctionnement.

L’infrastructure logicielle est donc responsable de garantir automatiquement leur bon fonctionnement – ce qui va du simple redémarrage en cas de crash, au scalling automatique selon la consommation du service – mais également de remonter aux développeurs les logs et le monitoring des services.
De la même manière, la sécurisation des APIs est déléguée à la couche d’exposition (API Management et Service Mesh, associés à un serveur d’autorisation), fournissant en entrée du service de quoi identifier le consommateur, réduisant l’effort de sécurité à la simple validation des règles fonctionnelles (par exemple : cet utilisateur a-t-il le droit de lire cette donnée ?).

La vue d’ensemble de la plateforme microservices

Une Architecture microservices, c’est donc la conjonction :

  • D’un découpage fonctionnel cohérent et global, basé sur la méthodologie Domain Driven Design
  • De composants autonomes et simples : les microservices
  • D’une infrastructure logicielle complète, dans le Cloud ou On-premise, à laquelle un maximum de requirements techniques sont délégués pour permettre une simplification technique des microservices
  • D’une méthodologie DevOps alliant automatisation et qualité logicielle, notamment axée sur les tests des microservices, permettant de tendre vers le déploiement continu.

C’est cet ensemble de bonnes pratiques qui permet d’obtenir un SI Agile et robuste, tout en obtenant une réactivité optimale pour la mise à disposition de nouvelles fonctionnalités utiles au business.

Auteur : onepoint

beyond the obvious