Cet article fait partie d’une série de quatre tutoriels qui vous aideront à mettre en pratique les concepts de Microservices March 2023 : commencez à déployer des microservices :
- Comment déployer et configurer des microservices (cet article)
- Comment gérer les secrets de manière sécurisée dans les conteneurs
- Comment utiliser GitHub Actions pour automatiser les déploiements canaris de microservices
- Comment utiliser le traçage OpenTelemetry pour comprendre vos microservices
Toutes les applications nécessitent une configuration, mais les considérations à prendre en compte lors de la configuration d’un microservice peuvent différer de celles qui s’appliquent à une application monolithique. Nous pouvons nous référer au facteur 3 (Stocker la configuration dans l’environnement) de l’application à douze facteurs pour obtenir des recommandations applicables aux deux types d’applications, tout en les adaptant aux applications de microservices. Nous pouvons notamment adapter la manière dont nous définissons la configuration du service, fournissons cette configuration à un service et rendons un service disponible en tant que valeur de configuration pour les autres services qui peuvent en dépendre.
Pour comprendre de manière conceptuelle comment adapter le facteur 3 aux microservices – notamment les bonnes pratiques relatives aux fichiers de configuration, aux bases de données et à la découverte de services – consultez l’article Bonnes pratiques de configuration des applications de microservices sur notre blog. Cet article est un excellent moyen de mettre ces connaissances en pratique.
Remarque : L’objectif de ce tutoriel est d’illustrer certains concepts fondamentaux, et non de présenter la bonne façon de déployer des microservices en production. Bien qu’il utilise une véritable architecture de « microservices », il convient de tenir compte de certaines réserves importantes :
- Ce tutoriel n’utilise pas de plateforme d’orchestration de conteneurs telle que Kubernetes ou Nomad. Vous pouvez ainsi vous familiariser avec les concepts liés aux microservices sans vous perdre dans les spécificités d’une plateforme donnée. Les modèles présentés ici peuvent être transposés à un système exécutant l’une de ces plateformes.
- Les services sont conçus pour être faciles à comprendre plutôt que pour respecter des pratiques rigoureuses de génie logiciel. L’objectif est d’examiner le rôle d’un service dans le système et ses modes de communication, et non les détails du code. Pour plus d’informations, consultez les fichiers README de chaque service.
Présentation du tutoriel
Ce tutoriel illustre comment les concepts du facteur 3 s’appliquent aux applications de microservices. Au fil de quatre défis, vous découvrirez plusieurs modèles courants de configuration des microservices, puis déploierez et configurerez un service à l’aide de ces modèles :
- Dans le défi 1 et le défi 2 vous explorez le premier modèle, qui porte sur l’emplacement de la configuration d’une application de microservices. Il existe trois emplacements courants :
- Le code de l’application
- Le script de déploiement de l’application
- Sources externes auxquelles le script de déploiement accède
- Dans le défi 3 vous avez mis en place deux autres modèles : l’exposition de l’application au monde extérieur via NGINX en tant que proxy inverse et l’activation de la découverte de services à l’aide de Consul.
- Dans le défi 4 vous mettez en œuvre le dernier modèle : utiliser une instance de votre microservice comme « exécuteur de tâches » afin d’effectuer une action ponctuelle différente de sa fonction habituelle (dans ce cas, simuler une migration de base de données).
Ce tutoriel utilise quatre technologies :
- messenger – Une API de chat simple permettant de stocker les messages, créée pour ce tutoriel
- NGINX Open Source – Un point d’entrée vers le service messenger et, plus largement, vers l’ensemble du système
- Consul – Un registre de services dynamique et un magasin clé-valeur
- RabbitMQ – Un courtier de messages open source populaire qui permet aux services de communiquer de manière asynchrone

Regardez cette vidéo pour avoir un aperçu du tutoriel. Les étapes ne correspondent pas exactement à celles décrites dans cet article, mais la vidéo aide à mieux comprendre les concepts.
 
