BLOG | NGINX

Bonnes pratiques pour la configuration des applications de microservices

NGINX-Partie-de-F5-horiz-black-type-RGB
Vignette de Javier Evans
Javier Evans
Publié le 02 mars 2023

Les lignes directrices connues sous le nom d’application à douze facteurs ont été publiées pour la première fois il y a plus de dix ans. Depuis lors, presque toutes ses pratiques obligatoires sont devenues la norme de facto pour écrire et déployer des applications Web. Et même si elles restent applicables face aux changements dans la façon dont les applications sont organisées et déployées, dans certains cas, des nuances supplémentaires sont nécessaires pour comprendre comment les pratiques s'appliquent aux modèles de microservices pour le développement et le déploiement d'applications.

Ce blog se concentre sur le facteur 3, stocker la configuration dans l'environnement , qui stipule :

  • La configuration correspond à tout ce qui varie entre les environnements de déploiement (que l’application à douze facteurs appelle « déploiements »).
  • La configuration doit être strictement séparée du code de l’application – sinon, comment peut-elle varier selon les déploiements ?
  • Les données de configuration sont stockées dans des variables d’environnement.

À mesure que vous évoluez vers les microservices, vous pouvez toujours respecter ces directives, mais pas toujours d'une manière qui correspond exactement à une interprétation littérale de l'application à douze facteurs. Certaines directives, telles que la fourniture de données de configuration sous forme de variables d'environnement, sont parfaitement transposables. D’autres pratiques courantes en matière de microservices, tout en respectant les principes fondamentaux de l’application à douze facteurs, s’apparentent davantage à des extensions de celle-ci. Dans cet article, nous examinerons trois concepts fondamentaux de la gestion de la configuration pour les microservices à travers le prisme du facteur 3 :

Terminologie et concepts clés des microservices

Avant de passer à la discussion sur l’adaptation du facteur 3 aux microservices, il est utile de comprendre certains termes et concepts clés.

  • Architecture d’application monolithique – Un modèle architectural traditionnel qui sépare les fonctions d’application en modules de composants, mais inclut tous les modules dans une seule base de code.
  • Architecture d’application de microservices – Un modèle architectural qui crée une application volumineuse et complexe à partir de plusieurs petits composants qui exécutent chacun un ensemble d’opérations bien définies (telles que l’authentification, la notification ou le traitement des paiements). « Microservice » est également le nom donné aux petits composants eux-mêmes. En pratique, certains « microservices » peuvent en réalité être assez volumineux.
  • Service – Terme général désignant une application unique ou un microservice dans un système.
  • Système – Dans le contexte de ce blog, l’ensemble complet des microservices et de l’infrastructure de support qui s’assemblent pour créer l’ensemble des fonctionnalités fournies par l’organisation.
  • Artefact – Un objet créé par un pipeline de test et de build. Cela peut prendre de nombreuses formes, comme une image Docker contenant le code d'une application.
  • Déploiement – Une « instance » en cours d’exécution d’un artefact, qui s’exécute dans un environnement tel que la préparation, l’intégration ou la production.

Microservices versus monolithes

Avec une application monolithique, toutes les équipes de l’organisation travaillent sur la même application et l’infrastructure environnante. Bien que les applications monolithiques semblent généralement plus simples que les microservices sur le papier, il existe plusieurs raisons courantes pour lesquelles les organisations décident de passer aux microservices :

  • Autonomie de l’équipe – Il peut être difficile de définir la propriété des fonctionnalités et des sous-systèmes dans un monolithe. À mesure que les organisations grandissent et mûrissent, la responsabilité des fonctionnalités des applications est souvent répartie entre de plus en plus d’équipes. Cela crée des dépendances entre les équipes car l'équipe qui possède une partie de la fonctionnalité ne possède pas tous les sous-systèmes associés dans le monolithe.
  • Réduire le « rayon d’explosion » – Lorsqu’une grande application est développée et déployée comme une seule unité, une erreur dans un sous-système peut dégrader la fonctionnalité de l’ensemble de l’application.
  • Mise à l’échelle indépendante des fonctionnalités – Même si un seul module d’une application monolithique est soumis à une charge importante, l’organisation doit déployer de nombreuses instances de l’application entière pour éviter une défaillance ou une dégradation du système.

