Points Clés
- La communication entre les services et le modèle de gouvernance sont des éléments critiques d’une architecture microservices. Des patterns comme l’orchestration et le Service Mesh peuvent aider à surmonter les difficultés que nous rencontrons dans les systèmes distribués.
- Certains éléments comme le développement en silos et les interactions complexes entre les services (architecture "Etoile de la Mort") peuvent mener à des performances dégradées en production, ainsi qu'à des problèmes de sécurité sur les contrôles d'accès et un manque de monitoring global du système.
- L'orchestration de services aide à relever ces challenges, mais elle a aussi des inconvénients comme l'absence de stratégie décentralisée.
- Les solutions basées sur le Service mesh présentent différents avantages par rapport à l'architecture microservices traditionnelle, notamment autour de la connectivité, fiabilité, sécurité et de l’observabilité.
La première partie de cette série d'articles évoque l'évolution de l'architecture et les patterns stratégiques d'architecture pour les tendances technologiques comme les microservices, le serverless et la conteneurisation. Les architectures s'appuyant sur des principes comme le couplage faible, l'extensibilité, et la programmation par interfaces seront plus résilients lors de changements majeurs de plateforme technologique. Les solutions bien conçues résistent au temps en isolant la logique métier des composants technologiques, ceux-ci devenant obsolètes tôt ou tard.
La seconde partie décrit le temps nécessaire à la stabilisation de l'architecture et certains anti-patterns comme le monolithe distribué et l'architecture "Etoile de la Mort". Il démontre le besoin d'équilibrer l'architecture et la stabilité technologique.
Cette troisième partie étudie l'importance des interactions entre les services dans une architecture microservices, les difficultés récurrentes des systèmes distribués, et comment des patterns d'architecture modernes comme l’orchestration de services et le Service Mesh peuvent y répondre.
Une architecture microservices présente plusieurs avantages mais apporte son lot de difficultés. Nous allons étudier ces difficultés comme des opportunités pour l'architecture, et explorer comment les surmonter grâce à l'orchestration de services.
Les design patterns décrits dans cet article sont applicables sur les plateformes cloud comme AWS, VMware Tanzu Application Service (anciennement Pivotal Cloud Foundry), ou Kubernetes. Mais vous pouvez également les utiliser dans une infrastructure traditionnelle.
Kubernetes est devenu la plateforme cloud standard dans beaucoup d'organisations. Une solution de Service mesh est un excellent moyen d'exploiter au maximum les avantages de Kubernetes dans le cloud, que vous utilisiez déjà Kubernetes ou que vous envisagiez de l'utiliser plus tard. Cet article démontre comment un Service mesh peut aider à gérer les services déployés en production.
[La logique métier doit être isolée des technologies. Dans le cas contraire, cela conduira à des applications métier critiques incapables de tirer parti des avancées technologiques, tout en étant exposées aux vulnérabilités et aux inefficacités des anciennes technologies]
Commençons avec les difficultés que nous rencontrons en tant que développeurs ou architectes dans les architectures d'applications distribuées.
Défi n°1 : Développement en silos
L'exemple ci-dessous illustre les défis classiques d'une architecture de système distribué, avant que les architectures orientées services et les plateformes Cloud émergent
Figure 1. Exemple de développement en silos
Il y a deux applications clientes (App 1 et App2) et trois services métier (Service 1, Service 2 et Service 3).
Dans cet exemple, App 1 communique avec le Service 1 et le Service 2. App 2 ne communique qu'avec le Service 2. Le Service 2 communique également avec le Service 3.
Examinons App 1 et regardons ce qui se passe à l'intérieur de l’application.
C'est un exemple de la façon dont les applications étaient généralement développées et déployées avant l'arrivée des microservices, et cette conception est utilisée dans certaines applications encore aujourd'hui.
Figure 2. Fonctionnalités d’une application et d’un service sous le capot
Cet exemple d'application comprend la logique métier critique mélangée à des tâches de logique non métier, toutes embarquées dans le code de l'application :
- Non fonctionnelles : authentification, autorisation, notification, etc.
- Tâches communes à la plateforme : routage des services, découverte de services, retry/circuit-breaker, traçage, etc.
Les exigences non fonctionnelles sont généralement codées en dur dans l'application et imbriquées dans la logique métier, bien qu'il s'agisse de sujets communs à plusieurs applications.
Cet exemple montre la quantité de code purement métier qu'une application typique contient, par rapport aux tâches non fonctionnelles qui devraient être gérées en dehors de l'application et exploitées par de multiples applications.
Seule une fonctionnalité sur huit gère une logique spécifique à l'application. Les sept autres fonctionnalités ne devraient pas être couplées à la logique de l'application.
[Les dirigeants doivent piloter des KPIs et des artéfacts d'architecture pour analyser dans quelle mesure la logique métier spécifique des applications est mélangée avec les tâches de plateforme et les fonctionnalités communes à de multiples applications]
Examiner d'autres applications confirmera vraisemblablement la même tendance.
Un examen des Services 1, 2 et 3 révèle un schéma similaire. Chaque service a sa propre logique métier mélangée aux mêmes tâches non fonctionnelles que nous avons vues précédemment, qui ne devraient pas faire partie du service. Nous voyons des services non fonctionnels courants tels que l'authentification, l'autorisation, les notifications client, etc. Il existe également des services de plateforme communs à toutes les applications métier hébergées sur la plateforme cloud, comme le routage, la découverte de services et le monitoring et le traçage des services.
Notre exemple simple d'un système distribué avec deux applications et trois services ne semble pas si simple lorsque nous examinons chacun de ces composants, comme montré ci-dessous.
Figure 3. Exemple d’un système distribué avec des fonctionnalités communes dupliquées dans chaque application et service
Toutes les applications et services embarquent tout le code non fonctionnel. Il y a beaucoup d'inconvénients avec ce type de conception.
Il y a beaucoup d'implémentations dupliquées et de prolifération des mêmes fonctionnalités dans chaque application et service, entraînant un développement plus long (time to market) et des coûts de maintenance exponentiellement plus élevés.
Avec toutes ces fonctionnalités communes intégrées dans chaque application et service, toutes sont fortement couplées à des technologies et des frameworks spécifiques utilisés pour ces fonctionnalités, par exemple Spring Cloud Gateway pour le routage et Zipkin ou Jaeger pour le traçage. Toute mise à jour des technologies sous-jacentes nécessitera la modification, la reconstruction et le redéploiement de chaque application et service, ce qui entraînera des indisponibilités et des pannes pour les utilisateurs.
[Les architectures qui mélangent la logique métier spécifique des applications avec les fonctionnalités communes présentent un time to market et des coûts de maintenance exponentiellement plus élevés que les solutions cloud native bien architecturées]
En raison de ces difficultés, les systèmes distribués deviennent complexes. Ces applications doivent être repensées et refondues pour éviter le développement en silos et la prolifération de solutions ponctuelles.
À mesure que le réseau devient plus stable et fiable, les appels «intra-process», comme sur la figure 3, peuvent commencer à évoluer vers une communication via le réseau.
Ces systèmes complexes doivent être repensés pour tirer parti de fonctionnalités communes sans devoir embarquer ces fonctionnalités dans chaque application et service, grâce au design pattern «Services communs».
Services communs
La solution pour éviter le code embarqué dans chaque application est d'encapsuler chacune de ces fonctionnalités dans un service dédié, et d'héberger le service sur un serveur central (dans le cas de machines virtuelles) ou des conteneurs dans le cloud.
Figure 4. Fonctionnalités communes encapsulées dans des microservices indépendants
Les applications clientes appelleraient ces services lorsqu’elles doivent exécuter une fonctionnalité commune. Comme nous pouvons le voir sur la figure 4, la logique commune n'est plus intégrée dans chaque application ou service métier.
Les services communs doivent être stateless, et dans l’idéal ils sont développés ou refondus suivant les pratiques Twelve-Factor App afin qu’ils puissent apporter de la valeur en étant réutilisés dans les applications clientes.
Nous pouvons utiliser des frameworks open source pour développer ces services communs, comme Spring Boot et Spring Cloud pour les applications Java et Asp.Net Core middleware pour les applications basées sur .NET. Étant donné que ces services sont idéalement basés sur Twelve-Factor App, il est plus facile de déployer les applications sur n'importe quelle plateforme cloud. Il est également plus facile de les gérer et de les monitorer en production.
Il y a plusieurs avantages à cette architecture :
- Développement et livraisons plus rapides, ayant pour conséquence un time to market plus court.
- Les déploiements sont isolés des autres applications et services, ce qui réduit la dépendance entre les composants.
- Scalabilité au niveau de chaque service.
- Conformité automatique aux normes de sécurité et aux standards techniques.
- Une maintenance plus petite, plus rapide et plus simple élimine le besoin de redéployer chaque application et service lors d’une mise à jour d’une fonctionnalité commune.
- Moins de dette technique sur le long terme.
Ces services communs peuvent être hébergés sur une plateforme cloud (comme AWS, Azure, Kubernetes ou VMWare Tanzu Application Service, anciennement connu sous le nom de Pivotal Cloud Foundry) qui fournit une bonne prise en charge des capacités comme l’auto-scaling, le monitoring et la facilité de déploiement.
Toutefois, les services communs ne sont qu’une architecture de transition sur la voie Cloud Native, pas l’objectif. Même si les architectures basées sur des microservices communs offrent plusieurs avantages, elles présentent également de nouveaux défis, notamment des interactions de services fortement couplées et le manque de règles centralisées en termes de routage, découverte de services et circuit-breaker.
Nous examinerons ces nouveaux défis plus loin dans l'article.
Avec l’adoption et l’expansion des microservices dans les organisations, la communication entre les applications clientes et les services métier ainsi que les interactions entre les services deviennent critiques. Ne pas adresser correctement cette communication complexe peut entraîner une mauvaise performance des services et des ruptures de disponibilité du système.
Examinons maintenant plus en détail les problématiques de communication entre les applications et les services.
Défi n°2 : Communication app/service et service/service
Au fur et à mesure que nous découpons les grosses applications en services plus fins, le nombre total de composants déployés augmente, ce qui rend l'interaction entre ces composants de plus en plus complexe. La figure 5 illustre cette complexité.
Figure 5. Problématique de communication Application/Service
Même avec les fonctionnalités communes extraites de chaque application cliente et déployées en tant que services dédiés, les interdépendances des services et comment ils sont autorisés à communiquer constituent une menace majeure sur l’architecture.
Étendons l'exemple précédent pour inclure quelques services métier supplémentaires et examinons les effets sur la communication des services.
Comme nous pouvons le voir dans la communication de service à service sur la figure 5, il existe toujours un couplage fort entre les différents services. Il est également difficile de déterminer quel service rencontre des problèmes lorsque l'ensemble du système ralentit ou subit une panne.
On pourrait se demander si les autres sociétés qui ont adopté les microservices et ce modèle d’interaction complexe ont traversé les mêmes épreuves. C’est le cas. Des entreprises comme Netflix, Amazon et Twitter ont rencontré les mêmes défis lorsqu'une application ou un service pouvait appeler un autre service sans un modèle efficace de communication ou de processus de gouvernance. Comme évoqué dans la deuxième partie de cette série, cette difficulté est si répandue dans l’industrie qu’elle a donné naissance à un anti-pattern nommé "architecture Etoile de la Mort."
[Le manque de gouvernance des services conduit à l’anti-pattern «architecture Etoile de la Mort»]
Ces entreprises ont relevé ce défi d’architecture grâce à l’orchestration de services.
Orchestration de services
Figure 6. L'orchestration des services améliore l'interaction entre les services
Dans le modèle d'orchestration de la Figure 6, nous gérons toujours les services communs en dehors des applications clientes comme dans l'architecture précédente, avec leurs propres déploiements, cycles de vie et besoins de scalabilité.
L'amélioration principale est que nous avons déplacé le service de routage pour qu'il soit en frontal de tous les services communs.
Les applications clientes doivent appeler uniquement le service de routage. Selon le cas d'utilisation et le contexte de la requête provenant des applications clientes, le service de routage appelle un ou plusieurs services communs et services applicatifs, dans un ordre prédéfini.
Cette architecture présente divers avantages :
- Tout d’abord, les applications clients et les services communs sont faiblement couplés. Cela permet également une gestion flexible du trafic et une stratégie de règles centralisée.
- Les règles peuvent être de l’ordre de la sécurité, comme l'authentification et l'autorisation, ou liées au SLA, comme les tentatives de rejeu et les règles de circuit-breaker, ou liées à l'observabilité et au monitoring.
- Cette architecture fournit un monitoring du système de bout en bout.
Cette architecture offre également une grande flexibilité sur la façon dont les différentes parties de l'architecture, qu'il s'agisse de l'application cliente, des services communs ou du routeur lui-même, interagissent :
- Les applications clientes peuvent être des applications web, des applications mobiles, des périphériques IoT ou d'autres services.
- Les services backend peuvent être des monolithes, des microservices, ou des fonctions serverless.
- Le service de routage peut être utilisé pour différents usages comme le routage, split routing ou les déploiements canary sans interruption pour les applications en production.
- La communication entre le client et le service peut être transactionnelle et synchrone à l'aide d'un mécanisme de requête/réponse, ou elle peut être basée sur un système de messaging asynchrone de mode publication/abonnement.
Dans cette architecture d’orchestration des services, les équipes des applications clientes se concentrent sur les interfaces utilisateur et les services spécifiques à l’application, protégeant ainsi la logique métier et la propriété intellectuelle de la versatilité de la technologie et des fonctionnalités communes. Tous les services communs, qu'ils soient métier, non fonctionnels ou de plateforme, seront hébergés sur la plateforme cloud et appelés par le service de routage, qui agit en tant qu'orchestrateur de service.
Comme le montre le diagramme de la figure 7, toutes les technologies et frameworks utilisés dans les services communs sont complètement abstraits des applications clientes.
Abstraction technologique
Figure 7. Les solutions basées sur l’orchestration des services abstraient les technologies des applications
Détaillons comment l'orchestration des services permet l’abstraction des technologies des applications clientes.
Avec un orchestrateur centralisé, comme illustré à la figure 7, les applications clientes n'ont pas besoin de connaître ces technologies. En outre, n'importe laquelle de ces technologies peut être mise à jour sans impact sur les applications clientes.
[L'orchestration de services isole les applications des technologies et des services communs, ce qui permet des mises à jour faciles de composants uniques]
Semblable aux architectures basées sur les services évoquées dans cet article, et même si l’orchestration de services semble une excellente option, elle présente encore quelques difficultés :
- Le service de routage peut devenir un point de défaillance unique (SPOF).
- Il y a un surcoût impactant les performances car le service de routage doit appeler chaque service impliqué dans le cas d'utilisation via le réseau.
- Il n'y a pas d'invocation native des services.
- Il n’y a pas de stratégie de règles décentralisée.
Avec les difficultés des trois architectures détaillées jusqu’à présent - systèmes distribués traditionnels, architecture microservices, et orchestration de services - discutons maintenant de quelques patterns cloud-native émergents, nommés service mesh et sidecar.
Service mesh et sidecar
Le dernier modèle d'architecture dont nous parlerons dans cet article est basé sur les patterns de Service mesh et sidecar, et il s'applique à la plateforme Kubernetes qui supporte nativement le sidecar.
Dans cette architecture illustrée à la figure 8, nous avons toujours un composant central, appelé "control plane", pour définir et gérer différentes règles, tout comme dans la solution d'orchestration de services.
Les conteneurs sidecar, qui font partie de ce que l'on appelle le "data plane", sont automatiquement injectés dans les services métiers au runtime. Ces proxys sidecar appliquent les règles définies dans le control plane et répliquées dans le data plane.
Les solutions de Service mesh peuvent aider à améliorer la sécurité, l’observabilité et la gestion du trafic dans l’architecture des systèmes distribués.
Les principes de base sur lesquels reposent les solutions de Service mesh sont la gestion et l’administration centralisée des règles, et une application décentralisée des règles (le meilleur des deux mondes).
Figure 8. Service mesh et sidecars pour les apps hébergées sur Kubernetes
Fonctionnalités du Service mesh
Les solutions basées sur un Service mesh offrent plusieurs avantages par rapport aux architectures de microservices traditionnelles en ce qui concerne la connectivité, la fiabilité, la sécurité et l'observabilité, comme indiqué ci-dessous.
Connectivité :
- Contrôle du trafic (routage, splitting)
- Gateway (ingress, egress)
- Découverte de services
- A/B testing, canary
- Timeouts des services, rejeux
Fiabilité :
- Circuit-breaker
- Injection de pannes/chaos testing
Sécurité :
- Authentification service à service (mTLS)
- Gestion des certificats
- Authentification d'utilisateur (JSON Web Tokens)
- Autorisation des utilisateurs (contrôle d'accès basé sur les rôles - RBAC)
- Chiffrement
Observabilité :
- Monitoring
- Télémétrie, instrumentation, métriques
- Traçage distribué
- Graphique des services
Les technologies de Service mesh ont suscité beaucoup d’intérêt ces dernières années et il existe plusieurs implémentations comme Istio, Linkerd, Consul Connect, etc. Puisque le but de cet article est de discuter des patterns d'architecture derrière les succès d’architectures microservices, nous n'entrerons pas dans les détails des fonctionnalités et des implémentations de Service mesh.
Si vous souhaitez en savoir plus sur les technologies Service mesh, consultez The InfoQ eMag - Service Mesh Ultimate Guide.
Conclusion
Il existe différentes manières d’implémenter les interactions et la communication entre les microservices. L'orchestration des services peut être gérée avec une API gateway comme composant central de l’architecture. Si nous avons besoin de fonctionnalités supplémentaires à celles offertes par l’API gateway, nous pouvons utiliser le Service mesh et le sidecar pour ces besoins additionnels d'architecture cloud native.
Il est important de concevoir l'interaction entre les différentes couches de l'architecture d'application cloud native, y compris la façon de modéliser les données, les services et les événements en tant que citoyens de première classe dans l'effort de modélisation.
Dans la quatrième partie de cet article, nous aborderons le dernier volet de l'adoption d'une architecture cloud native : DevOps cloud native. Nous verrons comment les pratiques DevOps telles que la CI/CD, la conteneurisation et la plateforme cloud Kubernetes, ainsi que les patterns de microservices et d'orchestration de services, peuvent aider les organisations à adopter le cloud.
Références (EN)
- Adoption of Cloud-Native Architecture, Part 1: Architecture Evolution and Maturity
- Adoption of Cloud Native Architecture, Part 2: Stabilization Gaps and Anti-Patterns
- Pattern: Service Mesh
- Microservices Communication and Governance Using Service Mesh
- Microservices Interaction and Governance Model - Orchestration v Choreography
- Multi-Runtime Microservices Architecture
- Responsible Microservices
- Introducing Istio Service Mesh for Microservices
À propos des auteurs
Srini Penchikala est architecte informatique sénior pour Global Manufacturing chez General Motors à Austin, Texas. Il a plus de 25 ans d'expérience dans l'architecture logicielle, la conception et le développement, et se concentre actuellement sur les architectures Cloud native, les microservices et le Service mesh, les pipelines de données dans le cloud et la livraison continue. Penchikala est le co-créateur et l'architecte de la mise en œuvre d'une solution de Service mesh au sein de l'organisation. Penchikala a écrit Big-Data Processing with Apache Spark et co-écrit Spring Roo in Action, chez Manning. Il est conférencier régulier, formateur en big data et a publié plusieurs articles sur divers sites web techniques.
Marcio Esteves est directeur du développement d'applications pour Tokyo Marine HCC à Houston au Texas, où il dirige les équipes d'architecture solution, d'assurance qualité et de développement qui collaborent au sein de la DSI pour permettre l’adoption de technologies avec un focus sur les systèmes basés sur le cloud et générateurs de revenus. Auparavant, Esteves était architecte en chef pour General Motors IT Global Manufacturing, menant les architectes et les ingénieurs cloud responsables des technologies favorisant la transformation numérique, comme le machine learning, le big data, l’IOT, l’intelligence artificielle ou les architectures cloud microservices. Esteves a développé la vision et la stratégie et a dirigé la mise en œuvre d'une solution de Service mesh d'entreprise chez GM avec des microservices scalables automatiquement utilisés par plusieurs applications métier critiques. Il est également conseiller technique du conseil d'administration de VertifyData à Austin.