Saltar a contenido

ECS

Esta nota explica cómo desplegar una API de NestJS en AWS usando ECS con Fargate. La API correrá en contenedores, usará una base de datos Postgres en RDS, será expuesta con un Application Load Balancer y la imagen Docker estará guardada en GitHub Container Registry. Repositorio, Package público

El package de ejemplo es público, así que ECS puede descargar la imagen sin credenciales privadas. Los pasos de subir imagen a GHCR y guardar credenciales en Secrets Manager solo son necesarios si vas a usar tu propia imagen o si el package está privado.

El flujo general será este:

  • Crear la base de datos en RDS.
  • Crear los Security Groups necesarios.
  • Crear un Target Group para registrar las tareas de ECS.
  • Crear un Application Load Balancer para exponer la API.
  • Usar la imagen pública de GitHub Container Registry o subir tu propia imagen.
  • Guardar las credenciales del registry en Secrets Manager solo si la imagen es privada.
  • Crear una Task Definition en ECS.
  • Crear un cluster y un servicio para mantener la API corriendo.

1. Crear la base de datos en RDS

Primero tenemos que crear la base de datos en:

RDS -> Crear Base de Datos

Mi API usa Postgres, así que elegiré ese motor.

Más abajo podemos ver las configuraciones de seguridad y conexión:

  • Elegimos la versión del motor Postgres.
  • Configuramos el usuario y contraseña master.
  • Estas credenciales las usaremos luego como variables de entorno en ECS.

Almacenamiento

Aquí elegimos los recursos de la base de datos, como almacenamiento y tipo de instancia.

Red

En la parte de red usaré estos ajustes:

Security Group

Un Security Group funciona como un firewall de AWS. Define qué tráfico puede entrar o salir de un recurso. Para este despliegue conviene separar reglas: el ALB recibe tráfico público por 80 o 443, ECS recibe tráfico solo desde el ALB por 3000, y RDS recibe tráfico solo desde ECS por 5432.

  • VPC por defecto de AWS.
  • Acceso público deshabilitado.
  • Security Group permitiendo entrada al puerto 5432.
  • La DB solo debería recibir tráfico desde recursos dentro de la VPC, por ejemplo las tasks de ECS.

2. Crear Target Group, ALB y Security Groups

Target Group

Target Group

El Target Group es el grupo de destinos al que el ALB enviará tráfico. En este caso, los destinos serán las IPs de las tasks (contenedores) de ECS. Esto es importante porque las tasks pueden cambiar de IP cuando se reinician o cuando hacemos un nuevo deploy.

Lo crearemos en:

EC2 -> Grupos de Destino

Configuración principal:

  • Tipo: Direcciones IP.
  • Protocolo y puerto: el puerto donde corre el contenedor. En mi caso la API corre en 3000.

Cuando AWS pida registrar destinos, lo dejamos en blanco. Fargate se encargará de registrar las tasks automáticamente cuando creemos el servicio.

Application Load Balancer

Application Load Balancer

El ALB es la entrada pública hacia la API. Los usuarios hacen peticiones al DNS del Load Balancer, no directamente a las tasks (contenedores). Luego el ALB reparte el tráfico hacia las tasks disponibles dentro del Target Group.

El balanceador lo crearemos en:

EC2 -> Balanceadores de Carga

Será de tipo Application Load Balancer.

Le colocamos el nombre que queramos y lo exponemos a internet.

En red, el ALB debe estar en la misma VPC que ECS y RDS.

En seguridad, creé un Security Group permitiendo tráfico de entrada por los puertos 80 y 443, que son los puertos donde escuchará el ALB.

Así se ve el Security Group:

Listeners

Los listeners son los puertos donde el ALB escucha peticiones. En este caso el tráfico que llegue por 80 o 443 será redirigido al Target Group que creamos antes.

3. Subir la imagen a GitHub Container Registry

Antes de crear la Task Definition necesitamos tener una imagen Docker subida a un registry. En este caso usaré GitHub Container Registry, también conocido como ghcr.io.

Si usas el package público de ejemplo, puedes saltarte este paso y usar directamente la URI de la imagen en ECS:

ghcr.io/wonderiing/aero-api:latest

Solo necesitas construir y subir tu propia imagen si quieres desplegar una versión propia del proyecto.

Para eso la app necesita un Dockerfile. Este archivo define cómo construir la imagen: versión de Node, instalación de dependencias, build del proyecto y comando para arrancar el contenedor.

Ejemplo general del flujo:

docker build -t ghcr.io/usuario/aero-api:latest .

Luego iniciamos sesión en GitHub Container Registry:

docker login ghcr.io

Y subimos la imagen:

docker push ghcr.io/usuario/aero-api:latest

La URI ghcr.io/usuario/aero-api:latest será la que colocaremos después en ECS.

4. Guardar credenciales en Secrets Manager