Bien sûr, les microservices comportent leurs propres défis – notamment une complexité accrue, une moindre observabilité et le besoin de nouveaux modèles de sécurité – mais de nombreuses organisations, en particulier les grandes ou celles à croissance rapide, décident que les défis en valent la peine pour donner à leurs équipes plus d’autonomie et de flexibilité dans la création de bases fiables et stables pour les expériences qu’elles offrent à leurs clients.

Modifications requises pour les architectures de microservices

Lorsque vous refactorisez une application monolithique en microservices, vos services doivent :

  • Accepter les changements de configuration de manière prévisible
  • Se faire connaître du système plus large de manière prévisible
  • Soyez bien documenté

Pour une application monolithique, de petites incohérences dans les processus et une dépendance à des hypothèses partagées ne sont pas critiques. Cependant, avec de nombreux microservices distincts, ces incohérences et hypothèses peuvent introduire beaucoup de douleur et de chaos. La plupart des changements que vous devez apporter aux microservices sont des nécessités techniques, mais un nombre surprenant d’entre eux concernent la manière dont les équipes travaillent en interne et interagissent avec les autres équipes.

Les changements organisationnels notables avec une architecture de microservices incluent :

  • Au lieu de travailler ensemble sur la même base de code, les équipes deviennent totalement séparées, chaque équipe étant entièrement responsable d'un ou de plusieurs services. Dans la mise en œuvre la plus courante des microservices, les équipes sont également réorganisées pour être « interfonctionnelles », ce qui signifie qu’elles ont des membres possédant toutes les compétences requises pour atteindre les objectifs de l’équipe avec des dépendances minimales vis-à-vis des autres équipes.
  • Les équipes de plateforme (responsables de la santé globale du système) doivent désormais coordonner plusieurs services appartenant à différentes équipes au lieu de gérer une seule application.
  • Les équipes d’outillage doivent rester en mesure de fournir des outils et des conseils aux différentes équipes de propriétaires de services pour les aider à atteindre rapidement leurs objectifs, tout en maintenant la stabilité du système.

Diagramme comparant l'organisation des équipes de développeurs pour les applications monolithiques et les microservices

Définir clairement la configuration de votre service

Un domaine de l’architecture des microservices dans lequel nous devons étendre le facteur 3 concerne la nécessité de définir clairement certaines informations vitales sur un service, y compris sa configuration, et de supposer un minimum de contexte partagé avec d’autres services. Le facteur 3 n’aborde pas ce problème directement, mais il est particulièrement important avec un grand nombre de microservices distincts contribuant aux fonctionnalités de l’application.

En tant que propriétaire de service dans une architecture de microservices, votre équipe possède des services qui jouent des rôles spécifiques dans le système dans son ensemble. Les autres équipes dont les services interagissent avec les vôtres doivent accéder au référentiel de votre service pour lire le code et la documentation ainsi que pour apporter des contributions.

De plus, c’est une triste réalité dans le domaine du développement de logiciels que la composition des équipes change souvent, non seulement parce que les développeurs rejoignent et quittent l’entreprise, mais aussi en raison de réorganisations internes. De plus, la responsabilité d’un service donné est également souvent transférée entre les équipes.

Au vu de ces réalités, votre base de code et votre documentation doivent être extrêmement claires et cohérentes, ce qui est réalisé en :

  • Définir clairement l'objectif de chaque option de configuration
  • Définir clairement le format attendu de la valeur de configuration
  • Définir clairement comment l'application s'attend à ce que les valeurs de configuration soient fournies
  • Enregistrer ces informations dans un nombre limité de fichiers

De nombreux frameworks d’application fournissent un moyen de définir la configuration requise. Par exemple, le package NPM convict pour les applications Node.js utilise un « schéma » de configuration complet stocké dans un seul fichier. Il agit comme la source de vérité pour toute la configuration dont une application Node.js a besoin pour fonctionner.

