BLOG | NGINX

Tutorial de NGINX: Cómo implementar y configurar microservicios

NGINX - Parte de F5 - horizontal, negro, tipo RGB
Miniatura de Javier Evans
Javier Evans
Publicado el 7 de marzo de 2023

Esta publicación es uno de los cuatro tutoriales que te ayudan a poner en práctica los conceptos de Microservicios de marzo de 2023: Comience a entregar microservicios :

 

Todas las aplicaciones requieren configuración, pero las consideraciones al configurar un microservicio pueden ser diferentes a las de una aplicación monolítica . Podemos consultar el Factor 3 ( Almacenar configuración en el entorno ) de la aplicación de doce factores para obtener orientación aplicable a ambos tipos de aplicaciones, aunque esta orientación puede adaptarse a las aplicaciones de microservicios. En particular, podemos adaptar la forma en que definimos la configuración del servicio, proporcionamos la configuración a un servicio y hacemos que un servicio esté disponible como valor de configuración para otros servicios que puedan depender de él.

Para obtener una comprensión conceptual de cómo adaptar Factor 3 para microservicios, específicamente las mejores prácticas para archivos de configuración, bases de datos y descubrimiento de servicios, lea Mejores prácticas para configurar aplicaciones de microservicios en nuestro blog. Esta publicación es una excelente manera de poner ese conocimiento en práctica.

Nota:  Nuestra intención en este tutorial es ilustrar algunos conceptos fundamentales, no mostrar la forma correcta de implementar microservicios en producción. Si bien utiliza una arquitectura de “microservicios” real, hay algunas advertencias importantes:

  • El tutorial no utiliza un marco de orquestación de contenedores como Kubernetes o Nomad. Esto es para que puedas aprender sobre los conceptos de microservicios sin empantanarte en los detalles de un determinado marco. Los patrones presentados aquí son portables a un sistema que ejecuta uno de estos marcos.
  • Los servicios están optimizados para facilitar la comprensión en lugar del rigor de la ingeniería de software. Lo importante es analizar el papel de un servicio en el sistema y sus patrones de comunicación, no los detalles del código. Para obtener más información, consulte los archivos README de los servicios individuales.

Descripción general del tutorial

Este tutorial ilustra cómo se aplican los conceptos del Factor 3 a las aplicaciones de microservicios. En cuatro desafíos, explorarás algunos patrones comunes de configuración de microservicios e implementarás y configurarás un servicio utilizando esos patrones:

  • En los Desafío 1 y 2, explorarás el primer patrón, que se refiere a dónde se ubica la configuración de una aplicación de microservicios. Hay tres ubicaciones típicas:

    • El código de la aplicação
    • El script de implementación de la aplicação
    • Fuentes externas a las que accede el script de implementación
  • En el desafío 3, configura dos patrones más: exponer la aplicación al mundo exterior a través de NGINX como proxy inverso y habilitar el descubrimiento de servicios mediante Consul.
  • En el Desafío 4 implementas el patrón final: usar una instancia de tu microservicio como un “ejecutor de trabajos” que realiza una acción única diferente de su función habitual (en este caso, emular una migración de base de datos).

El tutorial utiliza cuatro tecnologías:

  • Messenger : una API de chat simple con capacidades de almacenamiento de mensajes, creada para este tutorial.
  • NGINX de código abierto : un punto de entrada al servicio de mensajería y al sistema en general
  • Consul : un registro de servicios dinámico y un almacén de valores clave
  • RabbitMQ : un popular agente de mensajes de código abierto que permite que los servicios se comuniquen de forma asincrónica

Diagrama de topología que muestra NGINX Open Source y la plantilla Consul ejecutándose juntas en un contenedor. La plantilla de consulta se comunica con el cliente de consulta. NGINX Open Source es un proxy inverso para el servicio de mensajería, que almacena datos en messenger_db y se comunica con Rabbit MQ.

Mire este vídeo para obtener una descripción general del tutorial. Los pasos no coinciden exactamente con esta publicación, pero ayuda a comprender los conceptos.

Prerrequisitos y configuración

Requisitos previos

Para completar el tutorial en su propio entorno, necesitará:

  • Un entorno compatible con Linux/Unix
  • Conocimiento básico de la línea de comandos de Linux, JavaScript y bash (pero se proporcionan y explican todos los códigos y comandos, por lo que aún puede tener éxito con un conocimiento limitado)
  • Docker y Docker Compose
  • Node.js 19.x o posterior

    • Probamos la versión 19.x, pero esperamos que las versiones más nuevas de Node.js también funcionen.
    • Para obtener información detallada sobre la instalación de Node.js, consulte el archivo README en el repositorio del servicio de mensajería . También puedes instalar asdf para obtener exactamente la misma versión de Node.js utilizada en los contenedores.
  • curl (ya instalado en la mayoría de los sistemas)
  • Las cuatro tecnologías enumeradas en la descripción general del tutorial : messenger (lo descargará en la siguiente sección), NGINX Open Source , Consul y RabbitMQ .

Configuración

  1. Inicie una sesión de terminal (las instrucciones posteriores se referirán a esto como la terminal de la aplicación ).
  2. En su directorio de inicio, cree el directorio microservices-march y clone en él los repositorios de GitHub para este tutorial. (También puede utilizar un nombre de directorio diferente y adaptar las instrucciones en consecuencia).

    Nota:  A lo largo del tutorial se omite el aviso en la línea de comandos de Linux para que sea más fácil copiar y pegar los comandos en su terminal. La tilde ( ~ ) representa su directorio de inicio.

    mkdir ~/microservices-marchcd ~/microservices-march
    Clon de git https://github.com/microservices-march/platform.git --branch mm23-twelve-factor-start
    Clon de git https://github.com/microservices-march/messenger.git --branch mm23-twelve-factor-start
    
  3. Cambie al repositorio de la plataforma e inicie Docker Compose:

    cd platformdocker componer -d --build
    

    Esto inicia RabbitMQ y Consul, que se utilizarán en desafíos posteriores.

    • El indicador -d le indica a Docker Compose que se separe de los contenedores cuando se hayan iniciado (de lo contrario, los contenedores permanecerán conectados a su terminal).
    • El indicador --build le indica a Docker Compose que reconstruya todas las imágenes al iniciarse. Esto garantiza que las imágenes que está ejecutando se mantengan actualizadas ante cualquier posible cambio en los archivos.
  4. Cambie al repositorio de mensajería e inicie Docker Compose:

    cd ../messengerdocker componer -d --build
    

    Esto inicia la base de datos PostgreSQL para el servicio de mensajería , al que nos referiremos como messenger-database durante el resto del tutorial.

Desafío 1: Definir la configuración de microservicios a nivel de aplicación

En este desafío, configurarás la configuración en la primera de las tres ubicaciones que veremos en el tutorial: el nivel de la aplicação . ( El desafío 2 ilustra la segunda y tercera ubicación, los scripts de implementación y las fuentes externas).

