Introduction aux microservices

De Reservoircode
Aller à : navigation, rechercher

Monolithes et Microservices

Les architectures microservices sont une alternative sérieuse aux architectures monolithiques classiques, qui ont été longtemps plébiscitées par les développeurs. En effet, dans une architecture logicielle web ou client/serveur, les applications sont structurées autour de briques élémentaires comme le module web (HTML, AngularJS, React, JQuery...), le module backend (Java, .Net, Tomcat, Jersey, Spring MVC...) et le module base de données/documents (MongoDB, Postgres, Elastic...). La majeure partie des opérations sont alors exécutées par la couche service/repository, dont le rôle est d'opérer le code métier. On trouvera par exemple, le calcul d'une opération complexe, l'agrégation d'un flux partenaire, les appels à la base de données, l'indexation d'une entité, etc... D'un point de vue logiciel pur, ces monolithes ont l'avantage d'être simple à mettre en oeuvre et à maintenir à court ou à moyen terme. Mais dès lors, que l'ont souhaite coupler ces applicatifs à des services tierces (intra ou inter-entreprise), ou que l'on souhaite faire évoluer une partie de l'application ou que l'on désire faire passer à l'échelle un service particulier, il faut composer avec l'application dans son entièreté et ce, afin d'éviter des régressions dans une autre région de l'application. Quand il est correctement développé, un monolithe est un ensemble de code maîtrisé, qui à une période donnée, répond à un besoin fonctionnel précis dans une système d'information connu. Cette équation à quatre inconnues a tendance à se complexifier au fil du temps, car reposant sur un écosystème certes riches, mais d'une effroyable complexité. Sur le plan de l'équipe : qui a développé l'application ? Quels ont été les moyens mis en oeuvre pour réaliser la première version ? L'équipe d'origine est-t-elle encore présente ? Sur le plan technologique ensuite : quelles étaient les techniques en vogue au démarrage des développements ? Sont-elles encore d'actualités ? Est-ce encore possible de recruter des développeurs sur ses technologies ? Puis au niveau du périmètre : dans quelle mesure devra-t-on faire évoluer l'applicatif ? Quelle audience l'application devra-t-elle supporter dans x mois, années ? Et pour terminer sur le plan : écosystème : quelles seront les interactions entre les services du SI dans un futur proche ? L'application devra-t-elle s'ouvrir plus largement à l'extérieure ?

Ce sont d'autant de questions, qui portent à s'interroger sur l'architecture de son application dès sa phase de design ou encore mieux lors de son passage en maintenance applicative et ce, si l'on souhaite pérenniser son usage et capitaliser sur l'investissement initial.

Architecture des Microservices

Modularisation

L'approche microservice induit une modularisation complète et naturelle de l'application. En effet, les services se doivent d'être isolés des uns des autres et limitent les dépendances. Idéalement, il faut s'imaginer son logiciel comme un ensemble de briques Lego(TM), qui interagissent entres elles afin de composer un tout, qui une fois assemblées, définissent l'application. Intuitivement, on se rend vite compte, que ce découplage permet plus de flexibilité dans les choix d'architectures ou d'implémentations. Par ailleurs, il est plus aisé de remplacer ou de faire évoluer un microservice sans mettre en péril le reste du code, qu'un service imbriqué sur plusieurs couche de l'application.

Liaisons entres services

Inévitablement, les microservices ont l'obligation d'échanger de l'information ou tout autre types d’événements pendant leurs cycles d'activités. Le pattern idéal est de proposer des endpoints dédiés, qui feront office de point d'entrées ou de sorties. Pour ce faire, il existe plusieurs moyens de communication suivant l'usage voulu. On préférera par exemple REST pour sa simplicité de mise en oeuvre et son côté universel, GRPC pour son côté performant ou bien une solution maison, qui fera office de canal de communication. Dans cette façon d'appréhender les échange entre microservices, il faudra exclure les architectures centralisées, comme les bus d'entreprises dans lesquels se trouvent parfois une myriade de codes métiers.

Décentraliser l'accès à la base de données

Chaque microservice a des besoins spécifiques en terme de gestion des données. Ainsi, suivant le service fournit, une équipe de développement aura la liberté de s'appuyer sur un type de base de données, qui répondra au mieux à ses besoins. Cette approche engendre un degré de liberté, qui n'était pas envisageable dans un monolithe où chaque service de l'application doit se conformer à un schéma, une technologie ou une architecture de base de données. Il est donc courant d'avoir des données partagées, complémentaires, voir dupliquer si nécessaires et la tache qu'incombe aux développeurs est de garder une certaine cohérence dans la donnée pour répondre le plus efficacement aux besoins. Donc, actons qu'un microservice pourra par exemple utiliser une base de données de type graphe si son métier l'exige, alors qu'un autre microservice pourra s'appuyer sur une base de données de type relationnel pour plus d'efficacité si c'est besoin est.