Un schéma robuste et facilement détectable permet aux membres de votre équipe et à d'autres d'interagir en toute confiance avec votre service.

Comment la configuration est fournie à un service

Après avoir clairement défini les valeurs de configuration dont votre application a besoin, vous devez également respecter la distinction importante entre les deux sources principales à partir desquelles une application de microservices déployée extrait sa configuration :

  • Scripts de déploiement qui définissent explicitement les paramètres de configuration et accompagnent le code source de l'application
  • Sources externes interrogées au moment du déploiement

Les scripts de déploiement sont un modèle d’organisation de code courant dans les architectures de microservices. Comme ils sont nouveaux depuis la publication originale de l’application à douze facteurs, ils représentent nécessairement une extension de celle-ci.

Modèle: Déploiement et configuration de l'infrastructure à côté de l'application

Ces dernières années, il est devenu courant d’avoir un dossier appelé infrastructure (ou une variante de ce nom) dans le même référentiel que le code de votre application. Il contient généralement :

  • Infrastructure en tant que code ( Terraform est un exemple courant) qui décrit l'infrastructure dont dépend le service, comme une base de données
  • Configuration de votre système d'orchestration de conteneurs, comme les graphiques Helm et les manifestes Kubernetes
  • Tout autre fichier lié au déploiement de l'application

À première vue, cela peut sembler être une violation de la prescription du Facteur 3 selon laquelle la configuration est strictement séparée du code.

En fait, son placement à côté de votre application signifie qu'un dossier d'infrastructure respecte réellement la règle tout en permettant des améliorations de processus précieuses qui sont essentielles pour les équipes qui travaillent dans des environnements de microservices.

Les avantages de ce modèle incluent :

  • L’équipe propriétaire du service possède également le déploiement du service et le déploiement de l’infrastructure spécifique au service (comme les bases de données).
  • L'équipe propriétaire peut s'assurer que les modifications apportées à l'un de ces éléments passent par son processus de développement (révision du code, CI).
  • L’équipe peut facilement modifier la manière dont son service et son infrastructure de support sont déployés sans dépendre d’équipes externes pour effectuer le travail à sa place.

Notez que les avantages offerts par ce modèle renforcent l’autonomie individuelle de l’équipe, tout en garantissant qu’une rigueur supplémentaire est appliquée au processus de déploiement et de configuration.

Quel type de configuration va où ?

En pratique, vous utilisez les scripts de déploiement stockés dans votre dossier d'infrastructure pour gérer à la fois la configuration définie explicitement dans les scripts eux-mêmes et la récupération de la configuration à partir de sources externes au moment du déploiement, en ayant le script de déploiement pour un service :

  1. Définir directement certaines valeurs de configuration
  2. Définir où le processus exécutant le script de déploiement peut rechercher les valeurs de configuration souhaitées dans des sources externes

Les valeurs de configuration spécifiques à un certain déploiement de votre service et entièrement sous le contrôle de votre équipe peuvent être spécifiées directement dans les fichiers du dossier d’infrastructure. Un exemple pourrait être une limite sur la durée pendant laquelle une requête de base de données initiée par l'application est autorisée à s'exécuter. Cette valeur peut être modifiée en modifiant le fichier de déploiement et en redéployant l'application.

L’un des avantages de ce schéma est que les modifications apportées à une telle configuration passent nécessairement par une révision du code et des tests automatisés, ce qui réduit la probabilité qu’une valeur mal configurée provoque une panne. Les modifications apportées aux valeurs qui passent par la révision du code et les valeurs des clés de configuration à un moment donné sont détectables dans l’historique de vos outils de contrôle de source.

Les valeurs nécessaires à l’exécution de l’application mais qui ne sont pas sous le contrôle de votre équipe doivent être fournies par l’environnement dans lequel l’application est déployée. Un exemple est le nom d’hôte et le port auquel le service se connecte à un autre microservice dont il dépend.