La aplicación de doce factores excluye específicamente la configuración a nivel de aplicação, porque dicha configuración no necesita cambiar entre diferentes entornos de implementación (a los que la aplicación de doce factores llama "implementaciones" ). No obstante, cubrimos los tres tipos para completar: la forma de abordar cada categoría a medida que desarrolla, construye e implementa un servicio es diferente.

El servicio de mensajería está escrito en Node.js, con el punto de entrada en app/index.mjs en el repositorio de mensajería . Esta línea del archivo:

aplicación.use(express.json());

es un ejemplo de configuración a nivel de aplicação. Configura el marco Express para deserializar los cuerpos de solicitud que son de tipo aplicação/json en objetos JavaScript.

Esta lógica está estrechamente ligada al código de su aplicação y no es lo que la aplicación de doce factores considera "configuración". Pero en el software todo depende de tu situación, ¿no es así?

En las siguientes dos secciones, modificará esta línea para implementar dos ejemplos de configuración a nivel de aplicação.

Ejemplo 1

En este ejemplo, establece el tamaño máximo del cuerpo de una solicitud aceptada por el servicio de mensajería . Este límite de tamaño se establece mediante el argumento limit de la función express.json , como se analiza en la documentación de la API de Express . Aquí agrega el argumento de límite a la configuración del middleware JSON del marco Express discutido anteriormente .

  1. En su editor de texto preferido, abra app/index.mjs y reemplace:

    aplicación.use(express.json())
    

    con:

    aplicación.use(express.json({ límite: "20b" }));
    
  2. En la terminal de la aplicación (la que usaste en Configuración ), cambia al directorio de la aplicación e inicia el servicio de mensajería :

    cd app npm install node index.mjs messenger_service escuchando en el puerto 4000
    
  3. Inicie una segunda sesión de terminal independiente (cuyas instrucciones posteriores llaman a la terminal del cliente ) y envíe una solicitud POST al servicio de mensajería . El mensaje de error indica que la solicitud se procesó correctamente, porque el cuerpo de la solicitud estaba por debajo del límite de 20 bytes establecido en el Paso 1, pero que el contenido de la carga útil JSON es incorrecto:

    curl -d '{ "texto": "hola" }' -H "Tipo de contenido: aplicação/json" -X POST http://localhost:4000/conversaciones ... { "error": "La conversación debe tener 2 usuarios únicos"
    
  4. Envía un cuerpo de mensaje un poco más largo (nuevamente en la terminal del cliente). Hay mucho más resultado que en el paso 3, incluido un mensaje de error que indica que esta vez el cuerpo de la solicitud supera los 20 bytes:

    curl -d '{ "text": "hola, mundo" }' -H "Content-Type: aplicação/json" -X POST http://localhost:4000/conversations ... \”PayloadTooLargeError: la entidad solicitada es demasiado grande”
    

Ejemplo 2

Este ejemplo utiliza convict , una biblioteca que le permite definir un “esquema” de configuración completo en un solo archivo. También ilustra dos pautas del Factor 3 de la aplicación de doce factores:

  • Configuración de la tienda en variables de entorno : modifica la aplicación para que el tamaño máximo del cuerpo se establezca mediante una variable de entorno ( JSON_BODY_LIMIT ) en lugar de estar codificado en el código de la aplicación.
  • Defina claramente la configuración de su servicio : esta es una adaptación del Factor 3 para microservicios. Si no está familiarizado con este concepto, le recomendamos que se tome un momento para leerlo en Mejores prácticas para configurar aplicaciones de microservicios en nuestro blog.