Prérequis et configuration
Prérequis
Pour suivre ce tutoriel dans votre propre environnement, vous avez besoin des éléments suivants :
- Un environnement compatible avec Linux/Unix
- Connaissances de base de la ligne de commande Linux, de JavaScript et de
bash(mais l’ensemble du code et des commandes est fourni et expliqué, vous pouvez donc réussir même avec des connaissances limitées) - Docker et Docker Compose
- Node.js 19.x ou version ultérieure
- Nous avons testé la version 19.x, mais nous pensons que les versions plus récentes de Node.js fonctionnent également.
- Pour obtenir des informations détaillées sur l’installation de Node.js, consultez le fichier README dans le dépôt du service messenger. Vous pouvez également installer
asdfafin d’utiliser exactement la même version de Node.js que celle employée dans les conteneurs.
curl(déjà installé sur la plupart des systèmes)- Les quatre technologies répertoriées dans la Présentation du tutoriel : messenger (que vous téléchargerez dans la section suivante), NGINX Open Source, Consul et RabbitMQ.
Configuration
- Ouvrez une session de terminal (dans les instructions suivantes, celle-ci sera appelée le « terminal de l’application »).
- Dans votre répertoire personnel, créez le répertoire microservices-march et clonez-y les dépôts GitHub de ce tutoriel. (Vous pouvez également utiliser un autre nom de répertoire et adapter les instructions en conséquence.) Remarque : tout au long du tutoriel, l’invite de commande Linux est omise afin de faciliter le copier-coller des commandes dans votre terminal. Le tilde (
~) représente votre répertoire personnel.
mkdir ~/microservices-marchcd ~/microservices-march git clone https://github.com/microservices-march/platform.git --branch mm23-twelve-factor-start git clone https://github.com/microservices-march/messenger.git --branch mm23-twelve-factor-start- Accédez au dépôt « platform » et démarrez Docker Compose :
cd platformdocker compose up -d --build- Cela démarre RabbitMQ et Consul, qui seront utilisés dans les exercices suivants.
- L’option «
-d» indique à Docker Compose de se détacher des conteneurs une fois qu’ils ont démarré (sinon, les conteneurs resteront attachés à votre terminal). - L’option «
--build» indique à Docker Compose de reconstruire toutes les images au démarrage. Cela garantit que les images que vous exécutez restent à jour en cas de modification éventuelle des fichiers.
- L’option «
- Accédez au dépôt messenger et démarrez Docker Compose :
cd ../messengerdocker compose up -d --build- Cela démarre la base de données PostgreSQL du service messenger, que nous appellerons la messenger-database dans la suite de ce tutoriel.
Défi 1 : définir la configuration des microservices au niveau de l’application
Dans cet exercice, vous configurez le premier des trois emplacements que nous examinerons dans ce tutoriel : le niveau de l’application. (L’exercice 2 présente les deuxième et troisième emplacements : les scripts de déploiement et les sources externes.)
L’application « twelve-factor » exclut expressément la configuration au niveau de l’application, car celle-ci n’a pas besoin de changer entre les différents environnements de déploiement (que l’application « twelve-factor » appelle des « déploiements »). Néanmoins, par souci d’exhaustivité, nous abordons les trois types, car chaque catégorie doit être traitée différemment lors du développement, de la compilation et du déploiement d’un service.
Le service « messenger » est écrit en Node.js, avec pour point d’entrée app/index.mjs dans le dépôt messenger. Cette ligne du fichier :
app.use(express.json());est un exemple de configuration au niveau de l’application. Il configure le framework Express afin de désérialiser les corps de requête de type application/json en objets JavaScript.
Cette logique est étroitement liée au code de votre application et ne correspond pas à ce que la méthodologie des applications à douze facteurs considère comme de la « configuration ». Cependant, en matière de logiciels, tout dépend du contexte, n’est-ce pas ?
Dans les deux sections suivantes, vous modifierez cette ligne afin de mettre en œuvre deux exemples de configuration au niveau de l’application.
Exemple 1
Dans cet exemple, vous définissez la taille maximale du corps d’une requête acceptée par le service messenger. Cette limite de taille est définie par l’argument limit de la fonction express.json, comme indiqué dans la documentation de l’API Express. Ici, vous ajoutez l’argument limit à la configuration du middleware JSON du framework Express présentée ci-dessus.
- Dans votre éditeur de texte préféré, ouvrez app/index.mjs et remplacez :
app.use(express.json())- avec :
app.use(express.json({ limit: "20b" }));- Dans le terminal de l’application (celui que vous avez utilisé lors de la configuration), accédez au répertoire app et démarrez le service messenger :
cd app npm install node index.mjs
messenger_service listening on port 4000- Démarrez une deuxième session de terminal distincte (appelée terminal client dans les instructions suivantes) et envoyez une requête
POSTau service messenger. Le message d’erreur indique que la requête a bien été traitée, car son corps ne dépassait pas la limite de 20 octets définie à l’étape 1, mais que le contenu de la charge utile JSON est incorrect :
curl -d '{ "text": "hello" }' -H "Content-Type: application/json" -X POST http://localhost:4000/conversations...
{ "error": "Conversation must have 2 unique users" }- Envoyez un corps de message légèrement plus long (à nouveau dans le terminal client). La sortie est bien plus volumineuse qu’à l’étape 3 et comprend notamment un message d’erreur indiquant que, cette fois, le corps de la requête dépasse 20 octets :
curl -d '{ "text": "hello, world" }' -H "Content-Type: application/json" -X POST http://localhost:4000/conversations...
\”PayloadTooLargeError: request entity too large"Exemple 2
Cet exemple utilise convict, une bibliothèque qui permet de définir un « schéma » de configuration complet dans un seul fichier. Il illustre également deux recommandations du facteur 3 de l’application à douze facteurs :
- Stocker la configuration dans des variables d’environnement – Vous modifiez l’application afin que la taille maximale du corps soit définie à l’aide d’une variable d’environnement (
JSON_BODY_LIMIT) au lieu d’être codée en dur dans le code de l’application. - Définissez clairement la configuration de votre service – Il s’agit d’une adaptation du facteur 3 aux microservices. Si ce concept ne vous est pas familier, nous vous recommandons de prendre quelques instants pour consulter l’article Bonnes pratiques de configuration des applications de microservices sur notre blog.
L’exemple met également en place certains éléments d’infrastructure dont vous tirerez parti dans le défi 2 : le script de déploiement messenger que vous créerez dans ce défi définit la variable d’environnement JSON_BODY_LIMIT, que vous insérez ici dans le code de l’application afin d’illustrer une configuration spécifiée dans un script de déploiement.
- Ouvrez le fichier de configuration
convict, app/config/config.mjs, puis ajoutez le contenu suivant en tant que nouvelle clé après la cléamqpport:
jsonBodyLimit: { doc: `The max size (with unit included) that will be parsed by the
JSON middleware. Unit parsing is done by the
https://www.npmjs.com/package/bytes library.
ex: "100kb"`,
format: String,
default: null,
env: "JSON_BODY_LIMIT",
},- La bibliothèque
convictse charge d’analyser la variable d’environnementJSON_BODY_LIMITlorsque vous l’utilisez pour définir la taille maximale du corps dans la ligne de commande à l’étape 3 ci-dessous :- Récupère la valeur depuis la variable d’environnement appropriée
- Vérifie le type de la variable (
String) - Permet d’y accéder dans l’application via la clé «
jsonBodyLimit»
- Dans app/index.mjs remplacez :
app.use(express.json({ limit: "20b" }));- avec
app.use(express.json({ limit: config.get("jsonBodyLimit") }));- Dans le terminal de l’application (où vous avez démarré le service messenger à l’étape 2 de l’exemple 1), appuyez sur
Ctrl+cpour arrêter le service. Redémarrez-le ensuite en utilisant la variable d’environnementJSON_BODY_LIMITpour définir la taille maximale du corps sur 27 octets :
^cJSON_BODY_LIMIT=27b node index.mjs- Voici un exemple de modification de la méthode de configuration lorsque cela est pertinent pour votre cas d’usage : au lieu de coder en dur une valeur (ici, une limite de taille) dans le code de l’application, vous la définissez à l’aide d’une variable d’environnement, comme le recommande la méthodologie des applications à douze facteurs. Comme indiqué précédemment, dans le Défi 2, l’utilisation de la variable d’environnement
JSON_BODY_LIMITillustrera le deuxième emplacement de configuration, lorsque vous utiliserez le script de déploiement du service messenger pour définir la variable d’environnement plutôt que de la spécifier sur la ligne de commande. - Dans le terminal client, exécutez à nouveau la commande
curlde l’étape 4 de l’exemple 1 (avec le corps de requête plus volumineux). Comme vous avez désormais augmenté la taille maximale à 27 octets, le corps de la requête ne dépasse plus cette limite et vous obtenez un message d’erreur indiquant que la requête a été traitée, mais que le contenu de la charge utile JSON est incorrect :
curl -d '{ "text": "hello, world" }' -H "Content-Type: application/json" -X POST http://localhost:4000/conversations{ "error": "Conversation must have 2 unique users" }- Vous pouvez fermer le terminal client si vous le souhaitez. Pour la suite du tutoriel, vous exécuterez toutes les commandes dans le terminal de l’application.
- Dans le terminal de l’application, appuyez sur
Ctrl+cpour arrêter le service messenger (vous avez arrêté et redémarré le service dans ce terminal à l’étape 3 ci-dessus).
^c- Arrêtez messenger-database. Vous pouvez ignorer sans risque le message d’erreur affiché, car le réseau est toujours utilisé par les éléments d’infrastructure définis dans le dépôt platform. Exécutez cette commande à la racine du dépôt messenger.
docker compose down
...failed to remove network mm_2023....Défi 2 : créer des scripts de déploiement pour un service
« La configuration doit être strictement séparée du code (sinon, comment pourrait-elle varier d’un déploiement à l’autre ?) » — Extrait du facteur 3 de l’application à douze facteurs
À première vue, vous pourriez comprendre cela comme une invitation à « ne pas intégrer la configuration au système de gestion des versions ». Dans cet exercice, vous mettrez en œuvre un modèle courant pour les environnements de microservices qui peut sembler enfreindre cette règle, mais qui, en réalité, la respecte tout en apportant de précieuses améliorations aux processus, essentielles dans ce type d’environnement.
Dans ce défi, vous allez créer des scripts de déploiement afin de reproduire les fonctionnalités de l’infrastructure en tant que code et des manifestes de déploiement qui fournissent la configuration à un microservice, modifier les scripts afin d’utiliser des sources de configuration externes, définir un secret, puis exécuter les scripts afin de déployer les services et leur infrastructure.
Vous créez les scripts de déploiement dans un nouveau répertoire infrastructure du dépôt messenger. Un répertoire nommé infrastructure (ou une variante de ce nom) est couramment utilisé dans les architectures modernes de microservices pour stocker des éléments tels que :
- Infrastructure en tant que code (par exemple, Terraform, AWS CloudFormation, Google Cloud Deployment Manager, et Azure Resource Manager)
- Configuration du système d’orchestration de conteneurs (par exemple, charts Helm et manifestes Kubernetes)
- Tout autre fichier lié au déploiement d’applications
Les avantages de ce modèle sont les suivants :
- Il confie à l’équipe responsable du service la responsabilité du déploiement de celui-ci et de l’infrastructure qui lui est propre (comme les bases de données).
- L’équipe peut s’assurer que les modifications apportées à chacun de ces éléments suivent son processus de développement (revue de code, intégration continue, etc.).
- L’équipe peut facilement modifier la manière dont le service et son infrastructure sous-jacente sont déployés, sans dépendre d’équipes externes pour effectuer le travail à sa place.
Comme indiqué précédemment, ce tutoriel n’a pas pour objectif de montrer comment mettre en place un système réel, et les scripts que vous déployez dans le cadre de cet exercice ne ressemblent pas à ceux d’un véritable système de production. Ils illustrent plutôt certains concepts fondamentaux et problèmes résolus par une configuration propre à chaque outil lors du déploiement d’une infrastructure liée aux microservices, tout en limitant autant que possible la dépendance des scripts à des outils spécifiques.
Créer les scripts de déploiement initiaux
- Dans le terminal de l’application, créez un répertoire infrastructure à la racine du dépôt messenger et créez des fichiers destinés à contenir les scripts de déploiement du service messenger et de la base de données messenger-database. Selon votre environnement, vous devrez peut-être faire précéder les commandes
chmoddesudo:
mkdir infrastructurecd infrastructure touch messenger-deploy.sh chmod +x messenger-deploy.sh touch messenger-db-deploy.sh chmod +x messenger-db-deploy.sh- Dans votre éditeur de texte préféré, ouvrez messenger-deploy.sh et ajoutez le contenu suivant afin de créer un script de déploiement initial pour le service messenger :
#!/bin/bashset -e
JSON_BODY_LIMIT=20b
docker run \
--rm \
-e JSON_BODY_LIMIT="${JSON_BODY_LIMIT}" \
messengerÀ ce stade, ce script n’est pas encore complet, mais il illustre quelques concepts :
- Il attribue une valeur aux variables d’environnement en intégrant directement cette configuration dans le script de déploiement.
- Elle utilise l’option «
-e» de la commande «dockerrun» pour injecter des variables d’environnement dans le conteneur lors de son exécution.
Définir ainsi la valeur des variables d’environnement peut sembler redondant, mais cela signifie que, quelle que soit la complexité de ce script de déploiement, il vous suffit de jeter un coup d’œil au tout début du script pour comprendre comment les données de configuration sont fournies au déploiement.
De plus, bien qu’un véritable script de déploiement n’exécute pas nécessairement explicitement la commande docker run, cet exemple de script vise à illustrer les principaux problèmes que permet de résoudre un élément tel qu’un manifeste Kubernetes. Lorsqu’un système d’orchestration de conteneurs comme Kubernetes est utilisé, un déploiement démarre un conteneur et met à sa disposition la configuration de l’application issue de vos fichiers de configuration Kubernetes. Nous pouvons donc considérer cet exemple de fichier de déploiement comme une version minimale d’un script de déploiement jouant le même rôle que des fichiers de déploiement propres à un framework, tels que les manifestes Kubernetes.
Dans un environnement de développement réel, vous pourriez ajouter ce fichier au système de gestion de versions et le soumettre à une revue de code. Le reste de votre équipe pourrait ainsi commenter vos paramètres, ce qui contribuerait à éviter les incidents où des valeurs mal configurées entraînent un comportement inattendu. Par exemple, dans cette capture d’écran, un membre de l’équipe fait remarquer à juste titre qu’une limite de 20 octets pour les corps des requêtes JSON entrantes (définie avec JSON_BODY_LIMIT) est trop basse.

