Desplegando una Arquitectura de 3 capas en AWS

Analizaremos una arquitectura web de 3 capas avanzada, pensada para producción sobre AWS.
Haremos un paso a paso.
Construiremos capa por capa hasta llegar a una solución robusta.
Arrancamos con la red. Una VPC y la extenderemos por 2 AZ, para la resiliencia.
Dentro de cada una tendremos subredes publicas para permitir el acceso desde y hacia internet y subredes privadas para la lógica de la aplicación y la base de datos (seguridad).

En el tema de la base de datos, utilizaremos RDS MySql configurado en modo multi-AZ.
En la capa de Aplicación, el backend, NodeJs que las soportarán instancias EC2 repartidas entre las 2 AZs.
Serán gestionadas por un ASG con sus respectivos controles. Lo típico. Si el uso del CPU pasa del 50% añade una instancia, si no, mantén las que tienes configuradas como deseadas o como mínimo. (Elasticidad).
Adicional usaremos un ABL interno dentro de las subredes privadas para que reparta las peticiones que le llegan desde el frontend entre las distintas instancias EC2 del backend que estén sanas.

Para el Frontend usaremos una estructura muy parecida a la capa de presentación, otro grupo de instancias EC2 pero esta vez en las subredes públicas para que sean accesibles desde internet. Estas instancias servirán la aplicación React con Nginx y estarán gestionadas por su propio Auto Scaling Group para escalar el frontend por si hace falta.

Delante de todo, mirando a internet, tendremos un ALB público que recibirá las peticiones https de los navegadores web de los usuarios, y distribuirá la carga entre las instancias EC2 de las subredes públicas que estén disponibles y sanas.

Por otra parte, crearemos un Bastión Host para la administración segura de las instancias EC2. Esta instancia estará en la subred publica y tendrá acceso a las instancias privadas a través de SSH.

Cada subred tendrá Security Groups específicos.

Otro elemento que no puede faltar es el CDN con Cloudfront para el contenido estático y adicional nos ayudará a gestionar el certificado SSL/TLS para el https usando AWS Certificate Manager o ACM, haciendo la conexión segura.

Por la parte de DNS tendremos a Route 53. Usaremos un dominio propio y le indicaremos los registros DNS proporcionados por AWS.

Por ultimo y mas importante, tendremos AWS CloudWatch para la monitorización.

Comenzamos nuestra infraestructura:
Crear la VPC con CIDR 12.0.0.0/16 con 2 AZs, 2 subredes publicas 12.0.1.0/24, 12.0.2.0/24 y 2 subredes privadas 12.0.10.0/24, 12.0.11.0/24, 12.0.12.0/24, 120.13.0/24, agregamos un NatGateway.

Con 2 AZs logramos alta disponibilidad y tolerancia a fallos y resiliencia. Ahora si buscas mas redundancia, considera agregar una tercera AZs.

Con las subredes logramos separar las capas y disponerlas entre una subred publica para la capa de presentacion, una subred privada para la capa de aplicación (backend) y una subred privada para la capa dedicada para los datos.

Habilitar la asignación automática de direcciones ip en las subredes publicas para que todas las instancias desplegadas en estas subredes reciban una ip publica de una vez y facilitarnos el acceso a ellas.

Crear Los Grupos de Seguridad.
Estableceremos una postura sólida de seguridad para nuestra aplicación con los grupos de seguridad.
Nos van a permitir el acceso a nuestros recursos, con reglas de comunicación explicitas.

Crear un SG para un bastión Host que implementaremos mas adelante. Puerto 22

Crear un SG para el balanceador de carga de la capa de presentación. Este balanceador de carga estará de cara a internet y debe aceptar trafico http desde cualquier lugar. Puerto 80 desde 0.0.0.0, debemos mencionar que no crearemos una regla que permita trafico https debido a que todas las solicitudes https serán manejadas por una distribución de Cloudfront.

Crear un SG para las instancias EC2 de la capa de presentación.
Puerto 22 desde el bastión host, que permite conectarnos de forma segura cuando sea necesario.
Adicional permitir el acceso al trafico http puerto 80 desde el load Balancer de la capa de presentación. Esto permitirá que todo el trafico recibido por el balanceador de carga que esta de cara a internet, reenvíe todo el trafico a las instancias EC2 en la capa de presentación.

Crear un SG para el balanceador de carga de la capa de aplicación.
Permitir trafico http desde las instancias de la capa de presentación. Todas las solicitudes entrantes realizadas al backend se permitirán solo desde el frontend de nuestra aplicación React asegurando una comunicación fluida entre las capas.

