BLOG | NGINX

Tutoriel NGINX : Comment déployer et configurer des microservices

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

Cet article est l'un des quatre tutoriels qui vous aident à mettre en pratique les concepts de Microservices de mars 2023 : Commencez à fournir des microservices :

 

Toutes les applications nécessitent une configuration, mais les considérations à prendre en compte lors de la configuration d'un microservice peuvent ne pas être les mêmes que pour 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 conseils applicables aux deux types d'applications, mais ces conseils peuvent être adaptés aux applications de microservices. En particulier, nous pouvons adapter la manière dont nous définissons la configuration du service, fournir la configuration à un service et rendre un service disponible en tant que valeur de configuration pour d’autres services qui peuvent en dépendre.

Pour une compréhension conceptuelle de la manière d’adapter le facteur 3 aux microservices, en particulier les meilleures pratiques pour les fichiers de configuration, les bases de données et la découverte de services, lisez Meilleures pratiques pour la configuration des applications de microservices sur notre blog. Cet article est une excellente façon de mettre ces connaissances en pratique.

Note: Notre intention dans ce tutoriel est d’illustrer certains concepts de base, et non de montrer la bonne façon de déployer des microservices en production. Bien qu'il utilise une véritable architecture de « microservices », il existe quelques réserves importantes :

  • Le tutoriel n'utilise pas de framework d'orchestration de conteneurs tel que Kubernetes ou Nomad. Cela vous permet d’en apprendre davantage sur les concepts des microservices sans vous perdre dans les spécificités d’un certain framework. Les modèles introduits ici sont portables sur un système exécutant l’un de ces frameworks.
  • Les services sont optimisés pour faciliter la compréhension plutôt que pour la rigueur de l'ingénierie logicielle. L’objectif est d’examiner le rôle d’un service dans le système et ses modèles de communication, et non les spécificités du code. Pour plus d'informations, consultez les fichiers README des services individuels.

Présentation du didacticiel

Ce didacticiel illustre comment les concepts du facteur 3 s'appliquent aux applications de microservices. Dans quatre défis, vous explorerez certains modèles de configuration de microservices courants et déploierez et configurerez un service à l'aide de ces modèles :

  • Dans les défis 1 et 2, vous explorez le premier modèle, qui concerne l'emplacement de la configuration d'une application de microservices. Il existe trois emplacements typiques :

    • Le code de l'application
    • Le script de déploiement de l'application
    • Sources externes accessibles par le script de déploiement
  • Dans le défi 3, vous configurez deux autres modèles : exposer l'application au monde extérieur via un NGINX en tant que proxy inverse et activer la découverte de services à l'aide de Consul.
  • Dans le défi 4, vous implémentez le modèle final : utiliser une instance de votre microservice comme « exécuteur de tâches » qui exécute une action ponctuelle différente de sa fonction habituelle (dans ce cas, émuler une migration de base de données).

Le tutoriel utilise quatre technologies :

  • Messenger – Une API de chat simple avec des capacités de stockage de messages, créée pour ce tutoriel
  • NGINX Open Source – Un point d’entrée vers le service de messagerie et le système dans son ensemble
  • Consul – Un registre de services dynamique et un magasin de clés-valeurs
  • RabbitMQ – Un courtier de messages open source populaire qui permet aux services de communiquer de manière asynchrone

Diagramme de topologie montrant NGINX Open Source et le modèle Consul exécutés ensemble dans un conteneur. Le modèle Consult communique avec le client Consult. NGINX Open Source est un proxy inverse pour le service de messagerie, qui stocke les données dans messenger_db et communique avec Rabbit MQ.

Regardez cette vidéo pour avoir un aperçu du tutoriel. Les étapes ne correspondent pas exactement à cet article, mais cela aide à comprendre les concepts.

Prérequis et configuration

Prérequis