Modifier les scripts de déploiement pour interroger des sources externes afin d’obtenir les valeurs de configuration
Dans cette partie du défi, vous configurez le troisième emplacement destiné à la configuration d’un microservice : une source externe interrogée au moment du déploiement. Enregistrer dynamiquement les valeurs et les récupérer depuis une source externe au moment du déploiement constitue une bien meilleure pratique que de coder les valeurs en dur, car celles-ci doivent alors être constamment mises à jour et peuvent provoquer des défaillances. Pour en savoir plus, consultez l’article « Bonnes pratiques pour configurer les applications de microservices » sur notre blog.
À ce stade, deux composants d’infrastructure s’exécutent en arrière-plan afin de fournir les services auxiliaires requis par le service messenger :
- RabbitMQ, géré par l’équipe Plateforme dans un déploiement réel (démarré à l’étape 3 de « Configuration »)
- La messenger-database, gérée par votre équipe dans un déploiement réel (démarrée à l’étape 4 de la section « Configuration »)
Le schéma convict du service messenger dans app/config/config.mjs définit les variables d’environnement requises correspondant à ces éléments de configuration externe. Dans cette section, vous configurez ces deux composants afin qu’ils fournissent la configuration en définissant les valeurs des variables dans un emplacement accessible à tous, de sorte que le service messenger puisse les récupérer lors de son déploiement.
Les informations de connexion requises pour RabbitMQ et la base de données messenger-database sont enregistrées dans le magasin clé/valeur (KV) de Consul, un emplacement commun accessible à tous les services lors de leur déploiement. Le magasin KV de Consul n’est généralement pas destiné à stocker ce type de données, mais ce tutoriel l’utilise par souci de simplicité.
- Remplacez le contenu de infrastructure/messenger-deploy.sh (créé à l’étape 2 de la section précédente) par ce qui suit :
#!/bin/bashset -e
# This configuration requires a new commit to change
NODE_ENV=production
PORT=4000
JSON_BODY_LIMIT=100kb
# Postgres database configuration by pulling information from
# the system
POSTGRES_USER=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-application-user?raw=true)
PGPORT=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-port?raw=true)
PGHOST=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-host?raw=true)
# RabbitMQ configuration by pulling from the system
AMQPHOST=$(curl -X GET http://localhost:8500/v1/kv/amqp-host?raw=true)
AMQPPORT=$(curl -X GET http://localhost:8500/v1/kv/amqp-port?raw=true)
docker run \
--rm \
-e NODE_ENV="${NODE_ENV}" \
-e PORT="${PORT}" \
-e JSON_BODY_LIMIT="${JSON_BODY_LIMIT}" \
-e PGUSER="${POSTGRES_USER}" \
-e PGPORT="${PGPORT}" \
-e PGHOST="${PGHOST}" \
-e AMQPPORT="${AMQPPORT}" \
-e AMQPHOST="${AMQPHOST}" \
messenger- Ce script illustre deux types de configuration :
- Configuration spécifiée directement dans le script de déploiement – Elle définit l’environnement de déploiement (
NODE_ENV) et le port (PORT), et fait passerJSON_BODY_LIMITà 100 Ko, une valeur plus réaliste que 20 octets. - Configuration récupérée auprès de sources externes – Ce script récupère les valeurs des variables d’environnement
POSTGRES_USER,PGPORT,PGHOST,AMQPHOSTetAMQPPORTdans le magasin KV de Consul. Vous définissez les valeurs des variables d’environnement dans le magasin KV de Consul en suivant les deux étapes ci-dessous.
- Configuration spécifiée directement dans le script de déploiement – Elle définit l’environnement de déploiement (
- Ouvrez « messenger-db-deploy.sh » et ajoutez ce qui suit pour créer un script de déploiement initial pour « messenger-database » :
#!/bin/bashset -e
PORT=5432
POSTGRES_USER=postgres
docker run \
-d \
--rm \
--name messenger-db \
-v db-data:/var/lib/postgresql/data/pgdata \
-e POSTGRES_USER="${POSTGRES_USER}" \
-e POSTGRES_PASSWORD="${POSTGRES_PASSWORD}" \
-e PGPORT="${PORT}" \
-e PGDATA=/var/lib/postgresql/data/pgdata \
--network mm_2023 \
postgres:15.1
# Register details about the database with Consul
curl -X PUT http://localhost:8500/v1/kv/messenger-db-port \
-H "Content-Type: application/json" \
-d "${PORT}"
curl -X PUT http://localhost:8500/v1/kv/messenger-db-host \
-H "Content-Type: application/json" \
-d 'messenger-db' # This matches the "--name" flag above
# (the hostname)
curl -X PUT http://localhost:8500/v1/kv/messenger-db-application-user \
-H "Content-Type: application/json" \
-d "${POSTGRES_USER}"- En plus de définir une configuration que le service messenger peut interroger au moment du déploiement, le script illustre les deux mêmes concepts que le script initial du service messenger présenté dans « Créer les scripts de déploiement initiaux ») :
- Il spécifie certains paramètres de configuration directement dans le script de déploiement, en l’occurrence le port sur lequel la base de données PostgreSQL doit s’exécuter et le nom d’utilisateur de l’utilisateur par défaut.
- Il exécute Docker avec l’option «
-e» afin d’injecter des variables d’environnement dans le conteneur lors de l’exécution. Il définit également le nom du conteneur en cours d’exécution sur « messenger-db », qui devient le nom d’hôte de la base de données sur le réseau Docker que vous avez créé lorsque vous avez lancé le service « platform » à l’étape 2 de la section « Configuration ».
- Dans un déploiement réel, c’est généralement l’équipe Plateforme (ou une équipe similaire) qui se charge du déploiement et de la maintenance d’un service tel que RabbitMQ dans le dépôt platform, comme vous le faites pour messenger-database dans le dépôt messenger. L’équipe Plateforme veille ensuite à ce que l’emplacement de cette infrastructure puisse être découvert par les services qui en dépendent. Pour les besoins de ce tutoriel, définissez vous-même les valeurs de RabbitMQ :
curl -X PUT --silent --output /dev/null --show-error --fail \ -H "Content-Type: application/json" \ -d "rabbitmq" \ http://localhost:8500/v1/kv/amqp-host curl -X PUT --silent --output /dev/null --show-error --fail \ -H "Content-Type: application/json" \ -d "5672" \ http://localhost:8500/v1/kv/amqp-port- (Vous vous demandez peut-être pourquoi
amqpest utilisé pour définir les variables RabbitMQ : c’est parce que AMQP est le protocole utilisé par RabbitMQ.)
Définir un secret dans les scripts de déploiement
Il ne manque qu’une seule donnée (critique) dans les scripts de déploiement du service messenger : le mot de passe de la base de données messenger-database !
Remarque : La gestion des secrets n’est pas l’objet de ce tutoriel. Par souci de simplicité, le secret est donc défini dans les fichiers de déploiement. Ne faites jamais cela dans un environnement réel – qu’il s’agisse d’un environnement de développement, de test ou de production – car cela crée un risque de sécurité majeur.
Pour en savoir plus sur la gestion appropriée des secrets, consultez l’unité 2, « Les fondamentaux de la gestion des secrets dans les microservices » de Microservices March 2023. (Attention, spoiler : un outil de gestion des secrets est la seule méthode véritablement sûre pour stocker des secrets).
- Remplacez le contenu de infrastructure/messenger-db-deploy.sh par ce qui suit afin de stocker le mot de passe secret de la base de données messenger-database dans le magasin Consul KV :
#!/bin/bashset -e
PORT=5432
POSTGRES_USER=postgres
# NOTE: Never do this in a real-world deployment. Store passwords
# only in an encrypted secrets store.
POSTGRES_PASSWORD=postgres
docker run \
--rm \
--name messenger-db-primary \
-d \
-v db-data:/var/lib/postgresql/data/pgdata \
-e POSTGRES_USER="${POSTGRES_USER}" \
-e POSTGRES_PASSWORD="${POSTGRES_PASSWORD}" \
-e PGPORT="${PORT}" \
-e PGDATA=/var/lib/postgresql/data/pgdata \
--network mm_2023 \
postgres:15.1
echo "Register key messenger-db-port\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-port \
-H "Content-Type: application/json" \
-d "${PORT}"
echo "Register key messenger-db-host\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-host \
-H "Content-Type: application/json" \
-d 'messenger-db-primary' # This matches the "--name" flag above
# which for our setup means the hostname
echo "Register key messenger-db-application-user\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-application-user \
-H "Content-Type: application/json" \
-d "${POSTGRES_USER}"
echo "Register key messenger-db-password-never-do-this\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-password-never-do-this \
-H "Content-Type: application/json" \
-d "${POSTGRES_PASSWORD}"
printf "\nDone registering postgres details with Consul\n"- Remplacez le contenu de infrastructure/messenger-deploy.sh par ce qui suit afin de récupérer le secret du mot de passe de messenger-database depuis le magasin KV de Consul :
#!/bin/bashset -e
# This configuration requires a new commit to change
NODE_ENV=production
PORT=4000
JSON_BODY_LIMIT=100kb
# Postgres database configuration by pulling information from
# the system
POSTGRES_USER=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-application-user?raw=true)
PGPORT=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-port?raw=true)
PGHOST=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-host?raw=true)
# NOTE: Never do this in a real-world deployment. Store passwords
# only in an encrypted secrets store.
PGPASSWORD=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-password-never-do-this?raw=true)
# RabbitMQ configuration by pulling from the system
AMQPHOST=$(curl -X GET http://localhost:8500/v1/kv/amqp-host?raw=true)
AMQPPORT=$(curl -X GET http://localhost:8500/v1/kv/amqp-port?raw=true)
docker run \
--rm \
-d \
-e NODE_ENV="${NODE_ENV}" \
-e PORT="${PORT}" \
-e JSON_BODY_LIMIT="${JSON_BODY_LIMIT}" \
-e PGUSER="${POSTGRES_USER}" \
-e PGPORT="${PGPORT}" \
-e PGHOST="${PGHOST}" \
-e PGPASSWORD="${PGPASSWORD}" \
-e AMQPPORT="${AMQPPORT}" \
-e AMQPHOST="${AMQPHOST}" \
--network mm_2023 \
messengerExécuter les scripts de déploiement
- Accédez au répertoire app du dépôt messenger et créez l’image Docker du service messenger :
cd ../appdocker build -t messenger .- Vérifiez que seuls les conteneurs appartenant au service « platform » sont en cours d’exécution :
docker ps --format '{{.Names}}'consul-server
consul-client
rabbitmq- Accédez à la racine du dépôt « messenger », puis déployez la base de données « messenger-database » et le service « messenger » :
cd .../infrastructure/messenger-db-deploy.sh ./infrastructure/messenger-deploy.sh- Le script messenger-db-deploy.sh démarre la messenger-database et enregistre les informations appropriées dans le système (qui, dans ce cas, est le magasin Consul KV). Le script messenger-deploy.sh démarre ensuite l’application et récupère dans le système la configuration enregistrée par messenger-db-deploy.sh (là encore, le magasin Consul KV). Conseil : si un conteneur ne démarre pas, supprimez le deuxième paramètre de la commande
dockerrun(la ligne-d\) dans le script de déploiement, puis exécutez à nouveau le script. Le conteneur démarre alors au premier plan, ce qui signifie que ses journaux s’affichent dans le terminal et peuvent aider à identifier le problème. Une fois le problème résolu, rétablissez la ligne-d\afin que le conteneur proprement dit s’exécute en arrière-plan. - Envoyez une simple requête de contrôle d’intégrité à l’application pour vérifier que le déploiement a réussi :
curl localhost:4000/healthcurl: (7) Failed to connect to localhost port 4000 after 11 ms: Connection refused- Oups, échec ! Il s’avère qu’il vous manque encore un élément de configuration essentiel et que le service messenger n’est pas accessible au reste du système. Il fonctionne parfaitement au sein du réseau mm_2023, mais ce réseau n’est accessible que depuis Docker.
- Arrêtez le conteneur en cours d’exécution afin de préparer la création d’une nouvelle image dans le prochain exercice :
docker rm $(docker stop $(docker ps -a -q --filter ancestor=messenger --format="{{.ID}}"))Défi 3 : exposer un service au monde extérieur
Dans un environnement de production, on n’expose généralement pas les services directement. On suit plutôt un modèle courant d’architecture de microservices, qui consiste à placer un service de proxy inverse devant le service principal.
Dans ce défi, vous exposez le service messenger au monde extérieur en mettant en place la découverte de services, qui consiste à enregistrer les informations relatives aux nouveaux services et à les mettre à jour dynamiquement lorsque d’autres services y accèdent. Pour ce faire, vous utilisez les technologies suivantes :
- Consul, un registre de services dynamique, et Consul template, un outil permettant de mettre à jour dynamiquement un fichier à partir des données de Consul
- NGINX Open Source, en tant que proxy inverse et équilibreur de charge fournissant un point d’entrée unique pour votre service messenger, qui sera composé de plusieurs instances distinctes de l’application exécutées dans des conteneurs
Pour en savoir plus sur la découverte de services, consultez l’article « Rendre un service disponible en tant que configuration » dans « Bonnes pratiques de configuration des applications de microservices » sur notre blog.
Configurer Consul
Le fichier app/consul/index.mjs du dépôt messenger contient tout le code nécessaire pour enregistrer le service messenger auprès de Consul au démarrage et le désenregistrer lors d’un arrêt normal. Il expose une fonction, register, qui enregistre tout service nouvellement déployé dans le registre de services de Consul.
- Dans votre éditeur de texte préféré, ouvrez app/index.mjs et ajoutez l’extrait suivant après les autres instructions
import, afin d’importer la fonctionregisterdepuis app/consul/index.mjs :
import { register as registerConsul } from "./consul/index.mjs";- Modifiez ensuite la section «
SERVERSTART» à la fin du script, comme indiqué, afin d’appelerregisterConsul()après le démarrage de l’application :
/* ================= SERVER START
================== */
app.listen(port, async () => {
console.log(`messenger_service listening on port ${port}`);
registerConsul();
});
export default app;- Ouvrez le schéma
convictdans app/config/config.mjs et ajoutez les valeurs de configuration suivantes après la cléjsonBodyLimitque vous avez ajoutée à l’étape 1 de l’exemple 2.
consulServiceName: { doc: "The name by which the service is registered in Consul. If not specified, the service is not registered",
format: "*",
default: null,
env: "CONSUL_SERVICE_NAME",
},
consulHost: {
doc: "The host where the Consul client runs",
format: String,
default: "consul-client",
env: "CONSUL_HOST",
},
consulPort: {
doc: "The port for the Consul client",
format: "port",
default: 8500,
env: "CONSUL_PORT",
},- Cela configure le nom sous lequel un nouveau service est enregistré et définit le nom d’hôte et le port du client Consul. À l’étape suivante, vous modifierez le script de déploiement du service messenger afin d’y inclure ces nouvelles informations de connexion à Consul et d’enregistrement du service.
- Ouvrez infrastructure/messenger-deploy.sh et remplacez son contenu par ce qui suit afin d’inclure dans la configuration du service messenger les informations de connexion à Consul et d’enregistrement du service que vous avez définies à l’étape précédente :
#!/bin/bashset -e
# This configuration requires a new commit to change
NODE_ENV=production
PORT=4000
JSON_BODY_LIMIT=100kb
CONSUL_SERVICE_NAME="messenger"
# Consul host and port are included in each host since we
# cannot query Consul until we know them
CONSUL_HOST="${CONSUL_HOST}"
CONSUL_PORT="${CONSUL_PORT}"
# Postgres database configuration by pulling information from
# the system
POSTGRES_USER=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-application-user?raw=true")
PGPORT=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-port?raw=true")
PGHOST=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-host?raw=true")
# NOTE: Never do this in a real-world deployment. Store passwords
# only in an encrypted secrets store.
PGPASSWORD=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-password-never-do-this?raw=true")
# RabbitMQ configuration by pulling from the system
AMQPHOST=$(curl -X GET "http://localhost:8500/v1/kv/amqp-host?raw=true")
AMQPPORT=$(curl -X GET "http://localhost:8500/v1/kv/amqp-port?raw=true")
docker run \
--rm \
-d \
-e NODE_ENV="${NODE_ENV}" \
-e PORT="${PORT}" \
-e JSON_BODY_LIMIT="${JSON_BODY_LIMIT}" \
-e PGUSER="${POSTGRES_USER}" \
-e PGPORT="${PGPORT}" \
-e PGHOST="${PGHOST}" \
-e PGPASSWORD="${PGPASSWORD}" \
-e AMQPPORT="${AMQPPORT}" \
-e AMQPHOST="${AMQPHOST}" \
-e CONSUL_HOST="${CONSUL_HOST}" \
-e CONSUL_PORT="${CONSUL_PORT}" \
-e CONSUL_SERVICE_NAME="${CONSUL_SERVICE_NAME}" \
--network mm_2023 \
messenger- Les principaux points à retenir sont les suivants : Remarque : Dans un déploiement réel, il s’agit d’un exemple de configuration qui doit faire l’objet d’un accord entre les équipes : l’équipe responsable de Consul doit fournir les variables d’environnement
CONSUL_HOSTetCONSUL_PORTdans tous les environnements, car un service ne peut pas interroger Consul sans ces informations de connexion.- La variable d’environnement «
CONSUL_SERVICE_NAME» indique à l’instance du service messenger sous quel nom s’enregistrer auprès de Consul. - Les variables d’environnement «
CONSUL_HOST» et «CONSUL_PORT» sont destinées au client Consul qui s’exécute à l’emplacement où le script de déploiement est lancé.
- La variable d’environnement «
- Dans le terminal de l’application, accédez au répertoire app, arrêtez toutes les instances en cours d’exécution du service messenger, puis reconstruisez l’image Docker afin d’y intégrer le nouveau code d’enregistrement du service :
cd appdocker rm $(docker stop $(docker ps -a -q --filter ancestor=messenger --format="{{.ID}}")) docker build -t messenger .- Accédez à http://localhost:8500 dans un navigateur pour voir l’interface utilisateur de Consul en action (même s’il ne se passe encore rien d’intéressant).
- À la racine du dépôt messenger, exécutez le script de déploiement pour démarrer une instance du service messenger :
CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-deploy.sh- Dans l’interface utilisateur de Consul affichée dans le navigateur, cliquez sur « Services » dans la barre d’en-tête pour vérifier qu’un seul service « messenger » est en cours d’exécution.

- Exécutez le script de déploiement plusieurs fois supplémentaires afin de démarrer davantage d’instances du service « messenger ». Vérifiez dans l’interface utilisateur de Consul qu’elles sont bien en cours d’exécution.
CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-deploy.shConfigurer NGINX
L’étape suivante consiste à ajouter NGINX Open Source en tant que proxy inverse et répartiteur de charge afin d’acheminer le trafic entrant vers toutes les instances messenger en cours d’exécution.
- Dans le terminal de l’application, accédez au répertoire racine du dépôt messenger et créez un répertoire nommé load-balancer ainsi que trois fichiers :
mkdir load-balancercd load-balancer touch nginx.ctmpl touch consul-template-config.hcl touch Dockerfile- Le fichier Dockerfile définit le conteneur dans lequel s’exécutent NGINX et Consul Template. Consul Template utilise les deux autres fichiers pour mettre à jour dynamiquement les serveurs en amont de NGINX lorsque le service messenger change (des instances du service démarrent ou s’arrêtent) dans son registre de services.
- Ouvrez le fichier nginx.ctmpl créé à l’étape 1 et ajoutez l’extrait de configuration NGINX suivant, que Consul Template utilise pour mettre à jour dynamiquement le groupe de serveurs en amont NGINX :
upstream messenger_service { {{- range service "messenger" }}
server {{ .Address }}:{{ .Port }};
{{- end }}
}
server {
listen 8085;
server_name localhost;
location / {
proxy_pass http://messenger_service;
add_header Upstream-Host $upstream_addr;
}
}- Cet extrait ajoute l’adresse IP et le numéro de port de chaque instance du service messenger enregistrée auprès de Consul au groupe de serveurs en amont NGINX messenger_service. NGINX transmet les requêtes entrantes à l’ensemble d’instances de service en amont défini dynamiquement.
- Ouvrez le fichier consul-template-config.hcl créé à l’étape 1 et ajoutez la configuration suivante :
consul { address = "consul-client:8500"
retry {
enabled = true
attempts = 12
backoff = "250ms"
}
}
template {
source = "/usr/templates/nginx.ctmpl"
destination = "/etc/nginx/conf.d/default.conf"
perms = 0600
command = "if [ -e /var/run/nginx.pid ]; then nginx -s reload; else nginx; fi"
}- Cette configuration de Consul Template lui indique de générer à nouveau le modèle
source(l’extrait de configuration NGINX créé à l’étape précédente), de le placer à l’emplacementdestinationspécifié, puis d’exécuter lacommandindiquée (qui demande à NGINX de recharger sa configuration). En pratique, cela signifie qu’un nouveau fichier default.conf est créé chaque fois qu’une instance de service est enregistrée, mise à jour ou désenregistrée dans Consul. NGINX recharge ensuite sa configuration sans interruption de service, ce qui lui garantit de disposer d’un ensemble à jour de serveurs opérationnels (les instances du service messenger) vers lesquels il peut acheminer le trafic. - Ouvrez le fichier Dockerfile créé à l’étape 1 et ajoutez le contenu suivant, qui permet de créer le service NGINX. (Il n’est pas nécessaire de comprendre le Dockerfile pour suivre ce tutoriel, mais le code est commenté directement pour vous aider.)
FROM nginx:1.23.1
ARG CONSUL_TEMPLATE_VERSION=0.30.0
# Set an environment variable for the location of the Consul
# cluster. By default, it tries to resolve to consul-client:8500
# which is the behavior if Consul is running as a container in the
# same host and linked to this NGINX container (with the alias
# consul, of course). But this environment variable can also be
# overridden as the container starts if we want to resolve to
# another address.
ENV CONSUL_URL consul-client:8500
# Download the specified version of Consul template
ADD https://releases.hashicorp.com/consul-template/${CONSUL_TEMPLATE_VERSION}/consul-template_${CONSUL_TEMPLATE_VERSION}_linux_amd64.zip /tmp
RUN apt-get update \
&& apt-get install -y --no-install-recommends dumb-init unzip \
&& unzip /tmp/consul-template_${CONSUL_TEMPLATE_VERSION}_linux_amd64.zip -d /usr/local/bin \
&& rm -rf /tmp/consul-template_${CONSUL_TEMPLATE_VERSION}_linux_amd64.zip
COPY consul-template-config.hcl ./consul-template-config.hcl
COPY nginx.ctmpl /usr/templates/nginx.ctmpl
EXPOSE 8085
STOPSIGNAL SIGQUIT
CMD ["dumb-init", "consul-template", "-config=consul-template-config.hcl"]- Créez une image Docker :
docker build -t messenger-lb .- Accédez à la racine du répertoire messenger, puis créez un fichier nommé messenger-load-balancer-deploy.sh qui servira de fichier de déploiement pour le service NGINX (comme pour les autres services que vous avez déployés tout au long de ce tutoriel). Selon votre environnement, vous devrez peut-être faire précéder la commande
chmoddesudo:
cd .. touch infrastructure/messenger-load-balancer-deploy.sh chmod +x infrastructure/messenger-load-balancer-deploy.sh- Ouvrez « messenger-load-balancer-deploy.sh » et ajoutez le contenu suivant :
#!/bin/bashset -e
# Consul host and port are included in each host since we
# cannot query Consul until we know them
CONSUL_HOST="${CONSUL_HOST}"
CONSUL_PORT="${CONSUL_PORT}"
docker run \
--rm \
-d \
--name messenger-lb \
-e CONSUL_URL="${CONSUL_HOST}:${CONSUL_PORT}" \
-p 8085:8085 \
--network mm_2023 \
messenger-lb- Maintenant que tout est en place, déployez le service NGINX :
CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-load-balancer-deploy.sh- Vérifiez si vous pouvez accéder au service messenger depuis l’extérieur :
curl -X GET http://localhost:8085/health
OK- Ça fonctionne ! NGINX répartit désormais la charge entre toutes les instances créées du service messenger. Vous pouvez le constater, car l’en-tête
X-Forwarded-Foraffiche les mêmes adresses IP du service messenger que celles indiquées dans l’interface utilisateur de Consul à l’étape 8 de la section précédente.
Défi 4 : migrer une base de données en utilisant un service comme exécuteur de tâches
Les applications de grande envergure font souvent appel à des « exécuteurs de tâches », qui utilisent de petits processus de travail pour effectuer des opérations ponctuelles, comme modifier des données (par exemple, Sidekiq et Celery). Ces outils nécessitent souvent une infrastructure supplémentaire, telle que Redis ou RabbitMQ. Dans ce cas, vous utilisez le service messenger lui-même comme « exécuteur de tâches » pour effectuer des opérations ponctuelles. Cette approche est pertinente, car ce service est déjà très léger, parfaitement capable d’interagir avec la base de données et les autres composants d’infrastructure dont il dépend, et s’exécute de manière totalement indépendante de l’application qui traite le trafic.
Cette approche présente trois avantages :
- L’exécuteur de tâches (y compris les scripts qu’il exécute) est soumis exactement aux mêmes contrôles et au même processus de révision que le service de production.
- Les valeurs de configuration, telles que les utilisateurs de la base de données, peuvent facilement être modifiées afin de renforcer la sécurité du déploiement en production. Par exemple, vous pouvez exécuter le service de production avec un utilisateur disposant de « faibles privilèges », qui peut uniquement écrire dans les tables existantes et les interroger. Vous pouvez configurer une autre instance du service pour modifier la structure de la base de données avec un utilisateur disposant de privilèges plus élevés, capable de créer et de supprimer des tables.
- Certaines équipes exécutent des tâches sur des instances qui gèrent également le trafic de production du service. Cette pratique est dangereuse, car les problèmes liés à une tâche peuvent affecter les autres fonctions assurées par l’application dans le conteneur. Après tout, c’est justement pour éviter ce genre de situation que nous utilisons des microservices, n’est-ce pas ?
Dans ce défi, vous découvrez comment un artefact peut être modifié pour remplir un nouveau rôle en changeant certaines valeurs de configuration de la base de données et en migrant la base de données messenger afin qu’elle utilise les nouvelles valeurs, puis en testant ses performances.
Migrer la base de données messenger
Pour un déploiement réel en production, vous pouvez créer deux utilisateurs distincts disposant d’autorisations différentes : un « utilisateur de l’application » et un « utilisateur de migration ». Par souci de simplicité, dans cet exemple, vous utilisez l’utilisateur par défaut comme utilisateur de l’application et créez un utilisateur de migration doté de privilèges de superutilisateur. Dans une situation réelle, il est préférable de prendre davantage de temps pour déterminer précisément les autorisations minimales dont chaque utilisateur a besoin en fonction de son rôle.
- Dans le terminal de l’application, créez un nouvel utilisateur PostgreSQL disposant des privilèges de superutilisateur :
echo "CREATE USER messenger_migrator WITH SUPERUSER PASSWORD 'migrator_password';" | docker exec -i messenger-db-primary psql -U postgres- Ouvrez le script de déploiement de la base de données (infrastructure/messenger-db-deploy.sh) et remplacez son contenu afin d’ajouter les identifiants du nouvel utilisateur. Remarque : rappelons-le : dans le cadre d’un déploiement réel, ne placez JAMAIS de secrets, tels que des identifiants de base de données, dans un script de déploiement ni ailleurs que dans un outil de gestion des secrets. Pour plus de détails, consultez Unité 2 : Les fondamentaux de la gestion des secrets des microservices de Microservices March 2023.
#!/bin/bash
set -e
PORT=5432
POSTGRES_USER=postgres
# NOTE: Never do this in a real-world deployment. Store passwords
# only in an encrypted secrets store.
# Because we’re focusing on other concepts in this tutorial, we
# set the password this way here for convenience.
POSTGRES_PASSWORD=postgres
# Migration user
POSTGRES_MIGRATOR_USER=messenger_migrator
# NOTE: As above, never do this in a real deployment.
POSTGRES_MIGRATOR_PASSWORD=migrator_password
docker run \
--rm \
--name messenger-db-primary \
-d \
-v db-data:/var/lib/postgresql/data/pgdata \
-e POSTGRES_USER="${POSTGRES_USER}" \
-e POSTGRES_PASSWORD="${POSTGRES_PASSWORD}" \
-e PGPORT="${PORT}" \
-e PGDATA=/var/lib/postgresql/data/pgdata \
--network mm_2023 \
postgres:15.1
echo "Register key messenger-db-port\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-port \
-H "Content-Type: application/json" \
-d "${PORT}"
echo "Register key messenger-db-host\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-host \
-H "Content-Type: application/json" \
-d 'messenger-db-primary' # This matches the "--name" flag above
# which for our setup means the hostname
echo "Register key messenger-db-application-user\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-application-user \
-H "Content-Type: application/json" \
-d "${POSTGRES_USER}"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-password-never-do-this \
-H "Content-Type: application/json" \
-d "${POSTGRES_PASSWORD}"
echo "Register key messenger-db-application-user\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-migrator-user \
-H "Content-Type: application/json" \
-d "${POSTGRES_MIGRATOR_USER}"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-migrator-password-never-do-this \
-H "Content-Type: application/json" \
-d "${POSTGRES_MIGRATOR_PASSWORD}"
printf "\nDone registering postgres details with Consul\n"- Cette modification ajoute simplement l’utilisateur de migration à l’ensemble des utilisateurs configurés dans Consul après le déploiement de la base de données.
- Créez un nouveau fichier dans le répertoire infrastructure nommé messenger-db-migrator-deploy.sh (là encore, vous devrez peut-être faire précéder la commande
chmoddesudo) :
touch infrastructure/messenger-db-migrator-deploy.shchmod +x infrastructure/messenger-db-migrator-deploy.sh- Ouvrez « messenger-db-migrator-deploy.sh » et ajoutez ce qui suit :
#!/bin/bashset -e
# This configuration requires a new commit to change
NODE_ENV=production
PORT=4000
JSON_BODY_LIMIT=100kb
CONSUL_SERVICE_NAME="messenger-migrator"
# Consul host and port are included in each host since we
# cannot query Consul until we know them
CONSUL_HOST="${CONSUL_HOST}"
CONSUL_PORT="${CONSUL_PORT}"
# Get the migrator user name and password
POSTGRES_USER=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-migrator-user?raw=true")
PGPORT=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-port?raw=true")
PGHOST=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-host?raw=true)
# NOTE: Never do this in a real-world deployment. Store passwords
# only in an encrypted secrets store.
PGPASSWORD=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-migrator-password-never-do-this?raw=true")
# RabbitMQ configuration by pulling from the system
AMQPHOST=$(curl -X GET "http://localhost:8500/v1/kv/amqp-host?raw=true")
AMQPPORT=$(curl -X GET "http://localhost:8500/v1/kv/amqp-port?raw=true")
docker run \--rm \
-d \
--name messenger-migrator \
-e NODE_ENV="${NODE_ENV}" \
-e PORT="${PORT}" \
-e JSON_BODY_LIMIT="${JSON_BODY_LIMIT}" \
-e PGUSER="${POSTGRES_USER}" \
-e PGPORT="${PGPORT}" \
-e PGHOST="${PGHOST}" \
-e PGPASSWORD="${PGPASSWORD}" \
-e AMQPPORT="${AMQPPORT}" \
-e AMQPHOST="${AMQPHOST}" \
-e CONSUL_HOST="${CONSUL_HOST}" \
-e CONSUL_PORT="${CONSUL_PORT}" \
-e CONSUL_SERVICE_NAME="${CONSUL_SERVICE_NAME}" \
--network mm_2023 \
messenger- Ce script est assez similaire au script infrastructure/messenger-deploy.sh dans sa forme finale, que vous avez créé à l’étape 3 de la section Configurer Consul. La principale différence est que
CONSUL_SERVICE_NAMEvautmessenger-migratorau lieu demessenger, et quePGUSERcorrespond au superutilisateur « migrator » que vous avez créé à l’étape 1 ci-dessus. Il est important queCONSUL_SERVICE_NAMEvaillemessenger-migrator. S’il était défini surmessenger, NGINX intégrerait automatiquement ce service à la rotation pour recevoir les appels d’API, alors qu’il n’est pas destiné à traiter du trafic. Le script déploie une instance de courte durée dans le rôle de migrateur. Cela évite que d’éventuels problèmes liés à la migration n’affectent le traitement du trafic par les principales instances du service messenger. - Redéployez la base de données PostgreSQL. Comme vous utilisez des scripts
bashdans ce tutoriel, vous devez arrêter et redémarrer le service de base de données. Dans une application de production, il suffit généralement d’exécuter un script d’infrastructure en tant que code afin d’ajouter uniquement les éléments qui ont changé.
docker stop messenger-db-primaryCONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-db-deploy.sh- Déployez le service de migration de la base de données PostgreSQL :
CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-db-migrator-deploy.sh- Vérifiez que l’instance fonctionne comme prévu :
docker ps --format "{{.Names}}"
...
messenger-migrator- Vous pouvez également vérifier dans l’interface utilisateur de Consul que le service de migration de la base de données s’est correctement enregistré auprès de Consul sous le nom messenger-migrator (là encore, il ne s’enregistre pas sous le nom messenger car il ne gère aucun trafic) :

- Passons maintenant à la dernière étape : exécutez les scripts de migration de la base de données ! Ces scripts ne ressemblent pas à de véritables scripts de migration de base de données, mais ils utilisent bien le service messenger-migrator pour exécuter des scripts propres à la base de données. Une fois la base de données migrée, arrêtez le service messenger-migrator :
docker exec -i -e PGDATABASE=postgres -e CREATE_DB_NAME=messenger messenger-migrator node scripts/create-db.mjsdocker exec -i messenger-migrator node scripts/create-schema.mjs docker exec -i messenger-migrator node scripts/create-seed-data.mjs docker stop messenger-migratorTestez le service messenger en action
Maintenant que vous avez migré la base de données messenger vers son format définitif, le service messenger est enfin prêt à être testé en conditions réelles ! Pour ce faire, exécutez quelques requêtes curl simples sur le service NGINX (vous avez configuré NGINX comme point d’entrée du système dans la section « Configurer NGINX »).
Certaines des commandes suivantes utilisent la bibliothèque jq library pour mettre en forme la sortie JSON. Vous pouvez l’installer si nécessaire ou ne pas l’inclure dans la ligne de commande si vous le souhaitez.
- Créez une conversation :
curl -d '{"participant_ids": [1, 2]}' -H "Content-Type: application/json" -X POST 'http://localhost:8085/conversations'{
"conversation": { "id": "1", "inserted_at": "YYYY-MM-DDT06:41:59.000Z" }
}- Envoyez un message dans la conversation de la part d’un utilisateur dont l’ID est 1 :
curl -d '{"content": "This is the first message"}' -H "User-Id: 1" -H "Content-Type: application/json" -X POST 'http://localhost:8085/conversations/1/messages' | jq{
"message": {
"id": "1",
"content": "This is the first message",
"index": 1,
"user_id": 1,
"username": "James Blanderphone",
"conversation_id": 1,
"inserted_at": "YYYY-MM-DDT06:42:15.000Z"
}
}- Répondez avec un message d’un autre utilisateur (dont l’ID est 2) :
curl -d '{"content": "This is the second message"}' -H "User-Id: 2" -H "Content-Type: application/json" -X POST 'http://localhost:8085/conversations/1/messages' | jq{
"message": {
"id": "2",
"content": "This is the second message",
"index": 2,
"user_id": 2,
"username": "Normalavian Ropetoter",
"conversation_id": 1,
"inserted_at": "YYYY-MM-DDT06:42:25.000Z"
}
}- Récupérez les messages :
curl -X GET 'http://localhost:8085/conversations/1/messages' | jq{
"messages": [
{
"id": "1",
"content": "This is the first message",
"user_id": "1",
"channel_id": "1",
"index": "1",
"inserted_at": "YYYY-MM-DDT06:42:15.000Z",
"username": "James Blanderphone"
},
{
"id": "2",
"content": "This is the second message",
"user_id": "2",
"channel_id": "1",
"index": "2",
"inserted_at": "YYYY-MM-DDT06:42:25.000Z",
"username": "Normalavian Ropetoter"
}
]
}Nettoyage
Vous avez créé un grand nombre de conteneurs et d’images tout au long de ce tutoriel ! Utilisez les commandes suivantes pour supprimer les conteneurs et images Docker que vous ne souhaitez pas conserver.
- Pour supprimer tous les conteneurs Docker en cours d’exécution :
docker rm $(docker stop $(docker ps -a -q --filter ancestor=messenger --format="{{.ID}}"))docker rm $(docker stop messenger-db-primary) docker rm $(docker stop messenger-lb)- Pour supprimer les services de la plateforme :
# From the platform repositorydocker compose down- Pour supprimer toutes les images Docker utilisées tout au long du tutoriel :
docker rmi messengerdocker rmi messenger-lb docker rmi postgres:15.1 docker rmi hashicorp/consul:1.14.4 docker rmi rabbitmq:3.11.4-management-alpineÉtapes suivantes
Vous vous dites peut-être : « Cela semble demander beaucoup de travail pour mettre en place quelque chose de simple » — et vous avez raison ! L’adoption d’une architecture axée sur les microservices exige une grande rigueur dans la structuration et la configuration des services. Malgré toute cette complexité, vous avez bien avancé :
- Vous avez mis en place une configuration axée sur les microservices, facilement compréhensible par les autres équipes.
- Vous configurez le système de microservices de manière à offrir une certaine flexibilité, tant en matière de mise à l’échelle que d’utilisation des différents services concernés.
Pour approfondir vos connaissances sur les microservices, consultez Microservices March 2023. Unité 2 : les fondamentaux de la gestion des secrets dans les microservices, propose une présentation approfondie mais accessible de la gestion des secrets dans les environnements de microservices.

À propos de l’auteur
Articles de blog associés

Tendances actuelles des technologies cloud natives, de l’ingénierie de plateforme et de l’IA
Découvrez les principaux enseignements tirés des réponses à l’enquête annuelle F5 NGINX sur les dernières tendances en matière de technologies cloud natives, de sécurité, d’ingénierie de plateforme et d’IA.
Quels contrôleurs Ingress NGINX sont affectés par les vulnérabilités CVE-2022-4886, CVE-2023-5043 et CVE-2023-5044 ?
Lorsque de nouvelles CVE sont signalées, il est facile de ne plus savoir si elles concernent un outil de contrôleur Ingress NGINX particulier, car il existe plusieurs projets. Cet article présente les CVE et explique comment distinguer les différents projets de contrôleurs Ingress afin de dissiper toute confusion.
Votre API est-elle en temps réel ? Testez sa latence avec l’outil rtapi de NGINX
Votre API répond-elle aux requêtes en moins de 30 ms ? Si ce n’est pas le cas, il ne s’agit pas d’une API en temps réel offrant la faible latence exigée par la grande majorité des organisations. Mesurez la latence de votre API avec l’outil rtapi de NGINX et découvrez comment NGINX peut l’aider à offrir une réactivité en temps réel.

Un testeur d’expressions régulières pour NGINX et NGINX Plus
Le testeur d’expressions régulières (regex) pour NGINX et NGINX Plus vous évite de tâtonner en vous indiquant si une expression régulière utilisée dans un bloc location ou map correspond aux valeurs comme prévu. Il génère la configuration appropriée et affiche les valeurs des groupes de capture.
Caches partagés avec les clusters de cache NGINX Plus, partie 1
Optimisez la capacité de votre cache web en le répartissant entre plusieurs serveurs de cache web NGINX ou NGINX Plus. Découvrez comment procéder dans cet article.
Introduction aux microservices
Les microservices suscitent actuellement beaucoup d’intérêt. Cet article de blog est le premier d’une série de sept consacrée à la conception, au développement et au déploiement de microservices.