El ejemplo también configura algunas "plomerías" que aprovecharás en el Desafío 2 : el script de implementación de mensajero que crearás en ese desafío establece la variable de entorno JSON_BODY_LIMIT que insertas en el código de la aplicación aquí, como una ilustración de la configuración especificada en un script de implementación.

  1. Abra el archivo de configuración de convicto , app/config/config.mjs , y agregue lo siguiente como una nueva clave después de la clave amqpport :

    jsonBodyLimit: { doc: `El tamaño máximo (con unidad incluida) que analizará el middleware JSON. El análisis de unidades lo realiza la biblioteca https://www.npmjs.com/package/bytes.
    Ejemplo: "100kb"`,
    formato: Cadena,
    predeterminado: nulo,
    entorno: "LÍMITE_DE_CUERPO_JSON",
    

    La biblioteca convict se encarga de analizar la variable de entorno JSON_BODY_LIMIT cuando la usa para establecer el tamaño máximo del cuerpo en la línea de comando en el Paso 3 a continuación:

    • Extrae el valor de la variable de entorno correcta
    • Comprueba el tipo de la variable ( cadena )
    • Permite el acceso a él en la aplicação bajo la clave jsonBodyLimit
  2. En app/index.mjs reemplace:

    aplicación.use(express.json({ límite: "20b" }));
    

    con

    aplicación.use(express.json({ límite: config.get("jsonBodyLimit") }));
    
  3. En la terminal de la aplicación (donde inició el servicio de mensajería en el Paso 2 del Ejemplo 1 ), presione Ctrl+c para detener el servicio. Luego, inícielo nuevamente, utilizando la variable de entorno JSON_BODY_LIMIT para establecer el tamaño máximo del cuerpo en 27 bytes:

    ^cJSON_BODY_LIMIT=27b índice de nodo.mjs
    

    Este es un ejemplo de cómo modificar el método de configuración cuando hacerlo tiene sentido para su caso de uso: ha pasado de codificar un valor (en este caso, un límite de tamaño) en el código de la aplicación a configurarlo con una variable de entorno, como lo recomienda la aplicación de doce factores.

    Como se mencionó anteriormente, en el Desafío 2 , el uso de la variable de entorno JSON_BODY_LIMIT se convertirá en un ejemplo de la segunda ubicación para la configuración, cuando utilice el script de implementación del servicio de mensajería para configurar la variable de entorno en lugar de configurarla en la línea de comando.

  4. En la terminal del cliente, repita el comando curl del Paso 4 del Ejemplo 1 (con el cuerpo de solicitud más grande). Como ahora ha aumentado el límite de tamaño a 27 bytes, el cuerpo de la solicitud ya no excede el límite y recibe el mensaje de error que indica que se procesó la solicitud, pero que el contenido de la carga útil JSON es incorrecto:

    curl -d '{ "texto": "hola, mundo" }' -H "Tipo de contenido: aplicação/json" -X POST http://localhost:4000/conversations { "error": "La conversación debe tener 2 usuarios únicos"
    

    Puede cerrar la terminal del cliente si lo desea. Emitirás todos los comandos en el resto del tutorial en la terminal de la aplicación.

  5. En la terminal de la aplicación, presione Ctrl+c para detener el servicio de mensajería (detuvo y reinició el servicio en esta terminal en el Paso 3 anterior).

    ^c
    
  6. Detener la base de datos del mensajero . Puede ignorar de forma segura el mensaje de error que se muestra, ya que la red aún está en uso por los elementos de infraestructura definidos en el repositorio de la plataforma . Ejecute este comando en la raíz del repositorio del mensajero .

    docker compose down ...no se pudo eliminar la red mm_2023....
    

Desafío 2: Crear scripts de implementación para un servicio

La configuración debe estar estrictamente separada del código (de lo contrario, ¿cómo puede variar entre implementaciones?)
– Del Factor 3 de la aplicación de doce factores

A primera vista, esto se podría interpretar como “no registrar la configuración en el control de origen”. En este desafío, implementa un patrón común para entornos de microservicios que puede parecer romper esta regla, pero en realidad la respeta y al mismo tiempo proporciona valiosas mejoras de proceso que son fundamentales para los entornos de microservicios.

En este desafío, creará scripts de implementación para imitar la funcionalidad de la infraestructura como código y los manifiestos de implementación que proporcionan configuración a un microservicio, modificará los scripts para usar fuentes externas de configuración, establecerá un secreto y luego ejecutará los scripts para implementar los servicios y su infraestructura.

Crea los scripts de implementación en un directorio de infraestructura recién creado en el repositorio de Messenger . Un directorio llamado infraestructura (o alguna variación de ese nombre) es un patrón común en las arquitecturas de microservicios modernas, que se utiliza para almacenar cosas como:

Los beneficios de este patrón incluyen:

  • Asigna la propiedad de la implementación del servicio y de la implementación de la infraestructura específica del servicio (como bases de datos) al equipo que posee el servicio.
  • El equipo puede garantizar que los cambios en cualquiera de estos elementos pasen por su proceso de desarrollo (revisión de código, CI, etc.).
  • El equipo puede realizar cambios fácilmente en la forma en que se implementan el servicio y su infraestructura de soporte sin depender de equipos externos que realicen el trabajo para ellos.

Como se mencionó anteriormente, nuestra intención con este tutorial no es mostrar cómo configurar un sistema real, y los scripts que implementa en este desafío no se parecen a un sistema de producción real. Más bien, ilustran algunos conceptos centrales y problemas resueltos mediante la configuración específica de la herramienta cuando se trata de la implementación de infraestructura relacionada con microservicios, al mismo tiempo que abstraen los scripts a la mínima cantidad de herramientas específicas posible.

Crear scripts de implementación inicial

  1. En la terminal de la aplicación, cree un directorio de infraestructura en la raíz del repositorio de mensajería y cree archivos para contener los scripts de implementación para el servicio de mensajería y la base de datos de mensajería . Dependiendo de su entorno, es posible que necesite anteponer sudo a los comandos chmod :

    mkdir infraestructuracd infraestructura
    touch messenger-deploy.sh
    chmod +x messenger-deploy.sh
    touch messenger-db-deploy.sh
    chmod +x messenger-db-deploy.sh
    
  2. En su editor de texto preferido, abra messenger-deploy.sh y agregue lo siguiente para crear un script de implementación inicial para el servicio de mensajería :

    #!/bin/bashset -e LÍMITE_DEL_CUERPO_JSON=20b docker run \ --rm \ -e LÍMITE_DEL_CUERPO_JSON="${JSON_BODY_LIMIT} " \ mensajero
    

Este guión no está completo en este momento, pero ilustra un par de conceptos:

  • Asigna un valor a las variables de entorno incluyendo esa configuración directamente en el script de implementación.
  • Utiliza el indicador -e en el comando docker run para inyectar variables de entorno en el contenedor en tiempo de ejecución.

Puede parecer redundante establecer el valor de las variables de entorno de esta manera, pero significa que, sin importar cuán complejo se vuelva este script de implementación, puede echar un vistazo rápido a la parte superior del script y comprender cómo se proporcionan los datos de configuración a la implementación.

Además, aunque un script de implementación real puede no invocar explícitamente el comando docker run , este script de muestra está destinado a transmitir los problemas centrales que se resuelven con algo como un manifiesto de Kubernetes. Al utilizar un sistema de orquestación de contenedores como Kubernetes, una implementación inicia un contenedor y la configuración de la aplicação derivada de los archivos de configuración de Kubernetes se pone a disposición de ese contenedor. Por lo tanto, podemos considerar este archivo de implementación de muestra como una versión mínima de un script de implementación que cumple la misma función que los archivos de implementación específicos del marco, como los manifiestos de Kubernetes.

En un entorno de desarrollo real, puedes registrar este archivo en el control de origen y someterlo a una revisión de código. Esto le da al resto de su equipo la oportunidad de comentar sobre su configuración y, por lo tanto, ayuda a evitar incidentes en los que valores mal configurados provocan un comportamiento inesperado. Por ejemplo, en esta captura de pantalla un miembro del equipo señala correctamente que un límite de 20 bytes para los cuerpos de solicitud JSON entrantes (establecido con JSON_BODY_LIMIT ) es demasiado bajo.

Captura de pantalla de la revisión del código que indica que el límite de 20 bytes para el cuerpo del mensaje es demasiado pequeño

Modificar scripts de implementación para consultar valores de configuración de fuentes externas

En esta parte del desafío, configura la tercera ubicación para la configuración de un microservicio: una fuente externa que se consulta en el momento de la implementación. Registrar valores dinámicamente y obtenerlos de una fuente externa en el momento de la implementación es una práctica mucho mejor que codificar valores de forma rígida, que deben actualizarse constantemente y pueden provocar fallas. Para obtener más información, consulte Mejores prácticas para configurar aplicaciones de microservicios en nuestro blog.

En este punto, dos componentes de infraestructura se ejecutan en segundo plano para proporcionar los servicios auxiliares requeridos por el servicio de mensajería :

  1. RabbitMQ, propiedad del equipo de Plataforma en una implementación real (iniciada en el Paso 3 de la Configuración )
  2. La base de datos de mensajería , propiedad de su equipo en una implementación real (iniciada en el Paso 4 de la configuración )

El esquema de convicto para el servicio de mensajería en app/config/config.mjs define las variables de entorno requeridas correspondientes a estas piezas de configuración externa. En esta sección, configura estos dos componentes para proporcionar configuración estableciendo los valores de las variables en una ubicación de acceso común para que el servicio de mensajería pueda consultarlos cuando se implemente.

La información de conexión requerida para RabbitMQ y la base de datos de mensajería se registra en el almacén de clave/valor (KV) de Consul , que es una ubicación común a la que pueden acceder todos los servicios a medida que se implementan. El almacén Consul KV no es un lugar estándar para almacenar este tipo de datos, pero este tutorial lo utiliza para simplificar.

  1. Reemplace el contenido de infraestructura/messenger-deploy.sh (creado en el Paso 2 de la sección anterior ) con lo siguiente:

    #!/bin/bashset -e # Esta configuración requiere una nueva confirmación para cambiar NODE_ENV=production PORT=4000 JSON_BODY_LIMIT=100kb # Configuración de la base de datos de Postgres extrayendo información del # sistema 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) # Configuración de RabbitMQ extrayendo información del sistema 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 PUERTO="${PORT} " \ -e LÍMITE_DEL_CUERPO_JSON="${JSON_BODY_LIMIT} " \ -e PGUSER="${POSTGRES_USER} " \ -e PGPORT="${PGPORT} " \ -e PGHOST="${PGHOST} " \ -e AMQPPORT="${AMQPPORT} " \ -e AMQPHOST="${AMQPHOST} " \ mensajero
    

    Este script ejemplifica dos tipos de configuración:

    • Configuración especificada directamente en el script de implementación : establece el entorno de implementación ( NODE_ENV ) y el puerto ( PORT ), y cambia JSON_BODY_LIMIT a 100 KB, un valor más realista que 20 bytes.
    • Configuración consultada desde fuentes externas : obtiene los valores de las variables de entorno POSTGRES_USER , PGPORT , PGHOST , AMQPHOST y AMQPPORT del almacén Consul KV. Los valores de las variables de entorno en el almacén de Consul KV se establecen en los dos pasos siguientes.
  2. Abra messenger-db-deploy.sh y agregue lo siguiente para crear un script de implementación inicial para messenger-database :

    #!/bin/bashset -e PUERTO=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 CONTRASEÑA_POSTGRES="${POSTGRES_PASSWORD} " \ -e PGPORT="${PORT} " \ -e PGDATA=/var/lib/postgresql/data/pgdata \ --network mm_2023 \ postgres:15.1 # Registrar detalles sobre la base de datos con Consul curl -X PUT http://localhost:8500/v1/kv/messenger-db-port \ -H "Content-Type: aplicação/json" \ -d "${PORT} " curl -X PUT http://localhost:8500/v1/kv/messenger-db-host \ -H "Content-Type: aplicação/json" \ -d 'messenger-db' # Esto coincide con el indicador "--name" anterior # (el nombre del host) curl -X PUT http://localhost:8500/v1/kv/messenger-db-application-user \ -H "Content-Type: aplicação/json" \ -d "${POSTGRES_USER} "
    

    Además de definir la configuración que puede consultar el servicio de mensajería en el momento de la implementación, el script ilustra los mismos dos conceptos que el script inicial para el servicio de mensajería de Crear scripts de implementación inicial ):

    • Especifica cierta configuración directamente en el script de implementación, en este caso para indicar a la base de datos PostgreSQL el puerto en el que ejecutar y el nombre de usuario del usuario predeterminado.
    • Ejecuta Docker con el indicador -e para inyectar variables de entorno en el contenedor en tiempo de ejecución. También establece el nombre del contenedor en ejecución en messenger-db , que se convierte en el nombre de host de la base de datos en la red Docker que creó cuando lanzó el servicio de plataforma en el Paso 2 de Configuración .
  3. En una implementación real, generalmente es el equipo de la Plataforma (o similar) el que maneja la implementación y el mantenimiento de un servicio como RabbitMQ en el repositorio de la plataforma , tal como lo hace para la base de datos de Messenger en el repositorio de Messenger . Luego, el equipo de la Plataforma se asegura de que los servicios que dependen de ella puedan descubrir la ubicación de esa infraestructura. Para los fines de este tutorial, configure usted mismo los valores de RabbitMQ:

    curl -X PUT --silent --output /dev/null --show-error --fail \ -H "Tipo de contenido: aplicação/json" \
    -d "rabbitmq" \
    <a href="http://localhost:8500/v1/kv/amqp-host">http://localhost:8500/v1/kv/amqp-host</a>
    
    curl -X PUT --silent --output /dev/null --show-error --fail \
    -H "Tipo de contenido: aplicação/json" \
    -d "5672" \
    <a href="http://localhost:8500/v1/kv/amqp-port">http://localhost:8500/v1/kv/puerto-amqp</a>
    

    (Quizás te preguntes por qué se utiliza amqp para definir variables de RabbitMQ: es porque AMQP es el protocolo utilizado por RabbitMQ).