Secrets Manager

Secrets Manager sirve para guardar información sensible como tokens, passwords o credenciales. En este ejemplo solo sería necesario para GHCR si la imagen es privada. Si usamos el package público, ECS no necesita un secreto para descargar la imagen.

Este paso es opcional. Si usas el package público de ejemplo, no necesitas crear un secreto para GHCR porque ECS puede descargar la imagen sin autenticarse.

Si tu imagen está privada, ECS necesita credenciales para descargarla desde GitHub Container Registry. Para eso usaremos Secrets Manager.

En mi caso creé un Classic Token en GitHub con permisos para leer paquetes:

Settings -> Developer Settings -> Personal Access Tokens -> Tokens Classic

Cuando tengamos el token, creamos un nuevo secreto en AWS Secrets Manager. Este secreto debe guardar las credenciales que ECS usará para autenticarse contra el registry.

Después de crear el secreto, al rol de ejecución de ECS hay que asignarle el permiso AWSSecretsManagerClientReadOnlyAccess.

Esto permite que ECS lea el secreto y pueda autenticarse para pullear la imagen privada.

5. Crear la Task Definition

Task Definition

La Task Definition es la plantilla del contenedor. Ahí se define qué imagen Docker se usará, qué puerto expone, qué variables de entorno necesita, cuánta CPU y memoria tendrá, y si enviará logs a CloudWatch. No ejecuta nada por sí sola; solo describe cómo debe ejecutarse.

Vamos a:

ECS -> Definición de Tareas

La primera parte de la configuración consiste en seleccionar los recursos y el tipo de arquitectura que vamos a usar. En mi caso usare Fargate y los recursos minimos:

Más abajo aparece el rol de ejecución de tarea. Este rol debe tener permisos para leer el secreto de Secrets Manager.

Contenedor

Aquí configuramos el contenedor que correrá dentro de la task:

  • URI de la imagen en GitHub Conteiner Registry. Imageen de Ejemplo
  • ARN del secreto con las credenciales del registry. (solo si la imagen es privada).
  • Puerto del contenedor. Mi API corre en 3000.

Más abajo podemos agregar las variables de entorno que necesita la API para conectarse a RDS.

Las credenciales las podemos sacar desde:

RDS -> dev (id de rds) -> Conectividad y Seguridad

Variables importantes:

  • Host: usar el punto de enlace de RDS.
  • POSTGRES_PASSWORD: contraseña master que colocaste al crear la DB.
  • También deberías colocar el usuario, nombre de la base de datos, puerto y cualquier otra variable que use tu API.
  • En Las variables de JWT puedes colocar lo que quieras.

También podemos activar logs para revisar errores o comportamiento del contenedor desde CloudWatch.

Al crear la tarea podemos pasar a crear el servicio.

6. Crear el cluster de ECS

ECS

Amazon Elastic Container Service es el servicio de AWS que nos permite ejecutar contenedores. En vez de levantar manualmente una máquina, instalar Docker y correr el contenedor nosotros, ECS se encarga de iniciar, detener y monitorear esos contenedores dentro de AWS.

Fargate

Fargate es una forma de usar ECS sin administrar servidores EC2. Nosotros solo indicamos cuánta CPU y memoria necesita el contenedor, y AWS se encarga de crear la infraestructura necesaria para ejecutarlo.

El cluster es el lugar lógico donde ECS ejecutará nuestros servicios y tasks. Como usaremos Fargate, no necesitamos administrar instancias EC2 manualmente.

Creamos el cluster en:

ECS -> Crear Cluster

No hay mucho que setear mas que colocarle un nombre y el tipo de infra que usaremos.

7. Crear el servicio de ECS

Task

Una Task es una ejecución real de una Task Definition. Si la Task Definition es como el molde, la Task es el contenedor corriendo de verdad. Por ejemplo, si el servicio tiene 2 réplicas, ECS levantará 2 tasks usando la misma definición.

El Servicio mantiene corriendo las tasks. Por ejemplo, si configuramos 2 réplicas, ECS intentará mantener siempre dos tasks activas. Si una se cae, ECS levanta otra usando la misma Task Definition. Para crear un servicio solo tenemos que ir a a la parte de

Definicion de Tareas -> Seleccionas la Tarea que Creamos -> Implementara y crear un nuevo servicio

Elegimos el cluster que creamos y el proveedor de capacidad. En mi caso usaré FARGATE.

Réplicas

Aquí indicamos cuántas tasks queremos corriendo. En mi caso usaré 2.

Balanceador de carga

En este apartado conectamos el servicio con el ALB y el Target Group que creamos antes.

Seleccionamos los recursos que ya habíamos creado:

8. Revisar el despliegue

En el overview podemos ver si el servicio se creó correctamente.

También podemos revisar que las tasks estén corriendo.

Finalmente probamos la API usando el DNS del Load Balancer.