Étant donné que ce service n’appartient pas à votre équipe, vous ne pouvez pas faire d’hypothèses sur des valeurs telles que le numéro de port. Ces valeurs peuvent changer à tout moment et doivent être enregistrées dans un stockage de configuration central lorsqu'elles sont modifiées, que cette modification soit effectuée manuellement ou par un processus automatique. Ils peuvent ensuite être interrogés par les applications qui en dépendent.

Nous pouvons résumer ces directives en deux bonnes pratiques pour la configuration des microservices.

Une configuration de microservices à ne pas faire : S'appuyer sur des valeurs codées en dur ou convenues d'un commun accord

Il peut sembler plus simple de coder en dur certaines valeurs dans vos scripts de déploiement, par exemple l'emplacement d'un service avec lequel votre service interagit. En pratique, le codage en dur de ce type de configuration est dangereux, en particulier dans les environnements modernes où les emplacements des services changent souvent. Et c’est particulièrement dangereux si vous ne possédez pas le deuxième service.

Vous pourriez penser que vous pouvez compter sur votre propre diligence pour maintenir l’emplacement d’un service à jour dans vos scripts, ou pire, que vous pouvez compter sur l’équipe propriétaire pour vous informer lorsque l’emplacement change. En période de stress, la diligence est souvent négligée et le recours à la rigueur humaine expose votre système au risque de défaillance sans avertissement.

Une configuration de microservices à faire : Demander au service « Où se trouve ma base de données ? »

Que les informations de localisation soient codées en dur ou non, votre application ne doit pas dépendre de la présence d'une infrastructure critique à un certain emplacement. Au lieu de cela, un service nouvellement déployé doit poser à une source commune au sein du système des questions telles que « où se trouve ma base de données ? » et recevoir une réponse précise sur l'emplacement actuel de cette ressource externe. Le fait que chaque service s’enregistre auprès du système au fur et à mesure de son déploiement simplifie grandement les choses.

Rendre un service disponible en tant que configuration

Tout comme le système doit fournir des réponses aux questions « où se trouve ma base de données ? » et « où se trouve le « service X » dont je dépends ? », un service doit être exposé au système de manière à ce que d’autres services puissent facilement le trouver et lui communiquer sans rien savoir de la manière dont il est déployé.

Une pratique de configuration clé dans les architectures de microservices est la découverte de services : l’enregistrement de nouvelles informations de service et la mise à jour dynamique de ces informations telles qu’elles sont accessibles par d’autres services. Après avoir expliqué pourquoi la découverte de services est nécessaire pour les microservices, explorons un exemple de la manière de la réaliser avec NGINX Open Source et Consul.

Il est courant d’avoir plusieurs instances (déploiements) d’un service exécutées simultanément. Cela permet non seulement de gérer un trafic supplémentaire, mais également de mettre à jour un service sans temps d'arrêt en lançant un nouveau déploiement. Agissant comme proxy inverse et équilibreur de charge, des outils comme NGINX traitent le trafic entrant et l'acheminent vers l'instance la plus appropriée. Il s’agit d’un modèle intéressant, car les services qui dépendent de votre service envoient des requêtes uniquement à NGINX et n’ont pas besoin de connaître quoi que ce soit de vos déploiements.

À titre d’exemple, supposons que vous ayez une seule instance d’un service appelé Messenger exécuté derrière NGINX agissant comme un proxy inverse.

Diagramme d'une instance unique du microservice « messenger » faisant l'objet d'un proxy inverse par NGINX

Et maintenant, que se passe-t-il si votre application devient populaire ? Cela est considéré comme une bonne nouvelle, mais vous remarquez ensuite qu'en raison de l'augmentation du trafic, l'instance de messagerie consomme beaucoup de CPU et prend plus de temps pour traiter les demandes, alors que la base de données semble fonctionner très bien. Cela indique que vous pourrez peut-être résoudre le problème en déployant une autre instance du service de messagerie .

Lorsque vous déployez la deuxième instance du service de messagerie , comment NGINX sait-il qu'elle est active et commence-t-il à lui envoyer du trafic ? L’ajout manuel de nouvelles instances à votre configuration NGINX est une approche, mais elle devient rapidement ingérable à mesure que davantage de services évoluent vers le haut et vers le bas.