Establecer un secreto en los scripts de implementación

Solo falta un dato (crítico) en los scripts de implementación del servicio de mensajería : ¡la contraseña para la base de datos del mensajero !

Nota:  La gestión de secretos no es el foco de este tutorial, por lo que para simplificar el secreto se define en los archivos de implementación. Nunca hagas esto en un entorno real (desarrollo, prueba o producción), ya que crea un enorme riesgo de seguridad .

Para aprender sobre la gestión adecuada de secretos, consulte la Unidad 2, Gestión de secretos de microservicios 101 de Microservicios de marzo de 2023. (Spoiler: una herramienta de gestión de secretos es el único método verdaderamente seguro para almacenar secretos).

  1. Reemplace el contenido de infraestructura/messenger-db-deploy.sh con lo siguiente para almacenar el secreto de contraseña para la base de datos de messenger en el almacén Consul KV:

    #!/bin/bashset -e PORT=5432 POSTGRES_USER=postgres # NOTA: Nunca hagas esto en una implementación del mundo real. Almacene las contraseñas # únicamente en un almacén de secretos cifrados.
    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 CONTRASEÑA_POSTGRES="${POSTGRES_PASSWORD} " \ -e PGPORT="${PORT} " -e PGDATA=/var/lib/postgresql/data/pgdata --network mm_2023 postgres:15.1 echo "Registrar clave messenger-db-port\n" curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-port -H "Tipo de contenido: aplicação/json" -d${PORT} " echo "Registrar clave 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: aplicação/json" \ -d 'messenger-db-primary' # Esto coincide con el indicador "--name" anterior # que para nuestra configuración significa el nombre de host echo "Registrar clave 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: aplicação/json" \ -d "${POSTGRES_USER} " echo "Registrar clave messenger-db-password-nunca-haga-esto\n" curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-password-nunca-haga-esto \ -H "Tipo-de-contenido: aplicação/json" \ -d "${POSTGRES_PASSWORD} " printf "\nSe terminó de registrar los detalles de postgres con Consul\n"
    
  2. Reemplace el contenido de infraestructura/messenger-deploy.sh con lo siguiente para obtener el secreto de contraseña de la base de datos de messenger del almacén Consul KV:

    #!/bin/bashset -e # Esta configuración requiere una nueva confirmación para cambiar NODE_ENV=production PORT=4000 JSON_BODY_LIMIT=100kb # Configuración de la base de datos de Postgres extrayendo información del # sistema 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) # NOTA: Nunca hagas esto en una implementación del mundo real. Almacene las contraseñas # únicamente en un almacén de secretos cifrados.
    PGPASSWORD=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-password-never-do-this?raw=true) # Configuración de RabbitMQ extrayendo del sistema 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 PUERTO="${PORT} " \ -e LÍMITE_DEL_CUERPO_JSON="${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 \ mensajero
    