Pour réaliser le didacticiel dans votre propre environnement, vous avez besoin de :

  • Un environnement compatible Linux/Unix
  • Connaissance de base de la ligne de commande Linux, JavaScript et bash (mais tout le code et les commandes sont fournis et expliqués, vous pouvez donc toujours réussir 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 espérons que les versions plus récentes de Node.js fonctionneront également.
    • Pour des informations détaillées sur l'installation de Node.js, consultez le fichier README dans le référentiel du service de messagerie . Vous pouvez également installer asdf pour obtenir exactement la même version Node.js utilisé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 didacticiel : Messenger (vous le téléchargerez dans la section suivante), NGINX Open Source , Consul et RabbitMQ .

Installation

  1. Démarrez une session de terminal (les instructions suivantes feront référence à cela comme le terminal d'application ).
  2. Dans votre répertoire personnel, créez le répertoire microservices-march et clonez-y les référentiels GitHub de ce tutoriel. (Vous pouvez également utiliser un nom de répertoire différent et adapter les instructions en conséquence.)

    Note: Tout au long du didacticiel, l'invite sur la ligne de commande Linux est omise, pour faciliter la copie et le collage 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
    
  3. Accédez au référentiel de la plateforme et démarrez Docker Compose :

    cd platformdocker compose up -d --build
    

    Cela démarre à la fois RabbitMQ et Consul, qui seront utilisés dans les défis ultérieurs.

    • L'indicateur -d indique à Docker Compose de se détacher des conteneurs lorsqu'ils ont démarré (sinon les conteneurs resteront attachés à votre terminal).
    • L'indicateur --build indique à Docker Compose de reconstruire toutes les images au lancement. Cela garantit que les images que vous exécutez restent à jour malgré toutes les modifications potentielles apportées aux fichiers.
  4. Accédez au référentiel Messenger et démarrez Docker Compose :

    cd ../messengerdocker compose up -d --build
    

    Cela démarre la base de données PostgreSQL pour le service de messagerie , que nous appellerons la base de données de messagerie pour le reste du didacticiel.

Défi 1 : Définir la configuration des microservices au niveau de l'application

Dans ce défi, vous définissez la configuration dans le premier des trois emplacements que nous examinerons dans le didacticiel : le niveau de l'application. ( Le défi 2 illustre les deuxième et troisième emplacements, les scripts de déploiement et les sources externes.)

L'application à douze facteurs exclut spécifiquement la configuration au niveau de l'application, car une telle configuration n'a pas besoin de changer entre différents environnements de déploiement (que l'application à douze facteurs appelle déployer ). Néanmoins, nous couvrons les trois types par souci d’exhaustivité : la manière dont vous traitez chaque catégorie lorsque vous développez, créez et déployez un service est différente.

Le service de messagerie est écrit en Node.js, avec le point d'entrée dans app/index.mjs dans le référentiel Messenger . Cette ligne du fichier :

application.use(express.json());

est un exemple de configuration au niveau de l’application. Il configure le framework Express pour 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 l’application à douze facteurs considère comme une « configuration ». Cependant, dans le logiciel, tout dépend de votre situation, n’est-ce pas ?

Dans les deux sections suivantes, vous modifiez cette ligne pour implémenter deux exemples de configuration au niveau de l’application.

Exemple 1

Dans cet exemple, vous définissez la taille maximale d’un corps de requête accepté par le service de messagerie . 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 décrit ci-dessus .

  1. Dans votre éditeur de texte préféré, ouvrez app/index.mjs et remplacez :

    application.use(express.json())
    

    avec:

    app.use(express.json({ limite : "20b" }));
    
  2. Dans le terminal de l'application (celui que vous avez utilisé lors de la configuration ), accédez au répertoire de l'application et démarrez le service de messagerie :

    cd app npm install node index.mjs messenger_service écoute sur le port 4000
    
  3. Démarrez une deuxième session de terminal distincte (dont les instructions suivantes appellent le terminal client ) et envoyez une requête POST au service de messagerie . Le message d'erreur indique que la demande a été traitée avec succès, car le corps de la demande était inférieur à la limite de 20 octets définie à l'étape 1, mais que le contenu de la charge utile JSON est incorrect :

    curl -d '{ "texte": "bonjour" }' -H "Contenu-Type: application/json" -X POST http://localhost:4000/conversations ... { "erreur": "La conversation doit avoir 2 utilisateurs uniques" }
    
  4. Envoyez un corps de message légèrement plus long (à nouveau dans le terminal client). Il y a beaucoup plus de résultats qu'à l'étape 3, y compris un message d'erreur qui indique 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: entité demandée trop grande"
    

Exemple 2

Cet exemple utilise convict , une bibliothèque qui vous permet de définir un « schéma » de configuration complet dans un seul fichier. Il illustre également deux lignes directrices du facteur 3 de l’application à douze facteurs :

  • Stocker la configuration dans des variables d’environnement – Vous modifiez l’application de sorte 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 pour les microservices. Si vous n'êtes pas familier avec ce concept, nous vous recommandons de prendre un moment pour le lire dans Bonnes pratiques pour la configuration d'applications de microservices sur notre blog.

L'exemple met également en place une « plomberie » dont vous profiterez dans le défi 2 : le script de déploiement de messagerie que vous créerez dans ce défi définit la variable d'environnement JSON_BODY_LIMIT que vous insérez dans le code de l'application ici, comme illustration de la configuration spécifiée dans un script de déploiement.

  1. Ouvrez le fichier de configuration du condamné , app/config/config.mjs , et ajoutez ce qui suit comme nouvelle clé après la clé amqpport :

    jsonBodyLimit : { doc : `La taille maximale (avec unité incluse) qui sera analysée par le
    middleware JSON. L'analyse des unités est effectuée par la bibliothèque 
    https://www.npmjs.com/package/bytes.
    ex : "100 ko" ,
    format : Chaîne,
    par défaut : null,
    env : "JSON_BODY_LIMIT",
    },
    

    La bibliothèque convict se charge d'analyser la variable d'environnement JSON_BODY_LIMIT lorsque vous l'utilisez pour définir la taille maximale du corps sur la ligne de commande à l'étape 3 ci-dessous :

    • Extrait la valeur de la variable d'environnement correcte
    • Vérifie le type de la variable ( String )
    • Permet d'y accéder dans l'application sous la clé jsonBodyLimit
  2. Dans app/index.mjs, remplacez :

    app.use(express.json({ limite : "20b" }));
    

    avec

    application.use(express.json({ limite : config.get("jsonBodyLimit") }));
    
  3. Dans le terminal d’application (où vous avez démarré le service de messagerie à l’étape 2 de l’exemple 1 ), appuyez sur Ctrl+c pour arrêter le service. Ensuite, redémarrez-le en utilisant la variable d’environnement JSON_BODY_LIMIT pour définir la taille maximale du corps à 27 octets :

    ^cJSON_BODY_LIMIT=27b index du nœud.mjs
    

    Il s’agit d’un exemple de modification de la méthode de configuration lorsque cela est logique pour votre cas d’utilisation : vous êtes passé du codage en dur d’une valeur (dans ce cas, une limite de taille) dans le code de l’application à sa définition avec une variable d’environnement, comme recommandé par l’application à douze facteurs.

    Comme mentionné ci-dessus, dans le défi 2, l'utilisation de la variable d'environnement JSON_BODY_LIMIT deviendra un exemple du deuxième emplacement de configuration, lorsque vous utilisez le script de déploiement du service de messagerie pour définir la variable d'environnement plutôt que de la définir sur la ligne de commande.

  4. Dans le terminal client, répétez la commande curl de l’étape 4 de l’exemple 1 (avec le corps de requête plus grand). Étant donné que vous avez maintenant augmenté la limite de taille à 27 octets, le corps de la requête ne dépasse plus la limite et vous obtenez le 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 '{ "texte": "bonjour, monde" }' -H "Contenu-Type: application/json" -X POST http://localhost:4000/conversations { "erreur": "La conversation doit avoir 2 utilisateurs uniques" }
    

    Vous pouvez fermer le terminal client si vous le souhaitez. Vous exécuterez toutes les commandes du reste du didacticiel dans le terminal de l’application.

  5. Dans le terminal de l’application, appuyez sur Ctrl+c pour arrêter le service de messagerie (vous avez arrêté et redémarré le service dans ce terminal à l’étape 3 ci-dessus).

    ^c
    
  6. Arrêtez la base de données du messager . Vous pouvez ignorer en toute sécurité le message d’erreur affiché, car le réseau est toujours utilisé par les éléments d’infrastructure définis dans le référentiel de la plateforme . Exécutez cette commande à la racine du référentiel Messenger .

    docker compose down ...échec de la suppression du réseau 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 peut-elle varier entre les déploiements ?) »
– Du facteur 3 de l’application à douze facteurs

À première vue, vous pourriez interpréter cela comme « ne pas enregistrer la configuration dans le contrôle de source ». Dans ce défi, vous implémentez un modèle commun pour les environnements de microservices qui peut sembler enfreindre cette règle, mais qui en réalité respecte la règle tout en fournissant des améliorations de processus précieuses qui sont essentielles pour les environnements de microservices.

Dans ce défi, vous créez des scripts de déploiement pour imiter la fonctionnalité de l'infrastructure en tant que code et des manifestes de déploiement qui fournissent la configuration à un microservice, modifiez les scripts pour utiliser des sources externes de configuration, définissez un secret , puis exécutez les scripts pour déployer les services et leur infrastructure.

Vous créez les scripts de déploiement dans un répertoire d’infrastructure nouvellement créé dans le référentiel Messenger . Un répertoire appelé infrastructure (ou une variante de ce nom) est un modèle courant dans les architectures de microservices modernes, utilisé pour stocker des éléments tels que :

Les avantages de ce modèle incluent :

  • Il attribue la propriété du déploiement du service et du déploiement de l’infrastructure spécifique au service (comme les bases de données) à l’équipe propriétaire du service.
  • L'équipe 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, etc.).
  • L’équipe peut facilement apporter des modifications à la manière dont le service et son infrastructure de support sont déployés sans dépendre d’équipes externes effectuant le travail pour elle.

Comme mentionné précédemment, notre intention pour ce didacticiel n’est pas de montrer comment configurer un système réel, et les scripts que vous déployez dans ce défi ne ressemblent pas à un véritable système de production. Ils illustrent plutôt certains concepts et problèmes de base résolus par une configuration spécifique à l’outil lors du déploiement d’une infrastructure liée aux microservices, tout en réduisant les scripts au minimum d’outils spécifiques possible.

Créer des scripts de déploiement initiaux

  1. Dans le terminal d'application, créez un répertoire d'infrastructure à la racine du référentiel Messenger et créez des fichiers pour contenir les scripts de déploiement pour le service Messenger et la base de données Messenger . Selon votre environnement, vous devrez peut-être préfixer les commandes chmod avec sudo :

    mkdir infrastructurecd infrastructure
    touch messenger-deploy.sh
    chmod +x messenger-deploy.sh
    touch messenger-db-deploy.sh
    chmod +x messenger-db-deploy.sh
    
  2. Dans votre éditeur de texte préféré, ouvrez messenger-deploy.sh et ajoutez ce qui suit pour créer un script de déploiement initial pour le service de messagerie :

    #!/bin/bashset -e JSON_BODY_LIMIT=20b docker run \ --rm \ -e JSON_BODY_LIMIT="${JSON_BODY_LIMIT} " \ messager
    

Ce script n’est pas complet à ce stade, mais il illustre quelques concepts :

  • Il attribue une valeur aux variables d’environnement en incluant cette configuration directement dans le script de déploiement.
  • Il utilise l'indicateur -e sur la commande docker run pour injecter des variables d'environnement dans le conteneur au moment de l'exécution.

Il peut sembler redondant de définir la valeur des variables d'environnement de cette manière, mais cela signifie que, quelle que soit la complexité de ce script de déploiement, vous pouvez jeter un œil rapide tout en haut du script et comprendre comment les données de configuration sont fournies au déploiement.

De plus, bien qu'un script de déploiement réel ne puisse pas invoquer explicitement la commande docker run , cet exemple de script est destiné à transmettre les principaux problèmes résolus par quelque chose comme un manifeste Kubernetes. Lorsque vous utilisez un système d'orchestration de conteneurs comme Kubernetes, un déploiement démarre un conteneur et la configuration de l'application dérivée de vos fichiers de configuration Kubernetes est mise à la disposition de ce conteneur. Ainsi, nous pouvons considérer cet exemple de fichier de déploiement comme une version minimale d’un script de déploiement qui joue le même rôle que les fichiers de déploiement spécifiques au framework comme les manifestes Kubernetes.

Dans un environnement de développement réel, vous pouvez vérifier ce fichier dans le contrôle de source et le soumettre à une révision de code. Cela donne au reste de votre équipe la possibilité de commenter vos paramètres et permet ainsi d'é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 souligne à juste titre qu’une limite de 20 octets pour les corps de requête JSON entrants (définie avec JSON_BODY_LIMIT ) est trop basse.

Capture d'écran d'une révision de code indiquant qu'une limite de 20 octets pour le corps du message est trop petite

Modifier les scripts de déploiement pour interroger les valeurs de configuration à partir de sources externes

Dans cette partie du défi, vous définissez le troisième emplacement pour la configuration d'un microservice : une source externe interrogée au moment du déploiement. L'enregistrement dynamique des valeurs et leur récupération à partir d'une source externe au moment du déploiement est une bien meilleure pratique que le codage en dur des valeurs, qui doivent être constamment mises à jour et peuvent provoquer des échecs. Pour une discussion, consultez les meilleures pratiques pour la configuration des applications de microservices sur notre blog.

À ce stade, deux composants d’infrastructure s’exécutent en arrière-plan pour fournir les services auxiliaires requis par le service de messagerie :

  1. RabbitMQ, propriété de l'équipe de la plateforme dans un déploiement réel (commencé à l'étape 3 de la configuration )
  2. La base de données de messagerie , détenue par votre équipe dans un déploiement réel (commencé à l'étape 4 de la configuration )

Le schéma condamné pour le service de messagerie 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 pour fournir une configuration en définissant les valeurs des variables dans un emplacement généralement accessible afin qu'elles puissent être interrogées par le service de messagerie lors de son déploiement.

Les informations de connexion requises pour RabbitMQ et la base de données de messagerie sont enregistrées dans le magasin Clé/Valeur (KV) Consul , qui est un emplacement commun accessible à tous les services lors de leur déploiement. Le magasin Consul KV n'est pas un endroit standard pour stocker ce type de données, mais ce tutoriel l'utilise pour des raisons de simplicité.

  1. 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 # Cette configuration nécessite un nouveau commit pour être modifiée NODE_ENV=production PORT=4000 JSON_BODY_LIMIT=100kb # Configuration de la base de données Postgres en extrayant les informations du # système 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) # Configuration de RabbitMQ en extrayant les informations du système 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} " \ messager
    

    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 modifie JSON_BODY_LIMIT à 100 Ko, une valeur plus réaliste que 20 octets.
    • Configuration interrogée à partir de sources externes – Elle récupère les valeurs des variables d'environnement POSTGRES_USER , PGPORT , PGHOST , AMQPHOST et AMQPPORT à partir du magasin Consul KV. Vous définissez les valeurs des variables d’environnement dans le magasin Consul KV au cours des deux étapes suivantes.
  2. Ouvrez messenger-db-deploy.sh et ajoutez ce qui suit pour créer un script de déploiement initial pour la base de données messenger :

    #!/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 # Enregistrer les détails de la base de données avec 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' # Ceci correspond à l'indicateur "--name" ci-dessus # (le nom d'hôte) 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 la configuration qui peut être interrogée par le service de messagerie au moment du déploiement, le script illustre les deux mêmes concepts que le script initial pour le service de messagerie de Créer des scripts de déploiement initiaux ) :

    • Il spécifie certaines configurations directement dans le script de déploiement, dans ce cas pour indiquer à la base de données PostgreSQL le port sur lequel s'exécuter et le nom d'utilisateur de l'utilisateur par défaut.
    • Il exécute Docker avec l'indicateur -e pour injecter des variables d'environnement dans le conteneur au moment 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 dans le réseau Docker que vous avez créé lorsque vous avez lancé le service de plateforme à l’étape 2 de la configuration .
  3. Dans un déploiement réel, c'est généralement l'équipe de la plateforme (ou similaire) qui gère le déploiement et la maintenance d'un service comme RabbitMQ dans le référentiel de la plateforme , comme vous le faites pour la base de données de messagerie dans le référentiel de messagerie . L’équipe de la plateforme s’assure ensuite que l’emplacement de cette infrastructure est détectable par les services qui en dépendent. Pour les besoins du didacticiel, définissez vous-même les valeurs RabbitMQ :

    curl -X PUT --silent --output /dev/null --show-error --fail \ -H "Type de contenu : application/json" \
    -d "rabbitmq" \
    http://localhost:8500/v1/kv/amqp-host
    
    curl -X PUT --silent --output /dev/null --show-error --fail \
    -H "Type de contenu : application/json" \
    -d "5672" \
    http://localhost:8500/v1/kv/amqp-port
    

    (Vous vous demandez peut-être pourquoi amqp est utilisé pour définir les variables RabbitMQ – c'est parce qu'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 de messagerie : le mot de passe de la base de données de messagerie !

Note: La gestion des secrets n'est pas l'objet de ce tutoriel, donc pour plus de simplicité, le secret est défini dans les fichiers de déploiement. Ne faites jamais cela dans un environnement réel (développement, test ou production), cela crée un énorme risque de sécurité .

Pour en savoir plus sur la gestion appropriée des secrets, consultez l'unité 2, Microservices Secrets Management 101 de Microservices mars 2023. (Spoiler : un outil de gestion des secrets est la seule méthode véritablement sécurisée pour stocker des secrets).

  1. Remplacez le contenu de infrastructure/messenger-db-deploy.sh par ce qui suit pour stocker le mot de passe secret de la base de données Messenger dans le magasin Consul KV :

    #!/bin/bashset -e PORT=5432 POSTGRES_USER=postgres # REMARQUE : Ne faites jamais cela dans un déploiement réel. Stockez les mots de passe uniquement dans un magasin de secrets cryptés.
    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 "Enregistrer la clé 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 "Clé d'enregistrement 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' # Cela correspond à l'indicateur "--name" ci-dessus # qui, pour notre configuration, signifie le nom d'hôte echo "Clé d'enregistrement 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 "Clé d'enregistrement 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 "Type de contenu : application/json" \ -d "${POSTGRES_PASSWORD} " printf "\nEnregistrement des détails postgres auprès de Consul terminé\n"
    
  2. Remplacez le contenu de infrastructure/messenger-deploy.sh par ce qui suit pour récupérer le mot de passe secret de la base de données de messagerie à partir du magasin Consul KV :

    #!/bin/bashset -e # Cette configuration nécessite une nouvelle validation pour être modifiée NODE_ENV=production PORT=4000 JSON_BODY_LIMIT=100kb # Configuration de la base de données Postgres en extrayant des informations du # système 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) # REMARQUE : Ne faites jamais cela dans un déploiement réel. Stockez les mots de passe uniquement dans un magasin de secrets cryptés.
    PGPASSWORD=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-password-never-do-this?raw=true) # Configuration de RabbitMQ en tirant du système 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 \ messager
    

Exécuter les scripts de déploiement

  1. Accédez au répertoire de l’application dans le référentiel Messenger et créez l’image Docker pour le service Messenger :

    cd ../appdocker build -t messager .
    
  2. Vérifiez que seuls les conteneurs appartenant au service de plateforme sont en cours d’exécution :

    docker ps --format '{{.Names}}' consul-serveur consul-client rabbitmq
    
  3. Accédez à la racine du référentiel Messenger et déployez la base de données Messenger et le service Messenger :

    cd .../infrastructure/messenger-db-deploy.sh
    ./infrastructure/messenger-deploy.sh
    

    Le script messenger-db-deploy.sh démarre la base de données Messenger et enregistre les informations appropriées auprès du système (qui dans ce cas est le magasin Consul KV).

    Le script messenger-deploy.sh démarre ensuite l'application et extrait la configuration enregistrée par messenger-db-deploy.sh du système (encore une fois, le magasin Consul KV).

    Indice: Si un conteneur ne parvient pas à démarrer, supprimez le deuxième paramètre de la commande docker run (la ligne -d \ ) dans le script de déploiement et exécutez à nouveau le script. Le conteneur démarre alors au premier plan, ce qui signifie que ses journaux apparaissent dans le terminal et peuvent identifier le problème. Lorsque vous résolvez le problème, restaurez la ligne -d \ afin que le conteneur réel s'exécute en arrière-plan.

  4. Envoyez une simple demande de contrôle d’intégrité à l’application pour vérifier que le déploiement a réussi :

    curl localhost:4000/santé curl: (7) Échec de la connexion au port localhost 4000 après 11 ms : Connexion rejetée
    

    Oups, échec ! Il s’avère qu’il manque toujours un élément critique de configuration et que le service de messagerie n’est pas exposé au système plus large. Il fonctionne joyeusement à l'intérieur du réseau mm_2023 , mais ce réseau n'est accessible que depuis Docker.

  5. Arrêtez le conteneur en cours d’exécution en prévision de la création d’une nouvelle image dans le prochain défi :

    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, vous n’exposez généralement pas les services directement. Au lieu de cela, vous suivez un modèle de microservices commun et placez un service proxy inverse devant votre service principal.

Dans ce défi, vous exposez le service de messagerie au monde extérieur en configurant 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. Pour ce faire, vous utilisez ces technologies :

  • Consul , un registre de services dynamique et Consul template , un outil de mise à jour dynamique d'un fichier basé sur les données Consul
  • NGINX Open Source , en tant que proxy inverse et équilibreur de charge qui expose un point d'entrée unique pour votre service de messagerie qui sera composé de plusieurs instances individuelles de l'application exécutées dans des conteneurs

Pour en savoir plus sur la découverte de services, consultez Rendre un service disponible en tant que configuration dans Bonnes pratiques pour la configuration d'applications de microservices sur notre blog.

Configurer Consul

Le fichier app/consul/index.mjs dans le référentiel Messenger contient tout le code nécessaire pour enregistrer le service de messagerie auprès de Consul au démarrage et le désenregistrer lors de l'arrêt normal. Il expose une fonction, register , qui enregistre tout service nouvellement déployé auprès du registre de services de Consul.

  1. Dans votre éditeur de texte préféré, ouvrez app/index.mjs et ajoutez l'extrait suivant après les autres instructions d'importation , pour importer la fonction d'enregistrement depuis app/consul/index.mjs :

    importer { s'inscrire en tant que registerConsul } depuis "./consul/index.mjs";
    

    Modifiez ensuite la section SERVER START à la fin du script comme indiqué, pour appeler registerConsul() après le démarrage de l'application :

    /* ================= DÉMARRAGE DU SERVEUR ================== */ app.listen(port, async () => { console.log(`messenger_service écoute sur le port${port} `); registerConsul(); }); exporter l'application par défaut ;
    
  2. Ouvrez le schéma convict dans app/config/config.mjs et ajoutez les valeurs de configuration suivantes après la clé jsonBodyLimit que vous avez ajoutée à l'étape 1 de l'exemple 2 .

      consulServiceName : { doc : « Le nom sous lequel le service est enregistré auprès de Consul. Si non spécifié, le service n'est pas enregistré",
    format : "*",
    par défaut : null,
    env : "NOM_SERVICE_CONSUL",
    },
    consulHost : {
    doc : "L'hôte sur lequel s'exécute le client Consul",
    format : Chaîne,
    par défaut : « consul-client »,
    env : "CONSUL_HOST",
    },
    consulPort : {
    doc : "Le port pour le client Consul",
    format : "port",
    par défaut : 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 modifiez le script de déploiement du service de messagerie pour inclure cette nouvelle connexion Consul et les informations d’enregistrement du service.

  3. Ouvrez infrastructure/messenger-deploy.sh et remplacez son contenu par ce qui suit pour inclure dans la configuration du service de messagerie les informations de connexion et d'enregistrement du service Consul que vous avez définies à l'étape précédente :

    #!/bin/bashset -e # Cette configuration nécessite un nouveau commit pour changer NODE_ENV=production PORT=4000 JSON_BODY_LIMIT=100kb CONSUL_SERVICE_NAME="messenger" # L'hôte et le port Consul sont inclus dans chaque hôte car nous # ne pouvons pas interroger Consul tant que nous ne les connaissons pas CONSUL_HOST="${CONSUL_HOST} " CONSUL_PORT="${CONSUL_PORT} " # Configuration de la base de données Postgres en extrayant des informations du # système 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") # REMARQUE : Ne faites jamais cela dans un déploiement réel. Stockez les mots de passe uniquement dans un magasin de secrets cryptés.
    PGPASSWORD=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-password-never-do-this?raw=true") # Configuration de RabbitMQ en tirant du système 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 \ messager
    

    Les principaux points à noter sont :

    • La variable d'environnement CONSUL_SERVICE_NAME indique à l'instance du service de messagerie quel nom utiliser lors de son inscription auprès de Consul.
    • Les variables d’environnement CONSUL_HOST et CONSUL_PORT concernent le client Consul exécuté à l’emplacement où le script de déploiement s’exécute.

    Note: Dans un déploiement réel, il s’agit d’un exemple de configuration qui doit être convenu entre les équipes : l’équipe responsable de Consul doit fournir les variables d’environnement CONSUL_HOST et CONSUL_PORT dans tous les environnements, car un service ne peut pas interroger Consul sans ces informations de connexion.

  4. Dans le terminal d'application, accédez au répertoire de l'application , arrêtez toutes les instances en cours d'exécution du service de messagerie et reconstruisez l'image Docker pour 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 .
    
  5. Accédez à http://localhost:8500 dans un navigateur pour voir l'interface utilisateur Consul en action (bien que rien d'intéressant ne se produise encore).

  6. À la racine du référentiel 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
    
  7. Dans l’interface utilisateur Consul du navigateur, cliquez sur Services dans la barre d’en-tête pour vérifier qu’un seul service de messagerie est en cours d’exécution.

    Onglet Services de l'interface utilisateur du consul avec une instance de chacun des services de consul et de messagerie

  8. Exécutez le script de déploiement quelques fois de plus pour démarrer davantage d’instances du service de messagerie . Vérifiez dans l'interface utilisateur Consul qu'ils sont en cours d'exécution.

    CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-deploy.sh
    

    Onglet « messagerie » de l'interface utilisateur de Consul avec cinq instances du service

Configurer NGINX

L’étape suivante consiste à ajouter NGINX Open Source comme proxy inverse et équilibreur de charge pour acheminer le trafic entrant vers toutes les instances de messagerie en cours d’exécution.

  1. Dans le terminal de l'application, accédez au répertoire racine du référentiel Messenger et créez un répertoire appelé load-balancer et trois fichiers :

    mkdir load-balancercd load-balancer
    touchez nginx.ctmpl
    touchez consul-template-config.hcl
    touchez Dockerfile
    

    Le Dockerfile définit le conteneur dans lequel s'exécutent les modèles NGINX et Consul. Le modèle Consul utilise les deux autres fichiers pour mettre à jour dynamiquement les flux en amont NGINX lorsque le service de messagerie change (les instances de service apparaissent ou disparaissent) dans son registre de services.

  2. Ouvrez le fichier nginx.ctmpl créé à l'étape 1 et ajoutez l'extrait de configuration NGINX suivant, que le modèle Consul utilise pour mettre à jour dynamiquement le groupe en amont NGINX :

    service_messager_en_amont { {{- plage service "messenger" }}
    serveur {{ .Adresse }}:{{ .Port }};
    {{- fin }}
    }
    
    serveur {
    écouter 8085;
    nom_serveur localhost;
    
    emplacement / {
    proxy_pass http://service_messager;
    add_header Hôte_en_amont $upstream_addr;
    }
    }
    

    Cet extrait ajoute l'adresse IP et le numéro de port de chaque instance de service de messagerie enregistrée auprès de Consul au groupe en amont NGINX messenger_service . NGINX transmet les requêtes entrantes à l'ensemble défini dynamiquement d'instances de service en amont.

  3. Ouvrez le fichier consul-template-config.hcl créé à l'étape 1 et ajoutez la configuration suivante :

    consul { address = "consul-client:8500"
    
    retry {
    enabled = true
    tries = 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 pour le modèle Consul lui indique de restituer le modèle source (l'extrait de configuration NGINX créé à l'étape précédente), de le placer à la destination spécifiée et enfin d'exécuter la commande spécifiée (qui indique à 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 annulée dans Consul. NGINX recharge ensuite sa configuration sans temps d'arrêt, garantissant ainsi que NGINX dispose d'un ensemble de serveurs à jour et sains (instances de service de messagerie ) vers lesquels il peut envoyer du trafic.

  4. Ouvrez le fichier Dockerfile créé à l’étape 1 et ajoutez le contenu suivant, qui crée le service NGINX. (Vous n’avez pas besoin de comprendre le Dockerfile pour les besoins de ce didacticiel, mais le code est documenté en ligne pour votre commodité.)

    DE nginx:1.23.1 ARG CONSUL_TEMPLATE_VERSION=0.30.0 # Définissez une variable d'environnement pour l'emplacement du cluster Consul. Par défaut, il essaie de résoudre en consul-client:8500 # qui est le comportement si Consul s'exécute en tant que conteneur dans le # même hôte et lié à ce conteneur NGINX (avec l'alias # consul, bien sûr). Mais cette variable d'environnement peut également être remplacée au démarrage du conteneur si nous voulons résoudre une autre adresse.
    
    ENV CONSUL_URL consul-client:8500 # Téléchargez la version spécifiée du modèle Consul AJOUTER https://releases.hashicorp.com/consul-template/${CONSUL_TEMPLATE_VERSION} /consul-template_${CONSUL_TEMPLATE_VERSION} _linux_amd64.zip /tmp EXÉCUTER apt-get update \ && apt-get install -y --no-install-recommends dumb-init unzip \ && décompresser /tmp/consul-template_${CONSUL_TEMPLATE_VERSION} _linux_amd64.zip -d /usr/local/bin \ && rm -rf /tmp/consul-template_${CONSUL_TEMPLATE_VERSION} _linux_amd64.zip COPIER consul-template-config.hcl ./consul-template-config.hcl COPIER nginx.ctmpl /usr/templates/nginx.ctmpl EXPOSER 8085 STOPSIGNAL SIGQUIT CMD ["dumb-init", "consul-template", "-config=consul-template-config.hcl"]
    
  5. Créer une image Docker :

    docker build -t messager-lb .
    
  6. Accédez à la racine du répertoire Messenger , créez un fichier nommé messenger-load-balancer-deploy.sh comme fichier de déploiement pour le service NGINX (comme avec le reste des services que vous avez déployés tout au long du didacticiel). Selon votre environnement, vous devrez peut-être préfixer la commande chmod avec sudo :

    cd ..
    touch infrastructure/messenger-load-balancer-deploy.sh
    chmod +x infrastructure/messenger-load-balancer-deploy.sh
    
  7. Ouvrez messenger-load-balancer-deploy.sh et ajoutez le contenu suivant :

    #!/bin/bashset -e # L'hôte et le port Consul sont inclus dans chaque hôte puisque nous ne pouvons pas interroger Consul tant que nous ne les connaissons pas 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
    
  8. Maintenant que tout est en place, déployez le service NGINX :

    CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-load-balancer-deploy.sh
    
  9. Vérifiez si vous pouvez accéder au service de messagerie en externe :

    curl -X OBTENIR http://localhost:8085/health OK
    

    Ça marche! NGINX équilibre désormais la charge sur toutes les instances du service de messagerie qui ont été créées. Vous pouvez le constater car l’en-tête X-Forwarded-For affiche les mêmes adresses IP de service de messagerie que celles de l’interface utilisateur Consul à l’étape 8 de la section précédente.

Défi 4 : Migrer une base de données à l'aide d'un service comme exécuteur de tâches

Les grandes applications utilisent souvent des « job runners » avec de petits processus de travail qui peuvent être utilisés pour effectuer des tâches ponctuelles telles que la modification de données (par exemple Sidekiq et Celery ). Ces outils nécessitent souvent une infrastructure de support supplémentaire telle que Redis ou RabbitMQ . Dans ce cas, vous utilisez le service de messagerie lui-même comme « exécuteur de tâches » pour exécuter des tâches ponctuelles. Cela a du sens car il est déjà très petit, est entièrement capable d'interagir avec la base de données et d'autres éléments d'infrastructure dont il dépend, et fonctionne complètement séparément de l'application qui gère le trafic.

Cela présente trois avantages :

  1. L'exécuteur de tâches (y compris les scripts qu'il exécute) subit exactement les mêmes contrôles et processus de révision que le service de production.
  2. Les valeurs de configuration telles que les utilisateurs de la base de données peuvent être facilement modifiées pour rendre le déploiement de production plus sécurisé. Par exemple, vous pouvez exécuter le service de production avec un utilisateur « à faibles privilèges » qui ne peut écrire et interroger qu’à partir de tables existantes. Vous pouvez configurer une instance de service différente pour apporter des modifications à la structure de la base de données en tant qu’utilisateur disposant de privilèges supérieurs et capable de créer et de supprimer des tables.
  3. Certaines équipes exécutent des tâches à partir d’instances qui gèrent également le trafic de production de services. Cela est dangereux car les problèmes liés au travail peuvent avoir un impact sur les autres fonctions exécutées par l’application dans le conteneur. C’est pour éviter ce genre de choses que nous faisons des microservices en premier lieu, n’est-ce pas ?

Dans ce défi, vous explorez comment un artefact peut être modifié pour remplir un nouveau rôle en modifiant certaines valeurs de configuration de la base de données et en migrant la base de données Messenger pour utiliser les nouvelles valeurs et tester ses performances .

Migrer la base de données de messagerie

Pour un déploiement de production réel, vous pouvez créer deux utilisateurs distincts avec des autorisations différentes : un « utilisateur d’application » et un « utilisateur migrant ». Par souci de simplicité, dans cet exemple, vous utilisez l’utilisateur par défaut comme utilisateur d’application et créez un utilisateur migrateur avec des privilèges de superutilisateur. Dans une situation réelle, il vaut la peine de passer plus de temps à décider quelles autorisations minimales spécifiques sont nécessaires à chaque utilisateur en fonction de son rôle.

  1. Dans le terminal de l’application, créez un nouvel utilisateur PostgreSQL avec des privilèges de superutilisateur :

    echo "CRÉER UN UTILISATEUR messenger_migrator AVEC LE MOT DE PASSE DU SUPERUTILISATEUR 'migrator_password';" | docker exec -i messenger-db-primary psql -U postgres
    
  2. Ouvrez le script de déploiement de la base de données ( infrastructure/messenger-db-deploy.sh ) et remplacez son contenu pour ajouter les informations d'identification du nouvel utilisateur.

    Note: Prenons le temps de le répéter : pour un déploiement réel, ne placez JAMAIS de secrets tels que les informations d’identification de base de données dans un script de déploiement ou ailleurs que dans un outil de gestion des secrets. Pour plus de détails, voir l'unité 2 : Gestion des secrets des microservices 101 des microservices mars 2023.

    #!/bin/bash set -e PORT=5432 POSTGRES_USER=postgres # REMARQUE : Ne faites jamais cela dans un déploiement réel. Stockez les mots de passe # uniquement dans un magasin de secrets cryptés. # Étant donné que nous nous concentrons sur d'autres concepts dans ce didacticiel, nous # définissons le mot de passe de cette façon ici pour plus de commodité.
    POSTGRES_PASSWORD=postgres # Utilisateur de migration POSTGRES_MIGRATOR_USER=messenger_migrator # REMARQUE : Comme ci-dessus, ne faites jamais cela dans un déploiement réel.
    POSTGRES_MIGRATOR_PASSWORD=mot de passe du migrant 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 "Enregistrer la clé 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 "Clé d'enregistrement 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' # Cela correspond à l'indicateur "--name" ci-dessus # qui, pour notre configuration, signifie le nom d'hôte echo "Clé d'enregistrement 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 "Enregistrer la clé 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 "\nEnregistrement des détails postgres auprès de Consul terminé\n"
    

    Cette modification ajoute simplement l’utilisateur migrateur à l’ensemble des utilisateurs définis dans Consul après le déploiement de la base de données.

  3. Créez un nouveau fichier dans le répertoire d'infrastructure appelé messenger-db-migrator-deploy.sh (là encore, vous devrez peut-être préfixer la commande chmod avec sudo ) :

    infrastructure tactile/messenger-db-migrator-deploy.shchmod +x infrastructure/messenger-db-migrator-deploy.sh
    
  4. Ouvrez messenger-db-migrator-deploy.sh et ajoutez ce qui suit :

    #!/bin/bashset -e # Cette configuration nécessite un nouveau commit pour changer NODE_ENV=production PORT=4000 JSON_BODY_LIMIT=100kb CONSUL_SERVICE_NAME="messenger-migrator" # L'hôte et le port Consul sont inclus dans chaque hôte car nous # ne pouvons pas interroger Consul tant que nous ne les connaissons pas CONSUL_HOST="${CONSUL_HOST} " CONSUL_PORT="${CONSUL_PORT} " # Obtenir le nom d'utilisateur et le mot de passe du migrateur 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) # REMARQUE : Ne faites jamais cela dans un déploiement réel. Stockez les mots de passe uniquement dans un magasin de secrets cryptés.
    PGPASSWORD=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-migrator-password-never-do-this?raw=true") # Configuration de RabbitMQ en tirant du système 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 \ messager
    

    Ce script est assez similaire au script infrastructure/messenger-deploy.sh dans sa forme finale, que vous avez créé à l'étape 3 de la configuration de Consul . La principale différence est que CONSUL_SERVICE_NAME est messenger-migrator au lieu de messenger , et PGUSER correspond au superutilisateur « migrateur » que vous avez créé à l’étape 1 ci-dessus.

    Il est important que CONSUL_SERVICE_NAME soit messenger-migrator . S'il était défini sur Messenger , NGINX mettrait automatiquement ce service en rotation pour recevoir les appels API et il n'est pas censé gérer le trafic.

    Le script déploie une instance de courte durée dans le rôle de migrateur. Cela évite que des problèmes liés à la migration n’affectent la gestion du trafic par les principales instances du service de messagerie .

  5. Redéployer la base de données PostgreSQL. Étant donné que vous utilisez des scripts bash dans ce didacticiel, vous devez arrêter et redémarrer le service de base de données. Dans une application de production, vous exécutez généralement simplement un script d'infrastructure en tant que code , pour ajouter uniquement les éléments qui ont changé.

    docker stop messenger-db-primaryCONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-db-deploy.sh
    
  6. Déployez le service de migration de base de données PostgreSQL :

    CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-db-migrator-deploy.sh
    
  7. Vérifiez que l’instance fonctionne comme prévu :

    docker ps --format "{{.Noms}}" ... messenger-migrator
    

    Vous pouvez également vérifier dans l'interface utilisateur de Consul que le service de migration de base de données s'est correctement enregistré auprès de Consul en tant que messenger-migrator (là encore, il ne s'enregistre pas sous le nom de Messenger car il ne gère pas le trafic) :

    Onglet Services de l'interface utilisateur du consul avec une instance de chacun des services consul et messenger-migrator, et quatre instances du service de messagerie

  8. Passons maintenant à l’étape finale : exécutez les scripts de migration de la base de données ! Ces scripts ne ressemblent à aucun véritable script de migration de base de données, mais ils utilisent le service Messenger-Migrant pour exécuter des scripts spécifiques à 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 scripts de nœud messenger-migrator/create-db.mjsdocker exec -i scripts de nœud messenger-migrator/create-schema.mjs
    docker exec -i scripts de nœud messenger-migrator/create-seed-data.mjs
    docker stop messenger-migrator
    

Testez le service de messagerie en action

Maintenant que vous avez migré la base de données de messagerie vers son format final, le service de messagerie est enfin prêt à être observé en action ! Pour ce faire, exécutez quelques requêtes curl de base sur le service NGINX (vous avez configuré NGINX comme point d'entrée du système dans Configurer NGINX ).

Certaines des commandes suivantes utilisent la bibliothèque jq pour formater la sortie JSON. Vous pouvez l'installer si nécessaire ou l'omettre de la ligne de commande si vous le souhaitez.

  1. Créer une conversation :

    curl -d '{"participant_ids": [1, 2]}' -H "Content-Type: application/json" -X POST 'http://localhost:8085/conversations' { "conversation": { "id": "1", "inséré_à": " AAAA - MM - JJ T06:41:59.000Z " } }
    
  2. Envoyer un message à la conversation depuis un utilisateur avec l'ID 1 :

    curl -d '{"contenu": "Ceci est le premier message"}' -H "User-Id: 1" -H "Type de contenu : application/json" -X POST 'http://localhost:8085/conversations/1/messages' | jq { "message": { "id": "1", "contenu" : "Ceci est le premier message", "index": 1, « user_id » : 1, « nom d'utilisateur » : "James Blanderphone", "conversation_id" : 1, "inséré_à": " AAAA - MM - JJ T06:42:15.000Z " } }
    
  3. Répondre avec un message d'un autre utilisateur (avec l'ID 2) :

    curl -d '{"contenu": "Ceci est le deuxième message"}' -H "User-Id: 2" -H "Type de contenu : application/json" -X POST 'http://localhost:8085/conversations/1/messages' | jq { "message": { "id": "2", "contenu" : "Ceci est le deuxième message", "index": 2, « user_id » : 2, « nom d'utilisateur » : "Normalavien Ropetoter", "conversation_id": 1, "inséré_à": " AAAA - MM - JJ T06:42:25.000Z " } }
    
  4. Récupérer les messages :

    curl -X GET 'http://localhost:8085/conversations/1/messages' | jq { "messages": [ { "id": "1", "contenu" : "Ceci est le premier message", "user_id": "1", "channel_id": "1", "index": "1", "inséré_à": " AAAA - MM - JJ T06:42:15.000Z", "nom d'utilisateur" : "James Blanderphone" }, { "identifiant": "2", "contenu" : "Ceci est le deuxième message", "user_id": "2", "channel_id": "1", "index": "2", "inséré_à": " AAAA - MM - JJ T06:42:25.000Z", "nom d'utilisateur" : "Le grimpeur de corde normalavien" } ] }
    

Nettoyer

Vous avez créé un nombre important de conteneurs et d’images tout au long de ce didacticiel ! Utilisez les commandes suivantes pour supprimer tous 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 :

    # Depuis le référentiel de la plateforme docker compose down
    
  • Pour supprimer toutes les images Docker utilisées tout au long du didacticiel :

    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
    

Prochaines étapes

Vous pensez peut-être : « Cela semble demander beaucoup de travail de mettre en place quelque chose de simple » – et vous avez raison ! Passer à une architecture axée sur les microservices nécessite une grande minutie quant à la manière dont vous structurez et configurez les services. Malgré toute la complexité, vous avez fait de solides progrès :

  • Vous définissez une configuration axée sur les microservices qui est facilement compréhensible par les autres équipes.
  • Vous avez configuré le système de microservices pour qu'il soit quelque peu flexible, tant en termes de mise à l'échelle que d'utilisation des différents services impliqués.

Pour poursuivre votre formation sur les microservices, consultez Microservices mars 2023. L'unité 2, Microservices Secrets Management 101 , fournit un aperçu approfondi mais convivial de la gestion des secrets dans les environnements de microservices.


« 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."