Crear un SG para las instancias EC2 de la capa de aplicación.
Permitir ssh para el bastión host.
Y Trafico TCP por el puerto 3200 desde el balanceador de carga de la capa de aplicación.
El balanceador de carga de la capa de aplicación será un balanceador de carga interno y reenviará todas las solicitudes entrantes de nuestra aplicación React de el frontend al puerto 3200 en el Backend. Este puerto es que será utilizado la aplicación con nodejs. Esta configuración garantiza la comunicación fluida entre el frontend y el backend.

Crear un SG para la capa de base de datos.
Debemos permitir el tráfico entrante del puerto 3306 que como ya sabemos es el que utiliza MySql y Mariadb, desde nuestro bastion host y permitir a las instancias en la capa de aplicación por el mismo puerto. Esto para comunicarnos a la base de datos de forma segura, ya que tendremos que crear un tunel ssh desde nuestro host local a la instancia RDS. Es decir, solo las fuentes autorizadas podrán acceder a la base de datos. Tu IP.

Crear un Bastion Host. Con key pair (crear uno o indicarle uno existente). En la Subnet publica 1a. Auto Asign public ip y el SG para el Bastion Host que hemos creado.

Crear la Base de datos
Antes de Crear la base de datos RDS Mysql. debemos crear el grupo de subred de la base de datos. Esto para definir que subredes podrá usar nuestra base de datos dentro de la VPC. Seleccionar las AZs 1a y 1b y las subredes correspondientes o private 3 y private 4.

Crear la base de datos RDS MySql
MySQL -> Dev/Test para una prueba de concepto o producción si ya es la definitiva. -> Multi AZ DB instance, que nos garantiza una alta disponibilidad y resiliencia.
Nuestra base de datos se replicará automaticamente en diferentes AZs lo que mejora la tolerancia a fallos. -> credenicales self managed. > Selecconar la VPC y el grupo de subred de base de datos que creamos en el paso anterior. Seleccionar el grupo de seguridad creado para la capa de datos.
Revisar los costos (varia mucho según la configuración) -> Crear.
Considera utilizar la ofrecida por aws como gratuita por 12 meses para fines didácticos o pruebas de concepto.

Conectarse a la base de datos en la instancia RDS y pasarle los datos.
Lo podemos hacer por SSH.
Abrimos la terminal local y nos dirigimos a la carpeta donde tenemos la llave privada (key pair)

ssh-add your_key.pem

ssh -N -L 33307:endpoint_de_la_instancia_rds:3306 usuario@dns_de_el_bastion_host

Una vez establecido el tunel ssh abrimos Mysql Workbench en local y creamos una nueva conexión usando el usuario master en este caso (admin) y la contraseña que permite conectarse a la instancia RDS y el puerto 33307 configurado en el tunel ssh.

Una vez dentro de la instancia RDS crearemos un usuario específicamente para nuestro backend nodejs
Ejecutamos en Workbeanch

CREATE DATABASE react_node_app;
CREATE USER 'appuser'@'%' IDENTIFIED BY 'SiguemeRD02#';
GRANT ALL PRIVILEGES ON react_node_app.* TO 'appuser'@'%';
FLUSH PRIVILEGES;

Cerramos esa conexión y creamos una nueva conexión, esta vez con el nuevo usuario (appuser).
Esta es una buena practica para evitar exponer las credenciales de el usuario master.
Una vez logueado con el nuevo usuario procedemos a ingresar los datos de la base de datos.