Ejecutar los scripts de implementación

  1. Cambie al directorio de la aplicación en el repositorio de Messenger y cree la imagen de Docker para el servicio de Messenger :

    cd ../appdocker build -t messenger .
    
  2. Verifique que solo se estén ejecutando los contenedores que pertenecen al servicio de la plataforma :

    docker ps --format '{{.Names}}' consul-server consul-client rabbitmq
    
  3. Cambie a la raíz del repositorio de mensajería e implemente la base de datos de mensajería y el servicio de mensajería :

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

    El script messenger-db-deploy.sh inicia la base de datos de messenger y registra la información apropiada con el sistema (que en este caso es el almacén Consul KV).

    Luego, el script messenger-deploy.sh inicia la aplicação y extrae la configuración registrada por messenger-db-deploy.sh del sistema (nuevamente, el almacén Consul KV).

    Pista: Si un contenedor no puede iniciarse, elimine el segundo parámetro del comando docker run (la línea -d \ ) en el script de implementación y ejecute el script nuevamente. Luego, el contenedor se inicia en primer plano, lo que significa que sus registros aparecen en la terminal y podrían identificar el problema. Cuando resuelva el problema, restaure la línea -d \ para que el contenedor real se ejecute en segundo plano.

  4. Envíe una solicitud de verificación de estado simple a la aplicação para verificar que la implementación se realizó correctamente:

    curl localhost:4000/salud curl: (7) No se pudo conectar al puerto localhost 4000 después de 11 ms: Conexión rechazada
    

    ¡Ups, fallo! Resulta que todavía falta una pieza crítica de configuración y el servicio de mensajería no está expuesto al sistema más amplio. Funciona correctamente dentro de la red mm_2023 , pero esa red solo es accesible desde Docker.

  5. Detenga el contenedor en ejecución para prepararlo para crear una nueva imagen en el próximo desafío:

    docker rm $(docker stop $(docker ps -a -q --filter ancestor=messenger --format="{{.ID}}"))
    

Desafío 3: Exponer un servicio al mundo exterior

En un entorno de producción, generalmente no se exponen los servicios directamente. En lugar de ello, sigue un patrón común de microservicios y coloca un servicio de proxy inverso delante de tu servicio principal.

En este desafío, usted expone el servicio de mensajería al mundo exterior configurando el descubrimiento de servicios: el registro de nueva información de servicio y la actualización dinámica de esa información a medida que acceden otros servicios. Para ello, utiliza estas tecnologías:

  • Consul , un registro de servicios dinámico, y plantilla de Consul , una herramienta para actualizar dinámicamente un archivo basado en datos de Consul
  • NGINX de código abierto , como un proxy inverso y balanceador de carga que expone un único punto de entrada para su servicio de mensajería que estará compuesto por múltiples instancias individuales de la aplicação que se ejecuta en contenedores.

Para obtener más información sobre el descubrimiento de servicios, consulte Cómo hacer que un servicio esté disponible como configuración en Mejores prácticas para configurar aplicaciones de microservicios en nuestro blog.

Configurar Consul

