Ce guide de référence est le premier d'une série en quatre parties sur la conception, la création et le déploiement de microservices. Cette série décrit les différents éléments d'une architecture de microservices. La série inclut des informations sur les avantages et les inconvénients du modèle d'architecture de microservices, et sur la façon d'appliquer ce modèle.
- Présentation des microservices (le présent document)
- Refactoriser un monolithe en microservices
- Communication entre services dans une configuration à microservices
- Traçage distribué dans une application à microservices
Cette série est destinée aux développeurs et aux architectes d'applications qui conçoivent et mettent en œuvre la migration pour refactoriser une application monolithique en application à microservices.
Applications monolithiques
Une application monolithique est une application logicielle à un seul niveau dans laquelle différents modules sont combinés en un seul programme. Par exemple, si vous créez une application e-commerce, elle doit posséder une architecture modulaire conforme aux principes de programmation orientée objet (OOP). Le schéma suivant illustre un exemple de configuration d'application e-commerce dans lequel l'application est composée de divers modules. Dans une application monolithique, les modules sont définis à l'aide d'une combinaison de constructions de langages de programmation (par exemple, les packages Java) et d'artefacts de compilation (par exemple, les fichiers JAR Java).
Figure 1 : Schéma d'une application e-commerce monolithique avec plusieurs modules utilisant une combinaison de constructions de langages de programmation.
Dans la figure 1, les différents modules de l'application e-commerce correspondent à la logique métier pour la gestion des paiements, de la livraison et des commandes. Tous ces modules sont empaquetés et déployés sous la forme d'un exécutable logique unique. Le format réel dépend du langage et du framework de l'application. Par exemple, de nombreuses applications Java sont empaquetées sous forme de fichiers JAR et déployées sur des serveurs d'applications tels que Tomcat ou Jetty. De même, une application Rails ou Node.js est empaquetée sous forme de hiérarchie de répertoires.
Avantages du monolithe
L'architecture monolithique est une solution classique pour la création d'applications. Voici quelques avantages de l'adoption d'une conception monolithique pour votre application :
- Vous pouvez mettre en œuvre des tests de bout en bout de votre application monolithique à l'aide d'outils tels que Selenium.
- Pour déployer une application monolithique, vous pouvez simplement copier l'application empaquetée sur un serveur.
- Tous les modules d'une application monolithique partagent la mémoire, l'espace de stockage et les ressources. Vous pouvez donc utiliser une solution unique pour les problèmes transversaux tels que la journalisation, la mise en cache et la sécurité.
- L'approche monolithique peut offrir des avantages en termes de performance car les modules peuvent s'appeler directement. En revanche, les microservices nécessitent généralement un appel réseau pour communiquer entre eux.
Défis associés au monolithe
Au fil du temps, les monolithes complexes deviennent de plus en plus difficiles à créer, à déboguer et à interpréter. À un moment donné, les problèmes l'emportent sur les avantages.
- De manière générale, les applications deviennent de plus en plus imposantes au fil du temps. Il peut s'avérer compliqué de mettre en œuvre des modifications dans une application volumineuse et complexe dont les modules sont étroitement couplés. Étant donné que la moindre modification du code affecte l'ensemble du système, les modifications doivent être coordonnées avec une grande prudence. La coordination des modifications ralentit considérablement les processus de développement et de test par rapport aux applications à microservices.
- Il peut être difficile de mettre en œuvre l'intégration et le déploiement continus (CI/CD) avec un monolithe volumineux. Cette complexité est due au fait que vous devez redéployer l'application complète pour mettre à jour une partie de l'application. De plus, des tests manuels approfondis de l'ensemble de l'application sont souvent nécessaires pour rechercher les régressions.
- Les applications monolithiques peuvent être difficiles à faire évoluer lorsque différents modules présentent des exigences de ressources conflictuelles. Par exemple, un module peut mettre en œuvre une logique de traitement d'image nécessitant une utilisation intensive du processeur. Un autre module peut mettre en œuvre une base de données en mémoire. Ces modules étant déployés ensemble, vous devez faire un compromis au niveau du matériel.
- Étant donné que tous les modules s'exécutent dans un même processus, un bug dans un module (par exemple, une fuite de mémoire) peut potentiellement entraîner l'arrêt de l'ensemble du système.
- Les applications monolithiques ajoutent de la complexité lorsque vous souhaitez adopter de nouveaux frameworks et langages. Par exemple, il est coûteux (en termes de temps et d'argent) de réécrire une application entière pour utiliser un nouveau framework, même si ce framework est considérablement meilleur.
Applications basées sur des microservices
Un microservice met généralement en œuvre un ensemble de fonctionnalités distinctes. Chaque microservice est une mini-application qui possède sa propre architecture et sa propre logique métier. Par exemple, certains microservices exposent une API utilisée par d'autres microservices ou par les clients de l'application, comme pour les intégrations tierces avec les passerelles de paiement ou de logistique.
La figure 1 représente une application e-commerce monolithique comprenant plusieurs modules. Le schéma suivant montre une possible décomposition de l'application e-commerce en microservices :
Figure 2. Schéma d'une application e-commerce avec des domaines fonctionnels mis en œuvre par des microservices.
Dans la figure 2, un microservice dédié met en œuvre chaque domaine fonctionnel de l'application e-commerce. Chaque service de backend peut exposer une API, et les services consomment des API fournies par d'autres services. Par exemple, pour afficher les pages Web, les services d'interface utilisateur appellent le service de paiement et d'autres services. Les services peuvent également utiliser la communication asynchrone basée sur des messages. Pour en savoir plus sur la manière dont les services communiquent entre eux, consultez le troisième document de cette série, intitulé Communication entre services dans une configuration à microservices.
Le modèle d'architecture à microservices modifie de manière significative la relation entre l'application et la base de données. Au lieu de partager une base de données unique avec d'autres services, nous recommandons que chaque service dispose de sa propre base de données qui répond le mieux à ses besoins. Lorsque vous disposez d'une base de données pour chaque service, vous vous assurez que le couplage est faible entre les services, car toutes les requêtes de données transitent par l'API du service et non pas directement par la base de données partagée. Le schéma suivant illustre un modèle d'architecture à microservices dans lequel chaque service dispose de sa propre base de données :
Figure 3. Chaque service d'une architecture à microservices possède sa propre base de données.
Dans la figure 3, le service de commande de l'application d'e-commerce fonctionne bien avec une base de données axée sur les documents et dotée de fonctionnalités de recherche en temps réel. Les services de paiement et de livraison reposent sur les garanties solides d'atomicité, de cohérence, d'isolation, de durabilité (ACID) d'une base de données relationnelle.
Avantages des microservices
Le modèle d'architecture à microservices résout le problème de complexité décrit dans la section précédente Défis associés au monolithe. Une architecture à microservices offre les avantages suivants :
- Bien que le fonctionnement final reste inchangé, vous utilisez des microservices pour séparer l'application en fragments ou services gérables. Chaque service a des limites bien définies sous la forme d'une API RPC ou basée sur des messages. Par conséquent, les services individuels peuvent être plus rapides à développer, et plus faciles à comprendre et à gérer.
- Les équipes autonomes peuvent développer indépendamment des services individuels. Vous pouvez organiser les microservices autour de vos problématiques métier plutôt que des fonctionnalités techniques d'un produit. Vous organisez vos équipes autour d'une responsabilité indépendante et unique sur tout le cycle de vie du composant logiciel qui leur est attribué, du développement aux tests en passant par le déploiement, la maintenance et la surveillance.
- Un processus de développement de microservices indépendant permet également aux développeurs d'écrire chaque microservice dans un langage de programmation différent, créant ainsi une application polyglotte. Lorsque vous utilisez le langage le plus efficace pour chaque microservice, vous pouvez développer une application plus rapidement et l'optimiser afin de réduire la complexité du code et d'améliorer les performances et les fonctionnalités.
- Lorsque vous dissociez des fonctionnalités d'un monolithe, les équipes individuelles peuvent publier leur microservice de manière indépendante. Des cycles de publication indépendants permettent d'améliorer la vélocité des équipes et le temps de production des produits.
- L'architecture à microservices vous permet également de faire évoluer chaque service indépendamment. Vous pouvez déployer le nombre exact d'instances nécessaire pour respecter les contraintes de capacité et de disponibilité du service. Vous pouvez également utiliser le matériel qui correspond le mieux aux besoins en ressources de chaque service. Le fait de faire évoluer les services de manière indépendante vous permet d'accroître la disponibilité et la fiabilité de l'ensemble du système.
Voici quelques cas spécifiques dans lesquels il peut être bénéfique de migrer d'une application monolithique vers une architecture à microservices :
- Mettre en œuvre des améliorations d'évolutivité, de facilité de gestion, d'agilité ou de rapidité de livraison.
- Réécrire progressivement une grande application existante dans un langage et avec une pile technologique modernes pour répondre aux nouvelles exigences commerciales.
- Extraire des applications métier transversales ou des services transversaux afin de les réutiliser sur plusieurs canaux. Voici quelques exemples de services que vous pouvez réutiliser : services de paiement, services de connexion, services de chiffrement, services de recherche de vols, services de profil client et services de notification.
- Adopter un langage ou un framework spécialement conçu pour une fonctionnalité spécifique d'un monolithe existant.
Défis associés aux microservices
Les microservices présentent quelques difficultés spécifiques par rapport aux monolithes, notamment les suivantes :
- L'une des principales difficultés des microservices est la complexité résultant du fait que l'application est un système distribué. Les développeurs doivent choisir et mettre en œuvre un mécanisme de communication interservices. Les services doivent également gérer les défaillances partielles et l'indisponibilité des services en amont.
Un autre problème avec les microservices est que vous devez gérer les transactions impliquant plusieurs microservices (on parle également de transactions distribuées). Les opérations commerciales qui mettent à jour plusieurs entités commerciales sont assez courantes et sont généralement appliquées de manière atomique, ce qui signifie que soit toutes les opérations sont appliquées, soit toutes les opérations échouent. Lorsque vous encapsulez plusieurs opérations dans une même transaction de base de données, vous garantissez l'atomicité.
Dans une application basée sur des microservices, les opérations commerciales peuvent être réparties sur différents microservices, ce qui implique de mettre à jour plusieurs bases de données appartenant à différents services. En cas de défaillance, il peut s'avérer très complexe d'assurer le suivi de l'aboutissement des appels aux différents microservices pour effectuer un rollback de l'état. Dans le pire des cas, des incohérences de données entre services sont introduites lorsque le rollback d'état n'est pas appliqué correctement après des défaillances. Pour en savoir plus sur les différentes méthodologies permettant de configurer des transactions distribuées entre services, consultez le troisième document de cette série intitulé Communication entre services dans une configuration à microservices.
Tester complètement des applications basées sur des microservices est plus complexe que de tester une application monolithique. Par exemple, pour tester la fonctionnalité de traitement d'une commande dans un service e-commerce monolithique, vous devez sélectionner des articles, les ajouter à un panier, puis procéder au règlement. Pour tester le même flux dans une architecture basée sur des microservices, plusieurs services doivent s'appeler mutuellement (interface, commande et paiement) pour mettre en œuvre le test.
Le déploiement d'une application basée sur des microservices est plus complexe que le déploiement d'une application monolithique. Une application à microservices est généralement constituée de nombreux services, eux-mêmes exécutés sur plusieurs instances. Vous devez également mettre en œuvre un mécanisme de détection de services permettant à un service de découvrir les emplacements des autres services avec lesquels il doit communiquer.
Une architecture à microservices augmente les coûts opérationnels en raison du plus grand nombre de services à surveiller et pour lesquels émettre des alertes. L'architecture à microservices présente également plus de points de défaillance en raison du plus grand nombre de points de communication de service à service. Une application monolithique peut être déployée sur un petit cluster de serveurs d'applications. Une application basée sur des microservices peut comprendre des dizaines de services distincts pour la compilation, les tests, le déploiement et l'exécution, potentiellement dans plusieurs langages et environnements. Tous ces services doivent être regroupés en cluster pour le basculement et la résilience. Le passage en production d'une application basée sur des microservices nécessite une infrastructure de surveillance et d'exploitation de haute qualité.
La division des services dans une architecture à microservices permet à l'application d'exécuter simultanément un plus grand nombre de fonctions. Toutefois, étant donné que les modules s'exécutent en tant que services isolés, une latence est introduite dans le temps de réponse à cause des appels réseau entre les services.
Toutes les applications ne sont pas suffisamment volumineuses pour être décomposées en microservices. En outre, certaines applications nécessitent une intégration étroite entre les composants, par exemple pour les applications qui doivent traiter des flux de données rapides en temps réel. Toute couche de communication ajoutée entre les services peut ralentir le traitement en temps réel. Anticiper et planifier la communication entre les services peut fournir des informations utiles pour définir clairement les limites des services.
Pour déterminer si l'architecture à microservices est le bon choix pour votre application, tenez compte des points suivants :
- Les bonnes pratiques en matière de microservices nécessitent que chaque service dispose de sa propre base de données. Lorsque vous effectuez une modélisation des données pour votre application, essayez de déterminer si les bases de données par service sont un bon choix pour votre application.
- Lorsque vous mettez en œuvre une architecture à microservices, vous devez instrumenter et surveiller l'environnement afin d'identifier les goulots d'étranglement, de détecter et éviter les défaillances, et de faciliter les diagnostics.
- Dans une architecture à microservices, chaque service a des contrôles d'accès distincts. Pour garantir la sécurité, vous devez sécuriser l'accès à chaque service au sein de l'environnement mais aussi à partir des applications externes qui consomment ses API.
- La communication synchrone entre les services réduit généralement la disponibilité d'une application. Par exemple, si le service de commande d'une application e-commerce appelle de manière synchrone d'autres services en amont et que ces services ne sont pas disponibles, il ne peut pas créer de commande. Par conséquent, nous vous recommandons de mettre en œuvre une communication asynchrone basée sur des messages.
Quand migrer une application monolithique vers des microservices
Si vous exploitez déjà avec succès une application monolithique, l'adoption des microservices est un investissement considérable pour votre équipe. Différentes équipes mettent en œuvre les principes des microservices de différentes manières. Chaque équipe d'ingénieurs obtient des résultats uniques en fonction de la taille des microservices ou du nombre de microservices nécessaire.
Pour déterminer si les microservices sont la meilleure approche pour votre application, commencez par identifier les objectifs commerciaux clés ou les problématiques que vous cherchez à solutionner. Il existe peut-être des méthodes plus simples pour atteindre vos objectifs ou pour résoudre les problèmes que vous avez identifiés. Par exemple, si vous souhaitez faire évoluer votre application plus rapidement, vous trouverez peut-être que l'autoscaling est une solution plus efficace. Si vous trouvez des bugs en production, vous pouvez commencer par mettre en œuvre des tests unitaires et une intégration continue (CI).
Si vous pensez qu'une approche basée sur des microservices est le meilleur moyen d'atteindre vos objectifs, commencez par extraire un service du monolithe pour le développer, le tester et le déployer en production. Pour en savoir plus, consultez le document suivant de cette série, intitulé Refactoriser un monolithe en microservices. Une fois que vous avez extrait un service et qu'il est déployé en production, commencez l'extraction du service suivant en appliquant les apprentissages de chaque cycle précédent.
Le modèle d'architecture à microservices décompose un système en un ensemble de services déployables indépendamment. Lorsque vous développez une application monolithique, vous devez coordonner des équipes de grande envergure, ce qui peut ralentir le développement logiciel. Lorsque vous mettez en œuvre une architecture à microservices, vous permettez à de petites équipes autonomes de travailler en parallèle, ce qui peut accélérer le développement.
Dans le document suivant de la série, Refactoriser un monolithe en microservices, vous découvrirez diverses stratégies de refactorisation d'une application monolithique en microservices.
Étape suivante
- Lisez le document suivant de cette série pour en savoir plus sur les stratégies de refactorisation des applications en microservices.
- Lisez le troisième document de cette série pour en savoir plus sur la communication interservices dans une configuration à microservices.
- Lisez le quatrième et dernier document de cette série pour en savoir plus sur le traçage distribué des requêtes entre microservices.