Renforcer la robustesse de son application

Composer avec l'échec dans une architecture microservice devient un mal nécessaire. En effet, de part sa nature décentralisé, le microservice doit être faire l'objet de toute l'attention des équipes de développements. Celle-ci doivent prévoir et traiter les erreurs dès les premières phases de développement et jusqu'à l'exploitation du service en production. Grâce à cette approche, les développeurs prendront en compte assez rapidement qu'un appel externe ne peut aboutir pour plusieurs raisons, comme par exemple, une faiblesse dans le réseau, une montée de version en cours, ou encore à un simple bug dans le service appelé. Suivant l'usage, un microservice devra mettre en place des stratégies de rejeu, de coupe circuit, de monitoring et cela en faisant en sorte que l'utilisateur ne ressente pas d'instabilité. Il existe un modèle de stress test intéressant, qui met en lumière assez rapidement les faiblesses d'un système, c'est le Chaos Monkey.

Faciliter le déploiement des services

En terme de gestion de déploiement, l'architecture microservice implique de mobiliser toutes les équipes concernées et d'avoir une infrastructure capable de mettre en environnement, les composants avec le moins de latence possible. Pour ce faire, il existe des outils prêts à l'emploi, qui sont à la lisière des équipes Infrastructure et Développement et qui permettent construire, déployer tout en testant vite et bien. On parlera d'outils comme Docker, qui permet par exemple de développer, tester et déployer dans des contextes logiciels protégés et proche de l'environnement cible. D'expérience, on constate assez régulièrement que la mise en cluster des applicatifs pour les équipes de QA ou Perf. est une étape périlleuse. En effet, ces mises en environnements arrivent trop fréquemment à la fin du projet et ont tendances à emboliser l'équipe d'Infrastructure, qui dans les faits gère les demandes d'évolutions des autres applicatifs, la MCO de toute la plateforme, la supervision du data center, etc... Il faut donc s’appuyer sur une plateforme dédiée dont le rôle est de rendre cette phase simple. Un cloud provider comme par exemple Google Cloup Platform ou Microsoft Azure proposent une interface de gestion riche et complète permettant de déployer rapidement ses services. On pourra s'appuyer sur des outils d'orchestrations comme Kubernetes, Mesos ou Docker Swarm, pour gérer le cycle de vie de ses microservices.

Avantages et inconvénients des architectures Microservices

Un tel changement de paradigme implique d'être particulièrement vigilant sur certains points d'architectures et la souplesse des microservices est bien souvent au détriment de la simplicité d'implémentation, mais ces nouvelles architectures viennent avec leurs lots d'innovations.

En effet, d'un point de vue développeurs c'est un pari ambitieux, car il faut systématiquement envisager un microservice comme un tier et plus un composant applicatif. Il faut donc prendre en compte, la notion d'aléas réseaux. Heureusement, il existe des patterns d'architectures pouvant aider les développeurs à composer avec ces indisponibilités. De plus, si vous souhaitez encore améliorer les performances de votre application, vous serez probablement obligés de revoir certaines implémentations, afin de les rendre asynchrones. Ce genre d'approche est intéressante d'un point de vue performance, quoiqu'un peu plus complexe à mettre en oeuvre, elle a l'avantage de proposer une expérience utilisateur moderne. Un autre avantage à utiliser les microservices est aussi du côté refactoring (correction de bug, évolution fonctionnelle et technique), qui est un point clé dans la maintenance d'une application. Dans une approche monolithique, il est plus aisé de remodeler le code, car les dépendances étant plus visibles de l'IDE, à l'inverse dans le cadre des microservices, il est moins facile de mesurer une évolution de code. Cela implique donc de renforcer son harnais de tests (tests unitaires, intégration et fonctionnel) et également sa CI/CD.

D'un point de vue Infrastructure, l'approche microservices permet d'avoir une vision plus précise des composants applicatifs déployés, car plus finement identifiables. Cela améliore grandement le troubeshooting des applications lors de la MCO. C'est également un challenge pour elle, car il faudra probablement upgrader ses infrastructures serveurs et réseau et éventuellement repenser ses système de déploiements.

Ainsi, on peut conclure que les microservices facilitent la haute disponibilité et la robustesse applicative. Bien qu'il soit possible d'avoir des applications monolithiques, qui soient à la fois robustes et évolutives, il est tout de même plus simple de faire passer à l'échelle un microservice, dont le code métier sera à la fois circonscrit à un domaine dédié, léger à maintenir et plus aisément testable. Par ailleurs, les architectures microservices permettent une résilience accrue de l'application, car si malheureusement un service est amené à "tomber", l'application peut continuer à fournir ses autres services. L'inverse étant souvent faux dans une approche monolithique, où lorsque un service tombe, il peut malgré lui faire mettre en échec le reste de l'application.