En la entrega anterior de este artículo, exploramos aspectos esenciales de AWS App Mesh. En esta segunda parte, nos sumergiremos en un ejemplo práctico para comprender su funcionamiento. Durante este ejercicio, nos centraremos en aspectos críticos, como la comunicación entre microservicios dentro de la malla App Mesh y cómo exponerlos al balanceador con un AWS Application Load Balancer (ALB) por medio de una puerta de entrada (Virtual Gateway).
¡Acompáñanos en este recorrido práctico!
Para la demostración, emplearemos dos microservicios implementados en Node.js y Python. Estos microservicios exponen los siguientes APIs:
/call_and_wait/[nombre_servicio_en_cloudmap]_[puerto]/[tiempo_de_espera]
wait/[tiempo_de_espera]
Descripción de los parámetros:
nombre_servicio_en_cloudmap
: Nombre del microservicio registrado en AWS Cloud Map.puerto:
Puerto del microservicio.tiempo_de_espera
: Tiempo en segundos para ejecutar un período de espera.A continuación, detallaremos la secuencia de pasos a seguir para llevar a cabo la demostración:
Amazon Elastic Container Registry (ECR) es un servicio que proporciona un registro de contenedores seguro y completamente administrado en AWS.
Puesto que el enfoque principal de este artículo es la configuración de App Mesh, no profundizaré en el código de los microservicios. Por lo tanto, el código proporcionado es únicamente de referencia.
NodeJs:
const express = require("express");
const http = require("http");
...
app.get("/call_and_wait/:namespace/:wait_time", (req, res) => {
const namespace = req.params.namespace;
const waitTime = parseInt(req.params.wait_time, 10);
let externalServiceUrl;
...
...
Python:
from flask import Flask
...
@app.route('/', methods=['GET'])
def status():
return "OK"
@app.route('/wait/<int:wait_time>', methods=['GET'])
@xray_recorder.capture('wait_endpoint')
def wait(wait_time):
time.sleep(wait_time)
return f"Waited for {wait_time} seconds."
app.run(host='0.0.0.0', port=5000, debug=False)
...
...
Después de compilar nuestras aplicaciones, procederemos a crear las imágenes correspondientes y cargarlas en AWS ECR mediante los comandos siguientes:
docker build -t <nombre_aplicacion> <path_dockerfile_aplicacion>
docker tag crystal-service:latest $<nombre_aplicacion>:local
docker push $<url_ecr>:local
Antes de proceder, crearemos la malla para nuestro ecosistema de microservicios. Es crucial destacar la configuración del filtro de salida, que para nuestro caso se configura a Allow external traffic, con lo que permitimos el tráfico externo.
{
"meshName": "pxxx-local-appmesh",
"spec": {
"egressFilter": {
"type": "ALLOW_ALL"
}
}
}
Estableceremos la configuración de nodos y servicios virtuales para cada uno de nuestros microservicios, tal como se ilustra a continuación:
Al establecer el nodo virtual, es esencial seleccionar AWS Cloud Map como método de descubrimiento de servicio. También, debemos configurar el puerto del listener y establecer los servicios backends para definir la comunicación entre microservicios. En nuestro escenario, no es necesario modelar los servicios backends, ya que estamos configurando la malla para permitir tráfico externo.
La configuración de nodo virtual es la siguiente:
{
"meshName": "pxxx-local-appmesh",
"virtualNodeName": "ms_python",
"spec": {
"serviceDiscovery": {
"awsCloudMap": {
"namespaceName": "pxxx.local",
"serviceName": "ms_python",
"attributes": []
}
},
"listeners": [
...
],
"backends": [
{
"virtualService": {
"virtualServiceName": "ms_nodejs.pxxx.local"
}
}
],
"backendDefaults": {
"clientPolicy": {}
},
"logging": {
"accessLog": {
"file": {
"path": "/dev/stdout"
}
}
}
},
"meshOwner": "XXXXXXXXX"
Ahora estableceremos el servicio virtual para ms_python, en donde seleccionaremos como proveedor un nodo virtual. En el caso de que necesitemos trabajar con dos versiones de un aplicativo, debemos crear un virtual router, con rutas a cada versión y seleccionar como proveedor el virtual router.
La configuración de servicio virtual es la siguiente:
{
"meshName": "pxxx-local-appmesh",
"virtualServiceName": "ms_python.pxxx.local",
"spec": {
"provider": {
"virtualNode": {
"virtualNodeName": "ms_python"
}
}
},
"meshOwner": "XXXXXXXXXXXX"
}
Para habilitar el tráfico externo, específicamente desde un Load Balancer (ALB), es necesario crear un Virtual Gateway. En este, configuraremos los listeners correspondientes a nuestras aplicaciones y crearemos Gateway Routes para cada microservicio. Puedes encontrar la configuración detallada de nuestro Virtual Gateway a continuación:
{
"meshName": "pxxx-local-appmesh",
"virtualGatewayName": "pxxx-gateway",
"spec": {
"listeners": [
{
"portMapping": {
"port": 5000,
"protocol": "http"
}
},
{
"portMapping": {
"port": 3000,
"protocol": "http"
}
},
{
"portMapping": {
"port": 80,
"protocol": "http"
}
}
]
},
"meshOwner": "XXXXXXXXXXXXXXX"
}
Antes de desplegar los servicios de nuestros microservicios en AWS ECS, es necesario construir la definición de tarea (Task Definitions) para cada uno. Los aspectos cruciales de dicha definición incluyen:
"dependsOn": [
{
"containerName": "envoy",
"condition": "HEALTHY"
}
],
"environment": [
{
"name": "XRAY_DAEMON_PORT ",
"value": "2000"
},
{
"name": "APPMESH_RESOURCE_ARN",
"value": "arn:aws:appmesh:eu-central-1:xxxxxx:mesh/pxxx-local-appmesh/virtualNode/ms_python"
},
{
"name": "ENABLE_ENVOY_XRAY_TRACING",
"value": "1"
},
{
"name": "ENVOY_LOG_LEVEL",
"value": "info"
}
],
"proxyConfiguration": {
"type": "APPMESH",
"containerName": "envoy",
"properties": [
{
"name": "ProxyIngressPort",
"value": "15000"
},
{
"name": "AppPorts",
"value": "5000"
},
{
"name": "EgressIgnoredIPs",
"value": "169.254.170.2,169.254.169.254"
},
{
"name": "IgnoredUID",
"value": "1337"
},
{
"name": "ProxyEgressPort",
"value": "15001"
}
]
},
Definición de la tarea para el Virtual Gateway:
"environment": [
{
"name": "APPMESH_RESOURCE_ARN",
"value": "arn:aws:appmesh:eu-central-1:xxxxxxx:mesh/pxxx-local-appmesh/virtualGateway/pxxx-gateway"
},
{
"name": "ENABLE_ENVOY_XRAY_TRACING",
"value": "1"
},
{
"name": "ENVOY_LOG_LEVEL",
"value": "info"
}
Luego de tener la definición de tarea del virtual Gateway y de los dos microservicios, creamos los servicios en ECS. El único servicio que tendrá exposición al ALB será el Virtual Gateway.
ms_python y ms_nodejs:
Virtual Gateway
Para realizar las pruebas, empleamos la herramienta Postman, donde configuramos nuestras solicitudes a los microservicios implementados en AWS.
2. Realizamos una solicitud al microservicio de Python, incluyendo el nombre del servicio de Node.js como parámetro de entrada.
En el transcurso de la demostración, se evidenció la efectiva comunicación entre los microservicios dentro de la malla que construye AWS App Mesh, utilizando el nombre del microservicio registrado en Cloud Map. Además, logramos exponerlos mediante el un Virtual Gateway que opera como un balanceador interno de AWS App Mesh. Esta configuración no solo facilita la interacción fluida entre servicios, sino que también demuestra la capacidad de AWS App Mesh para gestionar de manera eficiente el tráfico y la conectividad en un entorno de microservicios distribuidos.