USE react_node_app;
-- Create Tables
CREATE TABLE `author` ( 
  `id` int NOT NULL AUTO_INCREMENT, 
  `name` varchar(255) NOT NULL, 
  `birthday` date NOT NULL, 
  `bio` text NOT NULL, 
  `createdAt` date NOT NULL, 
  `updatedAt` date NOT NULL, 
  PRIMARY KEY (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; 

CREATE TABLE `book` ( 
  `id` int NOT NULL AUTO_INCREMENT, 
  `title` varchar(255) NOT NULL, 
  `releaseDate` date NOT NULL, 
  `description` text NOT NULL, 
  `pages` int NOT NULL, 
  `createdAt` date NOT NULL, 
  `updatedAt` date NOT NULL, 
  `authorId` int DEFAULT NULL, 
  PRIMARY KEY (`id`), 
  KEY `FK_66a4f0f47943a0d99c16ecf90b2` (`authorId`), 
  CONSTRAINT `FK_66a4f0f47943a0d99c16ecf90b2` FOREIGN KEY (`authorId`) REFERENCES `author` (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; 

-- Restore Data
INSERT INTO `author` VALUES (1,'J.K. Rowling (Joanne Kathleen Rowling)','1965-07-31','J.K. Rowling is a British author best known for writing the Harry Potter fantasy series. The series has won multiple awards and sold over 500 million copies, becoming the best-selling book series in history. Rowling has also written other novels, including The Casual Vacancy and the Cormoran Strike crime series under the pen name Robert Galbraith.','2024-05-29','2024-05-29'),(3,'Jane Austen','1775-12-16','Jane Austen was an English novelist known for her wit, social commentary, and romantic stories. Her six major novels, which explore themes of love, marriage, and money, have earned her a place as one of the greatest writers in the English language.','2024-05-29','2024-05-29'),(4,'Harper Lee','1960-07-11','Harper Lee was an American novelist best known for her Pulitzer Prize-winning novel To Kill a Mockingbird. The novel explores themes of racial injustice and the importance of compassion. Lee published a sequel, Go Set a Watchman, in 2015.','2024-05-29','2024-05-29'),(5,'J.R.R. Tolkien','1954-07-29','J.R.R. Tolkien was a British philologist and writer best known for his fantasy novels The Hobbit and The Lord of the Rings. Tolkien\'s works have had a profound influence on the fantasy genre and popular culture.','2024-05-29','2024-05-29'),(6,'Mary Shelley','1818-03-03','Mary Shelley was a British novelist, playwright, and short story writer, the daughter of Mary Wollstonecraft Godwin and the wife of poet Percy Bysshe Shelley. Frankenstein, or, The Modern Prometheus (1818) is her most famous work.','2024-05-29','2024-05-29'),(7,'Douglas Adams','1979-10-12','Douglas Adams was an English science fiction writer, satirist, humorist, dramatist, screenwriter, and occasional actor. He is best known for the Hitchhiker\'s Guide to the Galaxy comedy series, which inspired a radio comedy, several books, stage shows, comic books, a 1981 TV series, a 1984 video game, a 2005 feature film, and a 2008 sequel film.','2024-05-29','2024-05-29'); 

INSERT INTO `book` VALUES (1,'Harry Potter and the Sorcerer\'s Stone','1997-07-26','On his birthday, Harry Potter discovers that he is the son of two well-known wizards, from whom he has inherited magical powers. He must attend a famous school of magic and sorcery, where he establishes a friendship with two young men who will become his companions on his adventure. During his first year at Hogwarts, he discovers that a malevolent and powerful wizard named Voldemort is in search of a philosopher\'s stone that prolongs the life of its owner.',223,'2024-05-29','2024-05-29',1),(3,'Harry Potter and the chamber of secrets','1998-07-02','Harry Potter and the sophomores investigate a malevolent threat to their Hogwarts classmates, a menacing beast that hides within the castle.',251,'2024-05-29','2024-05-29',1),(4,'Pride and Prejudice','1813-01-28','An English novel of manners by Jane Austen, first published in 1813. The story centres on the relationships among the Bennet sisters, in particular Elizabeth Bennet the middle daughter, and the wealthy Mr. Darcy. Austen satirizes the social classes of the English gentry through a witty and ironic narrative voice.',224,'2024-05-29','2024-05-29',3),(5,'Harry Potter and the Prisoner of Azkaban','1999-07-08','Harry\'s third year of studies at Hogwarts is threatened by Sirius Black\'s escape from Azkaban prison. Apparently, it is a dangerous wizard who was an accomplice of Lord Voldemort and who will try to take revenge on Harry Potter.',317,'2024-05-29','2024-05-29',1),(6,'Harry Potter and the Goblet of Fire','2000-07-08','Hogwarts prepares for the Triwizard Tournament, in which three schools of wizardry will compete. To everyone\'s surprise, Harry Potter is chosen to participate in the competition, in which he must fight dragons, enter the water and face his greatest fears.',636,'2024-05-29','2024-05-29',1),(7,'The Hitchhiker\'s Guide to the Galaxy','1979-10-12','A comic science fiction comedy series created by Douglas Adams. The story follows the comedic misadventures of Arthur Dent, a hapless Englishman, following the destruction of the Earth by the Vogons, a race of unpleasant bureaucratic aliens. Arthur escapes with his friend Ford Prefect, who reveals himself to be an undercover researcher for the titular Hitchhiker\'s Guide to the Galaxy, a galactic encyclopedia containing information about anything and everything.',184,'2024-05-29','2024-05-29',7),(8,'Frankenstein; or, The Modern Prometheus','1818-03-03','A Gothic novel by Mary Shelley that tells the story of Victor Frankenstein, a young scientist who creates a grotesque creature in an unorthodox scientific experiment. Frankenstein is horrified by his creation and abandons it, but the creature seeks revenge. The novel explores themes of scientific responsibility, creation versus destruction, and the nature of good and evil.',211,'2024-05-29','2024-05-29',6),(9,'The Lord of the Rings: The Fellowship of the Ring','1954-07-29','The first book in J.R.R. Tolkien\'s epic fantasy trilogy, The Lord of the Rings. The Fellowship of the Ring follows hobbit Frodo Baggins as he inherits the One Ring, an evil artifact of power created by the Dark Lord Sauron. Frodo embarks on a quest to destroy the Ring in the fires of Mount Doom, accompanied by a fellowship of eight companions.',482,'2024-05-29','2024-05-29',5); 

Revisar las tablas.
Tendríamos lista la capa de datos.

Configurar Route 53 que es el servicio de DNS de AWS.
Debemos disponer de nuestro nombre de dominio.
Creamos un Hosted Zone que sirve como contendor para registros de DNS que especifican como se debe enrutar el trafico de internet a un nombre de dominio especifico.
Domain name richarddevops.site -> Public hosted zone- > Crear.
Una vez creado el hosted zone obtendremos los servidores de nombres asignados desde Route 53.
Estos servidores de nombres deberan ser actualizados en tu registrador de dominio para que dirijan las consultas DNS a nuestro dominio en Route 53.
Debemos tomar en cuenta que este proceso puede tardar algunos minutos u horas en propagarse.

Para garantizar una comunicación segura y el cifrado de datos, obtendremos un certificado SSL/TLS público mediante AWS Certificate Manager (ACM), solicitando y validando certificados tanto para el dominio principal (richarddevops.site) como para sus subdominios (www.richarddevops.site). El proceso implica: 1) Solicitar el certificado en ACM ingresando los dominios, 2) Validar la propiedad del dominio creando registros CNAME específicos en la Hosted Zone de Route 53 que ACM proporciona, y 3) Esperar la propagación DNS para que ACM emita automáticamente el certificado.

Una vez completada la validación, ACM emitirá el certificado SSL/TLS que permitirá el tráfico HTTPS seguro para nuestra aplicación, garantizando cifrado de datos y autenticación del servidor. Los certificados de ACM son gratuitos, se renuevan automáticamente y se integran perfectamente con otros servicios AWS como CloudFront y ALB para una implementación simplificada.

Una configuración importante. Debemos integrar los logs de las instancias EC2 de la capa de aplicación con Amazon CloudWatch. Esto se debe a que, cada instancia EC2 almacenará sus registros de forma individual, llevando su propio registro local. Para centralizar y unificar toda la información de logs en un solo lugar, utilizaremos CloudWatch y lo configuraremos mas adelante en la capa de aplicación.
De esta manera, podremos monitorear, analizar y configurar alertas sobre los logs de todas las instancias desde un único panel de control, mejorando la visibilidad y la capacidad de respuesta ante incidencias.
Para esto debemos crear un rol IAM especifico para las instancias EC2 de la capa de aplicación para permitirles interactuar con CloudWatch.
Vamos al panel de IAM y a la sección de roles.
Crear rol -> Seleccionamos EC2 en Use case. -> Next -> buscamos los permisos, CloudWatchLogsFullAccess, lo seleccionamos y buscamos el permiso CloudWatchAgentServerPolicy y lo seleccionaos.
El primero hace que las instancias EC2 puedan escribir registros en Cloudwatch y el segundo otorga la capacidad de usar el agente de Cloudwatch que recopila y envía los registros y las métricas de las instancias.
Next- > le damos un nombre al nuevo rol: react-app-ec2-cloudwatch-role -> crear rol.

Este rol se lo adjuntaremos a las instancias de la capa de aplicación mas adelante.

Vamos a CloudWatch y creamos un Logs Group.
le damos un nombre: backend-node-app-logs -> crear Log Group.

Antes de pasar a configurar la capa de aplicación debemos tener un fragmento de código que nos ayudará a verificar la salud de las instancias EC2.
Asegurémonos de tener este parámetro en nuestro código backend/app.js

//Health Checking
app.get('/health', (req, res) => {
  res.json({
    status: 'OK',
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
  });
});

Este fragmento de código se usa generalmente para definir un endpoint de verificación de salud /health y monitorear si el servidor esta funcionando correctamente. (Lo veremos mas adelante).
Esto es importante ya que el balanceador de carga usará ese Endpoint para verificar cada cierto tiempo el estado de la instancia en el Target Group asegurando que solo las instancias saludables reciban trafico.

#

Pasemos a la capa de Aplicación:
Configuración de la capa de aplicación:
Creamos una Launch template para las instancias EC2 del backend
Luego creamos un Target Group para el ALB interno usará para enrutar el trafico a las instancias de el backend.
Luego pasamos a configurar el ALB interno.
Y finalmente crearemos un ASG para ajustar automáticamente el numero de instancias según la demanda y asegurar que nuestro backend este listo para soportar la carga.

Vamos al panel de Launch template
-> nombre -> versión 01 -> Habilitar Auto Scaling Guidance que ayudará a optimizar el escalado de las instancias. -> Seleccionar una AMI -> Amazon Linux -> Seleccionar Kay Pair -> seleccionar el Security Group que creamos para las instancias ec2 de la capa de aplicación.
No seleccionaremos una subred porque eso lo haremos cuando configuremos el auto scaling groups.
User Data:

#!/bin/bash 
# Update package list and install required packages 
sudo yum update -y 
sudo yum install -y git 

# Install Node.js (use NodeSource for the latest version) 
curl -fsSL https://rpm.nodesource.com/setup_18.x | sudo bash - 
sudo yum install -y nodejs 

# Install PM2 globally 
sudo npm install -g pm2 

# Define variables 
REPO_URL="https://github.com/richardaguirre1/react-node-mysql-app.git" 
BRANCH_NAME="main" 
REPO_DIR="/home/ec2-user/react-node-mysql-app/backend" 
ENV_FILE="$REPO_DIR/.env" 

# Clone the repository 
cd /home/ec2-user 
sudo -u ec2-user git clone $REPO_URL 
cd react-node-mysql-app  

# Checkout to the specific branch 
sudo -u ec2-user git checkout $BRANCH_NAME 
cd backend 

# Define the log directory and ensure it exists 
LOG_DIR="/home/ec2-user/react-node-mysql-app/backend/logs" 
mkdir -p $LOG_DIR 
sudo chown -R ec2-user:ec2-user $LOG_DIR

# Append environment variables to the .env file
echo "LOG_DIR=$LOG_DIR" >> "$ENV_FILE"
echo "DB_HOST=\"<rds-instance.end.point.region.rds.amazonaws.com>\"" >> "$ENV_FILE"
echo "DB_PORT=\"3306\"" >> "$ENV_FILE"
echo "DB_USER=\"<db-user>\"" >> "$ENV_FILE"
echo "DB_PASSWORD=\"<db-user-password>\"" >> "$ENV_FILE"  # Replace with actual password
echo "DB_NAME=\"<db-name>\"" >> "$ENV_FILE"

# Install Node.js dependencies as ec2-user
sudo -u ec2-user npm install

# Start the application using PM2 as ec2-user
sudo -u ec2-user npm run serve

# Ensure PM2 restarts on reboot as ec2-user
sudo -u ec2-user pm2 startup systemd 
sudo -u ec2-user pm2 save 

# Install CloudWatch agent
sudo yum install -y amazon-cloudwatch-agent

# Create CloudWatch agent configuration
sudo tee /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json > /dev/null <<EOL
{
  "logs": {
    "logs_collected": {
      "files": {
        "collect_list": [
          {
            "file_path": "/home/ec2-user/react-node-mysql-app/backend/logs/*.log",
            "log_group_name": "backend-node-app-logs",
            "log_stream_name": "{instance_id}",
            "timestamp_format": "%Y-%m-%d %H:%M:%S"
          }
        ]
      }
    }
  }
}
EOL

# Start CloudWatch agent
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json -s

Este script lo que hará será un update del Sistema operativo
Instalará git para administrar nuestro código fuente
Luego instalará nodejs y pm2. PM2 es un gestor de procesos de aplicación de nodejs y ayuda a mantener las aplicaciones en ejecución entre otras cosas.
Luego definimos algunas variables indicando la url del repositorio donde esta nuestro código fuente, la rama, el directorio dentro de la instancia EC2 donde se clonará el repositorio y la ruta de el archivo .env
Seguidamente se clonara el repositorio y la rama
Pasamos al directorio backend
Definimos una variable para el directorio de registro y lo creamos dentro de el directorio.
Cambiamos la propiedad de ese directorio para el usuario ec2-user para evitar problemas de permisos al generar los logs.
Seguidamente se crean las variables de entorno necesarias.
Aquí debes colocar los valores de tus propias variables, como el endpoint de base de datos RDS, el usuario, password y el nombre de la base de datos
Seguidamente se instalan las dependencias necesarias y se inicia pm2 para servir la aplicacion.
Se configura el agente de CloudWatch encargado de recopilar la información de nuestras instancias EC2.
Se instala el agente de CloudWatch.
Luego el script configura al agente de Cloudwatch definiendo las rutas de archivos para los registros que queremos monitorear especificando el nombre de el log group. Este lo creamos hace unos minutos. ese nombre debe coincidir así como la dirección de los archivos log dentro de la instancia.
Aparte le decimos que identifique a cada instancia por su id de instancia y le damos un formato de marca de tiempo para que nos diga con precisión el tiempo en que ocurrió el registro.
Y finalmente el script inicia el agente de Cloudwatch.
Revisado el script, lo copiamos y lo pegamos en el user-data y creamos el launch template.

Crear el Target Group de la capa de aplicacion.
Seleccionamos instances como target type
Le damos un nombre -> protocolo y puerto https : 3200 que es el puerto utilizado por la aplicacion nodejs -> seleccionamos la vpc -> Health Check path /health que será la ruta para la comprobación de estado utilizado por el balanceador de carga para monitorear el estado de las instancias
-> Next -> Crear.

Crear el Load Balancer interno.
Seleccionar ALB -> Nombre -> Internal -> VPC -> AZs con las subredes correspondientes private 1 y private 2 -> security group correspondiente a load balancer de la capa de aplicaciones -> seleccionar target group de la capa de aplicaciones protocolo http y puerto 80. Esta asociacion es fundamental ya el ALB dirige reenvia el trafico http en el puerto 80 a las instancias de el target group. -> crear load balancer.

Crear el Auto Scaling Group de la capa de aplicacion:
Nombre -> seleccionar el launch template de la capa de aplicacion -> version 1 -> next -> seleccionar VPC y las subredes donde queremos que se implementen las instancias ec2 de la capa de aplicacion. Private1 y private2.
Attach to an existing load balancer y seleccionamos el target group de destino que configuramos en el paso anterior.
Habilitar los load balancer health check para asegurar que el trafico se redirirá solo a las instancias en buen estado.
Activar la coleccion de metricas de monitoreo con cloudwtach
Next
Configurar la capacidad deseada, la minima y la maxima 1,1, 2 o 2, 2 y 3
Target tracking scaling policy para el automatic autoscaling y establescamos el uso de CPU al 50%. Esto hace que si el uso del CPU sobrepasa el 50%, Cloudwatch activará una alarma que solicita al autoscaling group que inicie una instancia adicional con tope a numero maximo configurado.
Procedemos a crear el ASG.

Navegamos al panel de Instancias EC2. veremos como se comienzan a crear nuevas instancias. y esperamos unos minutos.

Una vez que estén las instancias estables, pasamos a conectarnos a una de ellas a través de el bastión host y ejecutamos el comando pm2 logs. veremos el estado de la aplicación nodejs y la conexión con la base de datos. De lo contrario debemos revisar el script.

#

Pasemos a la capa de presentación.
Haremos algo muy parecido a lo hecho en la capa de aplicación.
Crear una Launch Template especifica para la capa de presentación. Esta Launch Template será usada por un grupo de auto escalado que vamos a crear en un momento y se encargará de crear instancias EC2 automáticamente a medida que se van requiriendo.
Despues de haber configurado la plantilla de lanzamiento, creamos un Target Group y un balanceador de carga de aplicaciones o ALB para administrar el trafico dirigido hacia la capa de presentacion. Luego estableceremos el ASG para la capa de presentacion asegurandonos que pueda escalar hacia adentro y hacia afuera de forma eficiente segun la demanda. Eso lo podremos hacer con pruebas de estress

Ir a crear la launch tamplate, -> nombre -> version 01 -> Habilitar auto scaling guidance que ayudará a optimizar el escalado de las instancias. -> Seleccionar una AMI -> Amazon Linux -> Seleccionar Kay Pair -> seleccionar el Security group que creamos para las instancias de la capa de presentacion.
No seleccionaremos una subred porque eso lo haremos cuando configuremos el auto scaling groups.
User Data:

#!/bin/bash
# Update package list and install required packages
sudo yum update -y
sudo yum install -y git

# Install Node.js (use NodeSource for the latest version)
curl -fsSL https://rpm.nodesource.com/setup_18.x | sudo bash -
sudo yum install -y nodejs

# Install NGINX
sudo yum install -y nginx

# Start and enable NGINX
sudo systemctl start nginx
sudo systemctl enable nginx

# Define variables
REPO_URL="https://github.com/richardaguirre1/react-node-mysql-app.git"
BRANCH_NAME="main"
REPO_DIR="/home/ec2-user/react-node-mysql-app/frontend"
ENV_FILE="$REPO_DIR/.env"
APP_TIER_ALB_URL="http://<internal-application-tier-alb-end-point.region.elb.amazonaws.com>"  # Replace with your actual alb endpoint
API_URL="/api"

# Clone the repository as ec2-user
cd /home/ec2-user
sudo -u ec2-user git clone $REPO_URL
cd react-node-mysql-app

# Checkout to the specific branch
sudo -u ec2-user git checkout $BRANCH_NAME
cd frontend

# Ensure ec2-user owns the directory
sudo chown -R ec2-user:ec2-user /home/ec2-user/react-node-mysql-app

# Create .env file with the API_URL
echo "VITE_API_URL=\"$API_URL\"" >> "$ENV_FILE"

# Install Node.js dependencies as ec2-user
sudo -u ec2-user npm install

# Build the frontend application as ec2-user
sudo -u ec2-user npm run build

# Copy the build files to the NGINX directory
sudo cp -r dist /usr/share/nginx/html/

# Update NGINX configuration
NGINX_CONF="/etc/nginx/nginx.conf"
SERVER_NAME="<domain subdomain>"  # Replace with your actual domain name

# Backup existing NGINX configuration
sudo cp $NGINX_CONF ${NGINX_CONF}.bak

# Write new NGINX configuration
sudo tee $NGINX_CONF > /dev/null <<EOL
user nginx;
worker_processes auto;

error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    log_format main '\$remote_addr - \$remote_user [\$time_local] "\$request" '
                    '\$status \$body_bytes_sent "\$http_referer" '
                    '"\$http_user_agent" "\$http_x_forwarded_for"';

    access_log /var/log/nginx/access.log main;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    include /etc/nginx/conf.d/*.conf;
}
EOL

# Create a separate NGINX configuration file
sudo tee /etc/nginx/conf.d/presentation-tier.conf > /dev/null <<EOL
server {
    listen 80;
    server_name $SERVER_NAME;
    root /usr/share/nginx/html/dist;
    index index.html index.htm;

    #health check
    location /health {
        default_type text/html;
        return 200 "<!DOCTYPE html><p>Health check endpoint</p>\n";
    }

    location / {
        try_files \$uri /index.html;
    }

    location /api/ {
        proxy_pass $APP_TIER_ALB_URL;
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto \$scheme;
    }
}
EOL


# Restart NGINX to apply the new configuration
sudo systemctl restart nginx

Copiamos el script y lo pegamos en el User Data.
Este script lo que hace es, primero instala git, nodejs, y nginx.
Inicia nginx y lo habilita para que se inicie en cada boteo o arranque.
Seguidamente define las variables url del repositorio github donde tenemos el codigo fuente, el branch, el directorio donde se manejaran los archivos del repositorio, el directorio del archivo .env, la url del ALB interno que creamos, la url de la API.
La URL del balanceador de carga debemos proporcionarlo en este punto.

Seguidamente el script e dirige al directorio de el usuario ec2-user y clona el codigo fuente, luego se dirige a la carpeta donde estan los archivos, Extrae la rama especifica, en este caso es main y se dirige al directorio frontend.
Al usuario ec2-user se le asigna la propiedad del directorio react-node-mysql-app
Se crea un archivo ENV con la URL del la API que es necesaria para que el frontend se comunique con el backend.
Seguidamente se instala todas las dependencias necesarias y se crea la aplicación react para producción.
Los archivos de compilación se copian al directorio predeterminado de nginx para servir el forntend.
A continuación, se define la ruta del archivo de configuración de nginx en una variable junto con otra variable donde debemos indicar el nombre del servidor, le indicamos el dominio y el subdominio separados por un espacio en blanco.
Acto seguido, se realiza una copia de seguridad del archivo de configuración de nginx existente y se crea un nuevo archivo de configuración con todos estos parámetros que vemos allí.
Seguidamente se crea un archivo de configuración de nginx, especifico para la capa de presentación, donde actualizamos el nombre del servidor incluyendo la url delcheck health y se configura un proxy inverso para reenviar todas las solicitudes de API al balanceador de carga de la capa de aplicación.
Finalmente se reinicia nginx para aplicar estos cambios.

Crear el Target Group de la capa de presentación:
Le damos un nombre -> protocolo y puerto https : 80 -> seleccionamos la VPC -> Health Check path /health que será la ruta para la comprobación de estado utilizado por el balanceador de carga para monitorear el estado de las instancias -> Next -> Crear.

Crear el balanceador de carga de la capa de presentación:
Application Load Balancer -> nombre -> internet facing -> VPC -> AZ con su respectiva subred publica. Asignar el SG creado para el LB de la capa de presentación.
Listeners and routing: Protocol http: port 80 : Default Action forward to presentation-tier-tg (target group creado anteriormente). Resto en default -> crear.

Crear el ASG de la capa de presentación:
Le damos un nombre -> seleccionamos el launch template -> VPC > Subnets publicas. Next ->
En instance Type Requirement seleccionamos la opción de agregar manuelamente -> buscamos el tipo de instancia t2.micro y eliminamos la sugerencia. -> Seleccionamos la VPC con las AZs -> next
le adjuntamos el balanceador de carga. (attach to existing load balancer -> chose from your load balancer group) y seleccionamos el target group creado. Esto permitirá que nuestro Auto Scaling Group enrute automáticamente el tráfico a las instancias que administra.
Habilitar Turn on elastic load balancing health checks -> habilitar monitoring Enabling group metrics collection within cloudwatch. Esta integracion nos permite monitorear el rendimiento de nuestro auto scaling group en tiempo real.
Ahora definimos la capacidad deseada, la capacidad minima y la capacidad maxima en 1, 1 y 2 o puede ser 2,2 y 3. La capacidad deseada representa el numero de instancias que queremos que se ejecuten siempre en condiciones normales. La capacidad minima es la cantidad de instancias que se deben ejecutar en todo momento, garantizando que nuestra aplicacion siempre este disponible. La capacidad máxima es el tope de instancias que se deben ejecutar al mismo tiempo -> Auto scaling policy: target tracking scaling policy y configuramos que se active cuando el uso del CPU pase del 50%. Esto activará una alarma de cloudwatch que le indica al autto scaling group que cree una nueva instancia -> next -> next -> crear.

Ahora podremos ver como las instancias EC2 comienzan a crearse según lo configurado en la capacidad deseada.
Si todo esta bien, podremos ir a nuestro load balancer y copiar el endpoint y pegarlo en un navegador web y ver el resultado.
Nos mostrará los detalles de la instancia activa. Estos datos los incluimos en el user data en la configuracion de el launch template para aprovisionar instancias EC2.

Luego de algunos minutos podemos hacer una prueba de stress para probar el grupo de auto escalado.
Eso lo hacemos a traves de el bastion host.
En nuestro terminal
ssh-add clave.pem
ssh -A usuario@bastionhost
Desde alli nos conectamos a una de las instancias EC2 que estan activas en la capa de presentacion.
ssh user@instanciaactiva
instalamos el paquete stress para aumentar el uso de CPU de la instancia

sudo yum install -y stress
#Iniciamos el proceso 
stress --cpu 4 --timeout 180s

Con el comando top podemos ver los procesos del sistema

Vamos al panel de EC2 en AWS y nos cercioramos que se este creando una nueva instancia Ec2
También podemos ir a CloudWatch y ver la alarma que se acaba de crear. Y que se ha activado indicando un uso de CPU superior al 50% e iniciará un escalado horizontal con el ASG lanzando una nueva instancia EC2.

Por otro lado, si ocurriera que se finalizan algunas instancias EC2, el ASG se encarga de iniciar las instancias necesarias al numero deseado.

Ahora accedemos a la URL del DNS de el balanceador de carga de la capa de presentación, y tenemos la aplicación funcionando como se esperaba.
Podemos probar el funcionamiento agregando un registro.

Para verificar las logs que estará capturando CloudWatch hacemos un registro en la aplicación.
Luego vamos a CloudWatch y vemos el Log Group que creamos anteriormente, deberíamos ver algunos logs. Abrimos uno y podemos ver el registro que hicimos hace unos segundos sobre la solicitud que mandamos desde el frontend.
Hacemos varias operaciones mas y vamos monitoreando.

Por ultimo vamos a crear una distribución de CloudFront para optimizar la entrega de contenido estático de nuestra aplicación.
Vamos a CloudFront -> distribution -> crear -> Origin domain y seleccionamos el ALB de la capa de presentación que creamos y para garantizar una conexión segura, configuramos CloudFront para redirigir el trafico automáticamente el trafico http a https.
Para mantener una aplicación simple le decimos que no necesitamos un Web Application Firewall por el momento y limitaremos el numero de ubicaciones de borde a solo North América y Europa.
Seguidamente agregamos nuestro dominio y subdominio como nombres de dominio alternativos asegurándonos que apunten correctamente a la distribución de CloudWatch.
También usaremos el certificado SSL que creamos anteriormente para asegurar la conexión.
Con esto seria suficiente de momento y creamos la distribución de CloudFront.
Esto tomará un tiempo.

Crear los DNS récords en Route 53.
Para asegurar que el trafico se enrute correctamente a nuestra distribución de CloudFront, configuraremos registros DNS dentro de la zona alojada de Route 53 que establecimos en pasos anteriores.
Vamos a Route 53 -> Hosted Zone -> seleccionamos el dominio -> crear record -> marcamos alias -> Alias to Cloud Distribution y seleccionamos el que ya habíamos creado -> crear Record.
Agregamos otro subdominio -> en Record Name : www -> marcamos Alias y seleccionamos: Alias to CloudFront Distribution -> seleccionamos de la lista la misma distribución

Probemos la aplicación:
Con todo configurado y si la propagación de tu DNS se ha completado, veremos desde el nombre de tu dominio la aplicación funcionando correctamente.

Hemos configurado con éxito una arquitectura de 3 capas, completamente funcional y con alta disponibilidad en AWS

Validación Final:

    ◦ Acceder a la aplicación usando el dominio raíz y el subdominio para verificar el funcionamiento.

    ◦ Realizar operaciones CRUD para confirmar que todo está funcionando correctamente y que los registros se generan en CloudWatch.

Agregar un comentario

Tu dirección de correo electrónico no será publicada. Los campos requeridos están marcados *