Une solution courante consiste à suivre les services dans un système avec un registre de services hautement disponible comme Consul . Les nouvelles instances de service s'enregistrent auprès de Consul au fur et à mesure de leur déploiement. Consul surveille l'état des instances en leur envoyant périodiquement des contrôles de santé. Lorsqu'une instance échoue aux contrôles d'intégrité, elle est supprimée de la liste des services disponibles.

Diagramme de deux instances du microservice « messenger » faisant l'objet d'un proxy inverse par NGINX, avec Consul pour la découverte de services

NGINX peut interroger un registre comme Consul en utilisant diverses méthodes et ajuster son routage en conséquence. N'oubliez pas que lorsqu'il agit en tant que proxy inverse ou équilibreur de charge, NGINX achemine le trafic vers des serveurs « en amont ». Considérez cette configuration simple :


# Définir un groupe en amont appelé "messenger_service"
upstream messenger_service {
server 172.18.0.7:4000;
server 172.18.0.8:4000;
}

server {
listen 80;

location /api {
# Trafic HTTP proxy avec des chemins commençant par '/api' vers le
# bloc 'upstream' ci-dessus. L'algorithme d'équilibrage de charge par défaut, 
# Round-Robin, alterne les requêtes entre les deux serveurs 
# dans le bloc.
proxy_pass http://messenger_service;
proxy_set_header X-Forwarded-For $remote_addr;
}
}


Par défaut, NGINX doit connaître l'adresse IP et le port précis de chaque instance de messagerie pour acheminer le trafic vers celle-ci. Dans ce cas, il s'agit du port 4000 sur 172.18.0.7 et 172.18.0.8.

C'est là qu'interviennent Consul et le modèle Consul . Le modèle Consul s'exécute dans le même conteneur que NGINX et communique avec le client Consul qui gère le registre de services.

Lorsque les informations du registre changent, le modèle Consul génère une nouvelle version du fichier de configuration NGINX avec les adresses IP et les ports corrects, l'écrit dans le répertoire de configuration NGINX et indique à NGINX de recharger sa configuration. Il n’y a aucun temps d’arrêt lorsque NGINX recharge sa configuration et la nouvelle instance commence à recevoir du trafic dès que le rechargement est terminé.

Avec un proxy inverse tel que NGINX dans ce genre de situation, il existe un point de contact unique pour s'inscrire auprès du système comme lieu d'accès pour les autres services. Votre équipe a la flexibilité de gérer des instances de service individuelles sans avoir à se soucier de la perte d’accès d’autres services au service dans son ensemble.

Découvrez NGINX et les microservices en mars

Il est vrai que les microservices augmentent la complexité, tant au niveau technique de vos services qu’au niveau organisationnel de vos relations avec les autres équipes. Pour profiter des avantages d’une architecture de microservices, il est important de réexaminer de manière critique les pratiques conçues pour les monolithes afin de s’assurer qu’elles offrent toujours les mêmes avantages lorsqu’elles sont appliquées à un environnement très différent. Dans ce blog, nous avons exploré comment le facteur 3 de l’application à douze facteurs offre toujours de la valeur dans un contexte de microservices, mais peut bénéficier de petits changements dans la manière dont il est concrètement appliqué.

Pour en savoir plus sur l’application de l’application à douze facteurs aux architectures de microservices, consultez l’unité 1 de Microservices mars 2023 ( à venir sur le blog ). Inscrivez-vous gratuitement pour avoir accès à un webinaire sur ce sujet et à un atelier pratique.


« Cet article de blog peut faire référence à des produits qui ne sont plus disponibles et/ou qui ne sont plus pris en charge. Pour obtenir les informations les plus récentes sur les produits et solutions F5 NGINX disponibles, explorez notre famille de produits NGINX . NGINX fait désormais partie de F5. Tous les liens NGINX.com précédents redirigeront vers un contenu NGINX similaire sur F5.com."