El archivo app/consul/index.mjs en el repositorio de mensajería contiene todo el código necesario para registrar el servicio de mensajería con Consul al inicio y cancelar su registro al apagar correctamente. Expone una función, register , que registra cualquier servicio recién implementado con el registro de servicios de Consul.

  1. En su editor de texto preferido, abra app/index.mjs y agregue el siguiente fragmento después de las otras declaraciones de importación , para importar la función de registro desde app/consul/index.mjs :

    importar { registrarse como registerConsul } desde "./consul/index.mjs";
    

    Luego modifique la sección SERVER START al final del script como se muestra, para llamar a registerConsul() después de que la aplicação se haya iniciado:

    /* ================= INICIO DEL SERVIDOR ================== */ app.listen(port, async () => { console.log(`messenger_service escuchando en el puerto${port} `); registerConsul(); }); exportar aplicación predeterminada;
    
  2. Abra el esquema de convicto en app/config/config.mjs y agregue los siguientes valores de configuración después de la clave jsonBodyLimit que agregó en el Paso 1 del Ejemplo 2 .

      consulServiceName: { doc: "El nombre con el que se registra el servicio en Consul. Si no se especifica, el servicio no está registrado.
    Formato: "*",
    Predeterminado: nulo,
    Entorno: "NOMBRE_SERVICIO_CONSUL",
    },
    consulHost: {
    doc: "El host donde se ejecuta el cliente Consul", formato: Cadena,
    predeterminado: "consul-client",
    env: "CONSUL_HOST",
    },
    consulPort: {
    doc: "El puerto del cliente Consul",
    formato: "puerto",
    predeterminado: 8500,
    env: "CONSUL_PORT",
    

    Esto configura el nombre bajo el cual se registra un nuevo servicio y define el nombre de host y el puerto para el cliente Consul. En el siguiente paso, modifique el script de implementación del servicio de mensajería para incluir esta nueva conexión de Consul y la información de registro del servicio.

  3. Abra Infrastructure/messenger-deploy.sh y reemplace su contenido con lo siguiente para incluir en la configuración del servicio de mensajería la información de conexión y registro del servicio de Consul que configuró en el paso anterior:

    #!/bin/bashset -e # Esta configuración requiere una nueva confirmación para cambiar NODE_ENV=production PORT=4000 JSON_BODY_LIMIT=100kb CONSUL_SERVICE_NAME="messenger" # El host y el puerto de Consul se incluyen en cada host ya que # no podemos consultar a Consul hasta que los conozcamos CONSUL_HOST="${CONSUL_HOST} " CONSUL_PORT="${CONSUL_PORT} " # Configuración de la base de datos de Postgres extrayendo información del # sistema 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") # NOTA: Nunca hagas esto en una implementación del mundo real. Almacene las contraseñas # únicamente en un almacén de secretos cifrados.
    PGPASSWORD=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-password-never-do-this?raw=true") # Configuración de RabbitMQ extrayendo del sistema 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 PUERTO="${PORT} " \ -e LÍMITE_DEL_CUERPO_JSON="${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 NOMBRE_SERVICIO_CÓNSUL="${CONSUL_SERVICE_NAME} " \ --network mm_2023 \ mensajero
    

    Los principales aspectos a tener en cuenta son:

    • La variable de entorno CONSUL_SERVICE_NAME le dice a la instancia del servicio de mensajería qué nombre usar cuando se registra en Consul.
    • Las variables de entorno CONSUL_HOST y CONSUL_PORT son para el cliente Consul que se ejecuta en la ubicación donde se ejecuta el script de implementación.

    Nota:  En una implementación del mundo real, este es un ejemplo de configuración que debe acordarse entre los equipos: el equipo responsable de Consul debe proporcionar las variables de entorno CONSUL_HOST y CONSUL_PORT en todos los entornos, ya que un servicio no puede consultar Consul sin esta información de conexión.

  4. En la terminal de la aplicación, cambie al directorio de la aplicación , detenga cualquier instancia en ejecución del servicio de mensajería y reconstruya la imagen de Docker para integrar el nuevo código de registro del servicio:

    cd appdocker rm $(docker stop $(docker ps -a -q --filter ancestor=messenger --format="{{.ID}}"))
    docker build -t messenger .
    
  5. Navegue a http://localhost:8500 en un navegador para ver la interfaz de usuario de Consul en acción (aunque todavía no sucede nada interesante).

  6. En la raíz del repositorio de mensajería , ejecute el script de implementación para iniciar una instancia del servicio de mensajería :

    CONSUL_HOST=cliente-consul CONSUL_PORT=8500 ./infraestructura/messenger-deploy.sh
    
  7. En la interfaz de usuario de Consul en el navegador, haga clic en Servicios en la barra de encabezado para verificar que se esté ejecutando un solo servicio de mensajería .

    Pestaña Servicios de la interfaz de usuario de Consul con una instancia de cada uno de los servicios de Consul y Messenger

  8. Ejecute el script de implementación unas cuantas veces más para iniciar más instancias del servicio de mensajería . Verifique en la interfaz de usuario de Consul que se estén ejecutando.

    CONSUL_HOST=cliente-consul CONSUL_PORT=8500 ./infraestructura/messenger-deploy.sh
    

    Pestaña 'mensajero' de la interfaz de usuario de Consul con cinco instancias del servicio

Configurar NGINX

El siguiente paso es agregar NGINX Open Source como proxy inverso y balanceador de carga para enrutar el tráfico entrante a todas las instancias de mensajería en ejecución.

  1. En la terminal de la aplicación, cambie el directorio a la raíz del repositorio de Messenger y cree un directorio llamado load-balancer y tres archivos:

    mkdir balanceador-de-cargarcd balanceador-de-carga
    toca nginx.ctmpl
    toca consul-template-config.hcl
    toca Dockerfile
    

    El Dockerfile define el contenedor donde se ejecutan las plantillas NGINX y Consul. La plantilla de Consul usa los otros dos archivos para actualizar dinámicamente los upstreams de NGINX cuando el servicio de mensajería cambia (las instancias de servicio aparecen o desaparecen) en su registro de servicio.

  2. Abra el archivo nginx.ctmpl creado en el Paso 1 y agregue el siguiente fragmento de configuración de NGINX, que la plantilla de Consul usa para actualizar dinámicamente el grupo ascendente de NGINX:

    servicio de mensajería ascendente { {{- rango servicio "mensajero" }}
    servidor {{ .Dirección }}:{{ .Puerto }};
    {{- fin }}
    }
    
    servidor {
    escuchar 8085;
    nombre_servidor localhost;
    
    ubicación / {
    contraseña_proxy http://servicio_mensajero;
    add_header Host_ascendente $upstream_addr;
    }
    }
    

    Este fragmento agrega la dirección IP y el número de puerto de cada instancia de servicio de mensajería registrada con Consul al grupo ascendente messenger_service de NGINX. NGINX envía las solicitudes entrantes al conjunto definido dinámicamente de instancias de servicio ascendentes.

  3. Abra el archivo consul-template-config.hcl creado en el Paso 1 y agregue la siguiente configuración:

    cónsul { dirección = "consul-client:8500"
    
    retry {
    habilitado = verdadero
    intentos = 12
    retroceso = "250ms"
    }
    }
    plantilla {
    origen = "/usr/templates/nginx.ctmpl"
    destino = "/etc/nginx/conf.d/default.conf"
    perms = 0600
    comando = "si [ -e /var/run/nginx.pid ]; entonces nginx -s recargar; de lo contrario nginx; fi"
    }
    

    Esta configuración para la plantilla Consul le indica que vuelva a renderizar la plantilla de origen (el fragmento de configuración de NGINX creado en el paso anterior), la coloque en el destino especificado y, finalmente, ejecute el comando especificado (que le indica a NGINX que vuelva a cargar su configuración).

    En la práctica, esto significa que se crea un nuevo archivo default.conf cada vez que se registra, actualiza o cancela el registro de una instancia de servicio en Consul. Luego, NGINX vuelve a cargar su configuración sin tiempo de inactividad, lo que garantiza que NGINX tenga un conjunto de servidores (instancias de servicio de mensajería ) actualizado y en buen estado a los que pueda enviar tráfico.

  4. Abra el archivo Dockerfile creado en el Paso 1 y agregue el siguiente contenido, que crea el servicio NGINX. (No es necesario que comprenda el Dockerfile para los fines de este tutorial, pero el código está documentado en línea para su conveniencia).

    DESDE nginx:1.23.1 ARG CONSUL_TEMPLATE_VERSION=0.30.0 # Establezca una variable de entorno para la ubicación del clúster Consul. De forma predeterminada, intenta resolverse en consul-client:8500 , que es el comportamiento si Consul se ejecuta como un contenedor en el mismo host y está vinculado a este contenedor NGINX (con el alias consul, por supuesto). Pero esta variable de entorno también puede ser anulada cuando se inicia el contenedor si queremos resolverlo en otra dirección.
    
    ENV CONSUL_URL consul-client:8500 # Descargar la versión especificada de la plantilla Consul ADD https://releases.hashicorp.com/consul-template/${CONSUL_TEMPLATE_VERSION} /plantilla-consul_${CONSUL_TEMPLATE_VERSION} _linux_amd64.zip /tmp EJECUTAR 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 COPIAR consul-template-config.hcl ./consul-template-config.hcl COPIAR nginx.ctmpl /usr/templates/nginx.ctmpl EXPONER 8085 SEÑAL DE PARADA SIGQUIT CMD ["dumb-init", "consul-template", "-config=consul-template-config.hcl"]
    
  5. Construir una imagen de Docker:

    docker build -t messenger-lb .
    
  6. Cambie a la raíz del directorio del mensajero y cree un archivo llamado messenger-load-balancer-deploy.sh como archivo de implementación para el servicio NGINX (al igual que con el resto de los servicios que ha implementado a lo largo del tutorial). Dependiendo de su entorno, es posible que necesite anteponer sudo al comando chmod :

    cd ..
    touch infraestructura/messenger-load-balancer-deploy.sh
    chmod +x infraestructura/messenger-load-balancer-deploy.sh
    
  7. Abra messenger-load-balancer-deploy.sh y agregue el siguiente contenido:

    #!/bin/bashset -e # El host y el puerto de Consul se incluyen en cada host ya que # no podemos consultar a Consul hasta que los conozcamos 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. Ahora que tiene todo en su lugar, implemente el servicio NGINX:

    CONSUL_HOST=cliente-consul CONSUL_PORT=8500 ./infraestructura/messenger-load-balancer-deploy.sh
    
  9. Vea si puede acceder al servicio de mensajería externamente:

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

    ¡Funciona! NGINX ahora equilibra la carga en todas las instancias del servicio de mensajería que se han creado. Puedes saberlo porque el encabezado X-Forwarded-For muestra las mismas direcciones IP del servicio de mensajería que las de la interfaz de usuario de Consul en el paso 8 de la sección anterior.

Desafío 4: Migrar una base de datos utilizando un servicio como ejecutor de trabajos

Las aplicações grandes a menudo utilizan “ejecutores de trabajos” con pequeños procesos de trabajo que pueden usarse para realizar tareas únicas como modificar datos (algunos ejemplos son Sidekiq y Celery ). Estas herramientas a menudo requieren infraestructura de soporte adicional, como Redis o RabbitMQ . En este caso, utiliza el propio servicio de mensajería como un “ejecutor de tareas” para ejecutar tareas puntuales. Esto tiene sentido porque ya es muy pequeño, es totalmente capaz de interactuar con la base de datos y otras piezas de infraestructura de las que depende, y se ejecuta de forma completamente independiente de la aplicação que sirve el tráfico.

Hacer esto tiene tres beneficios:

  1. El ejecutor de trabajos (incluidos los scripts que ejecuta) pasa por exactamente los mismos controles y procesos de revisión que el servicio de producción.
  2. Los valores de configuración, como los usuarios de la base de datos, se pueden cambiar fácilmente para que la implementación de producción sea más segura. Por ejemplo, puede ejecutar el servicio de producción con un usuario de “privilegios bajos” que solo pueda escribir y realizar consultas en tablas existentes. Puede configurar una instancia de servicio diferente para realizar cambios en la estructura de la base de datos como un usuario con mayores privilegios capaz de crear y eliminar tablas.
  3. Algunos equipos ejecutan trabajos desde instancias que también manejan el tráfico de producción de servicios. Esto es peligroso porque los problemas con el trabajo pueden afectar las otras funciones que la aplicação en el contenedor está realizando. Evitar este tipo de cosas es la razón por la que utilizamos microservicios en primer lugar, ¿no es así?

En este desafío, explorarás cómo se puede modificar un artefacto para cumplir una nueva función cambiando algunos valores de configuración de la base de datos y migrando la base de datos del mensajero para usar los nuevos valores y probar su rendimiento .

Migrar la base de datos de Messenger

Para una implementación de producción en el mundo real, puede crear dos usuarios distintos con diferentes permisos: un “usuario de aplicação ” y un “usuario migrador”. Para simplificar, en este ejemplo se utiliza el usuario predeterminado como usuario de la aplicação y se crea un usuario migrador con privilegios de superusuario. En una situación real, vale la pena dedicar más tiempo a decidir qué permisos mínimos específicos necesita cada usuario en función de su rol.

  1. En la terminal de la aplicación, cree un nuevo usuario PostgreSQL con privilegios de superusuario:

    echo "CREAR USUARIO messenger_migrator CON CONTRASEÑA DE SUPERUSUARIO 'migrator_password';" | docker exec -i messenger-db-primary psql -U postgres
    
  2. Abra el script de implementación de la base de datos ( infrastructure/messenger-db-deploy.sh ) y reemplace su contenido para agregar las credenciales del nuevo usuario.

    Nota:  Tomémonos el tiempo para reiterar: para una implementación en el mundo real, NUNCA coloque secretos como credenciales de base de datos en un script de implementación o en cualquier otro lugar que no sea una herramienta de administración de secretos. Para más detalles, consulte la Unidad 2: Secretos de microservicios Gestión 101 de microservicios Marzo de 2023.

    #!/bin/bash set -e PORT=5432 POSTGRES_USER=postgres # NOTA: Nunca hagas esto en una implementación del mundo real. Almacene las contraseñas # solo en un almacén de secretos cifrados. # Debido a que nos centraremos en otros conceptos en este tutorial, # configuramos la contraseña de esta manera aquí para mayor comodidad.
    POSTGRES_PASSWORD=postgres # Usuario de migración POSTGRES_MIGRATOR_USER=messenger_migrator # NOTA: Como se mencionó anteriormente, nunca haga esto en una implementación real.
    POSTGRES_MIGRATOR_PASSWORD=contraseña_del_migrador docker run \ --rm \ --name messenger-db-primary \ -d \ -v db-data:/var/lib/postgresql/data/pgdata \ -e POSTGRES_USER="${POSTGRES_USER} " \ -e CONTRASEÑA_POSTGRES="${POSTGRES_PASSWORD} " \ -e PGPORT="${PORT} " -e PGDATA=/var/lib/postgresql/data/pgdata --network mm_2023 postgres:15.1 echo "Registrar clave messenger-db-port\n" curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-port -H "Tipo de contenido: aplicação/json" -d${PORT} " echo "Registrar clave 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: aplicação/json" \ -d 'messenger-db-primary' # Esto coincide con el indicador "--name" anterior # que para nuestra configuración significa el nombre de host echo "Registrar clave 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: aplicação/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 "Tipo de contenido: aplicação/json" \ -d "${POSTGRES_PASSWORD} " echo "Registrar clave 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 "Tipo de contenido: aplicação/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 "Tipo de contenido: aplicação/json" \ -d "${POSTGRES_MIGRATOR_PASSWORD} " printf "\nSe terminó de registrar los detalles de postgres con Consul\n"
    

    Este cambio solo agrega el usuario migrador al conjunto de usuarios que se configura en Consul después de que se implementa la base de datos.

  3. Cree un nuevo archivo en el directorio de infraestructura llamado messenger-db-migrator-deploy.sh (nuevamente, es posible que deba anteponer el comando chmod con sudo ):

    toque infraestructura/messenger-db-migrator-deploy.shchmod +x infraestructura/messenger-db-migrator-deploy.sh
    
  4. Abra messenger-db-migrator-deploy.sh y agregue lo siguiente:

    #!/bin/bashset -e # Esta configuración requiere una nueva confirmación para cambiar NODE_ENV=production PORT=4000 JSON_BODY_LIMIT=100kb CONSUL_SERVICE_NAME="messenger-migrator" # El host y el puerto de Consul se incluyen en cada host ya que # no podemos consultar a Consul hasta que los conozcamos CONSUL_HOST="${CONSUL_HOST} " CONSUL_PORT="${CONSUL_PORT} " # Obtener el nombre de usuario y la contraseña del migrador 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) # NOTA: Nunca hagas esto en una implementación del mundo real. Almacene las contraseñas # únicamente en un almacén de secretos cifrados.
    PGPASSWORD=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-migrator-password-never-do-this?raw=true") # Configuración de RabbitMQ extrayendo del sistema 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 PUERTO="${PORT} " \ -e LÍMITE_DEL_CUERPO_JSON="${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 NOMBRE_SERVICIO_CÓNSUL="${CONSUL_SERVICE_NAME} " \ --network mm_2023 \ mensajero
    

    Este script es bastante similar al script Infrastructure/messenger-deploy.sh en su forma final, que creó en el Paso 3 de Configurar Consul . La principal diferencia es que CONSUL_SERVICE_NAME es messenger-migrator en lugar de messenger , y PGUSER corresponde al superusuario “migrator” que creó en el Paso 1 anterior.

    Es importante que CONSUL_SERVICE_NAME sea messenger-migrator . Si se configurara como Messenger , NGINX pondría automáticamente este servicio en rotación para recibir llamadas API y no está destinado a manejar ningún tráfico.

    El script despliega una instancia de corta duración en el rol de migrador. Esto evita que cualquier problema con la migración afecte el servicio de tráfico por parte de las instancias del servicio de mensajería principal.

  5. Volver a implementar la base de datos PostgreSQL. Dado que en este tutorial utiliza scripts bash , deberá detener y reiniciar el servicio de base de datos. En una aplicação de producción, normalmente solo se ejecuta un script de infraestructura como código para agregar únicamente los elementos que han cambiado.

    docker stop messenger-db-primaryCONSUL_HOST=consul-client CONSUL_PORT=8500 ./infraestructura/messenger-db-deploy.sh
    
  6. Implementar el servicio de migración de bases de datos PostgreSQL:

    CONSUL_HOST=cliente-consul CONSUL_PORT=8500 ./infraestructura/messenger-db-migrator-deploy.sh
    
  7. Verifique que la instancia se esté ejecutando como se espera:

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

    También puedes verificar en la interfaz de usuario de Consul que el servicio de migración de base de datos se haya registrado correctamente en Consul como messenger-migrator (nuevamente, no se registra con el nombre de messenger porque no maneja tráfico):

    Pestaña Servicios de la interfaz de usuario de Consul con una instancia de cada servicio de Consul y de Messenger-migrator, y cuatro instancias del servicio de Messenger

  8. ¡Ahora, el paso final: ejecutar los scripts de migración de la base de datos! Estos scripts no se parecen a ningún script de migración de base de datos real, pero utilizan el servicio messenger-migrator para ejecutar scripts específicos de la base de datos. Una vez migrada la base de datos, detenga el servicio messenger-migrator :

    docker exec -i -e PGDATABASE=postgres -e CREATE_BD_NAME=messenger messenger-migrator node scripts/create-db.mjs docker exec -i messenger-migrator node scripts/create-schema.mjs
    docker exec -i messenger-migrator node scripts/create-seed-data.mjs
    docker stop messenger-migrator
    

Pruebe el servicio de mensajería en acción

Ahora que has migrado la base de datos de mensajería a su formato final, ¡el servicio de mensajería finalmente está listo para que lo veas en acción! Para hacer esto, ejecute algunas consultas curl básicas contra el servicio NGINX (usted configuró NGINX como el punto de entrada del sistema en Configurar NGINX ).

Algunos de los siguientes comandos utilizan la biblioteca jq para formatear la salida JSON. Puede instalarlo según sea necesario u omitirlo desde la línea de comando si lo desea.

  1. Crear una conversación:

    curl -d '{"participant_ids": [1, 2]}' -H "Tipo de contenido: aplicação/json" -X POST 'http://localhost:8085/conversations' { "conversation": { "id": "1", "insertado_en": " AAAA - MM - DD T06:41:59.000Z" } }
    
  2. Envía un mensaje a la conversación desde un usuario con ID 1:

    curl -d '{"contenido": "Este es el primer mensaje"}' -H "Id. de usuario: 1" -H "Tipo de contenido: aplicação/json" -X POST 'http://localhost:8085/conversaciones/1/mensajes' | jq { "mensaje": { "id": "1", "contenido": "Este es el primer mensaje", "índice": 1, "id_de_usuario": 1, "nombre de usuario": "James Blanderphone", "conversación_id": 1, "insertado_en": " AAAA - MM - DD T06:42:15.000Z" } }
    
  3. Responder con un mensaje de un usuario diferente (con ID 2):

    curl -d '{"contenido": "Este es el segundo mensaje"}' -H "Id. de usuario: 2" -H "Tipo de contenido: aplicação/json" -X POST 'http://localhost:8085/conversaciones/1/mensajes' | jq { "mensaje": { "id": "2", "contenido": "Este es el segundo mensaje", "índice": 2, "id_de_usuario": 2, "nombre de usuario": "Arcoíris de Normandía", "id de conversación": 1, "insertado_en": " AAAA - MM - DD T06:42:25.000Z" } }
    
  4. Obtener los mensajes:

    curl -X GET 'http://localhost:8085/conversaciones/1/mensajes' | jq { "mensajes": [ { "id": "1", "contenido": "Este es el primer mensaje", "user_id": "1", "id_del_canal": "1", "índice": "1", "insertado_en": " AAAA - MM - DD T06:42:15.000Z", "nombre de usuario": "James Blanderphone" }, { "identificación": "2", "contenido": "Este es el segundo mensaje", "user_id": "2", "id_del_canal": "1", "índice": "2", "insertado_en": " AAAA - MM - DD T06:42:25.000Z", "nombre de usuario": "Cabezudo normando"
    

Limpiar

¡Has creado una cantidad significativa de contenedores e imágenes a lo largo de este tutorial! Utilice los siguientes comandos para eliminar cualquier contenedor e imagen de Docker que no desee conservar.

  • Para eliminar cualquier contenedor Docker en ejecución:

    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)
    
  • Para eliminar los servicios de la plataforma :

    # Desde el repositorio de la plataforma docker compose down
    
  • Para eliminar todas las imágenes de Docker utilizadas en el tutorial:

    Docker RMI Messenger
    Docker RMI Messenger-LB
    Docker RMI Postgres: 15.1
    Docker RMI Hashicorp/Consul: 1.14.4
    Docker RMI RabbitMQ: 3.11.4-Management-Alpine
    

Próximos pasos

Podrías estar pensando: “Parece mucho trabajo montar algo tan sencillo”, ¡y estarías en lo cierto! Para pasar a una arquitectura centrada en microservicios es necesario ser meticuloso en cómo estructurar y configurar los servicios. A pesar de toda la complejidad, has logrado avances importantes:

  • Configura una configuración centrada en microservicios que sea fácilmente comprensible para otros equipos.
  • Configura el sistema de microservicios para que sea algo flexible tanto en términos de escalabilidad como de uso de los distintos servicios involucrados.

Para continuar su educación sobre microservicios, consulte Microservicios de marzo de 2023. La Unidad 2, Microservices Secrets Management 101 , proporciona una descripción general detallada pero fácil de usar de la gestión de secretos en entornos de microservicios.


"Esta publicación de blog puede hacer referencia a productos que ya no están disponibles o que ya no reciben soporte. Para obtener la información más actualizada sobre los productos y soluciones F5 NGINX disponibles, explore nuestra familia de productos NGINX . NGINX ahora es parte de F5. Todos los enlaces anteriores de NGINX.com redirigirán a contenido similar de NGINX en F5.com.