Usando AWS para desplegar un proyecto de una startup: Parte 1 - Frontend
El desafío
Años atrás, me uní a una startup para crear un producto que estaba intentando encontrar su espacio en el mercado para su idea. Primero me uní como desarrollador Frontend y, después de acabar con la primera versión de la aplicación, quería involucrarme más en el rol Backend. Así que intercambiamos roles con el desarrollador Backend.
La aplicación consistía en tres servicios: una API Backend, un Frontend cuyo objetivo era ser desplegado en dispositivos móviles, y un panel de administración.
Decidimos con el CTO en ese tiempo que podríamos desplegar nuestros servicios en AWS, y que podríamos usar Docker Images, así que escogimos AWS ECS como nuestra plataforma de despliegue.
No escogimos Kubernetes ya que se veía como un exceso, pues no teníamos tantos usuarios al principio.
Sin embargo, tuvimos unos requerimientos específicos:
Frontend: Debería estar almacenado en caché del lado del cliente, y el usuario debería ser capaz de subir una foto.
API Backend: Debería ser pública, para ser consumida por la aplicación móvil y el Frontend.
Admin: Debería ser una herramienta de uso interno. Ya que éramos una startup pequeña en ese entonces, estaba bien usar conexiones VPN.
Para todos los casos anteriores, requeríamos un entorno de pruebas, al que llamamos entorno staging.
Para almacenar el dominio, usamos Route53, y ya que teníamos que validar el certificado, podíamos automatizar la validación a través de DNS.
Para los siguientes ejemplos, usaré thestartup.domain
(lastartup.dominio en español) como dominio de ejemplo. Los ejemplos pueden variar del diagrama y casos reales, ya que esto es un concepto general basado en mi memoria.
Por que evitamos usar sólo la consola de AWS, y por qué usamos Terraform
Primero experimentamos creando unos recursos manualmente en la consola, pero vimos que depender mucho de la memoria para empezar a hacer modificaciones era peligroso, ya que podríamos dañar algunos recursos accidentalmente.
A pesar de que teníamos acceso a CloudFormation, no recuerdo qué características nos faltaban en ese tiempo, así que decidimos ir con Terraform en su lugar. Sólo recuerdo que la razón principal que nos hizo escogerlo, era que la sintaxis de Terraform se veía mucho más limpia y fácil comparada con CloudFormation.
Frontend
Arquitectura
Para el frontend, averiguamos que podíamos usar S3 para almacenar nuestra aplicación estática. Podíamos usar CloudFront conectado a AWS Certificate Manager para desplegar una aplicación completa estática HTTPS para servir HTML. Además, ya que CloudFront almacena en caché los contenidos en sus Edges, se vio como la opción más lógica.
Dos zonas diferentes de disponibilidad
Usamos dos zonas de disponibilidad, ya que usar sólo us-east-1
es riesgoso, pues todos los nuevos cambios de AWS serán lanzados allí primero y pueden causar algunas disrupciones a la plataforma.
El único servicio que no podíamos mover fue el Administrador de Certificados (SSL). Para validar nuestro dominio, siempre debía usar us-east-1
.
Usando dos dominios
La razón por la cual usamos dos dominios para desplegar la aplicación, fuera de “thestartup.domain”, era porque la página principal era una página landing, y queríamos desplegar la aplicación real dentro de un subdominio. Como escribí antes, tuvimos que desplegar un entorno de staging que cualquiera de nuestros desarrolladores pudiera utilizar.
Almacenando los entornos de producción y staging dentro del mismo bucket
Por supuesto, podíamos crear un bucket para cada proyecto. Pero para permitirle a Terraform fácilmente replicar un entorno por desarrollador, usamos un bucket único con diferentes directorios, ya que crear un bucket por entorno se veía como sobre-ingeniería para la arquitectura.
Además, habilitamos la característica de servir el contenido del bucket como una página web estática.
Pero no queríamos exponer todo el contenido del bucket a todo el público.
Usando Cloudfront para mostrar el contenido del bucket
Para tomar ventaja del contenido en caché y evitar exponerlo, creamos dos distribuciones CloudFront dentro de la capa más asequible: PriceClass_100
. Con esto, nos aseguramos de que nuestro contenido sólo estaría en caché para el público de América (norte y sur).
Certificado
Nuestro certificado era de tipo wildcard. Esto por si necesitáramos tener más alternativas o instancias de prueba en el frontend. Un certificado de tipo wildcard te permite asignar múltiples subdominios a un certificado único.
Para este caso, usamos *.frontend.thestartup.domain
.
Un entorno por desarrollador
Por un par de semanas, intentamos tener algo como desarrolladoruno.frontend.thestartup.domain
. Pero, ya que no tuvimos tiempo de reforzar algunas políticas al momento de hacer merge en nuestras ramas de Git, era más difícil mantener el código de ese modo. La idea fue tener entornos frontend alternativos apuntando a la misma API de staging.
Incluso si hubiéramos querido recrear la misma estructura corriendo Terraform por cada Pull Request, podría tomar demasiado tiempo esperar por el despliegue, así que descartamos esta idea.
Sí es posible, por supuesto, pero si queremos una manera más sencilla de hacerlo, sería con contenedores.
Flujo para desplegar el proyecto (Pipeline)
El flujo era para construir el HTML y desplegarlo como contenido estático. Para evitar salirme del sistema de AWS, estaba usando AWS CodeCommit combinado con AWS CodeBuild para la versión uno.
Pero las GitHub Actions comenzaron a ganar popularidad en ese entonces, y fue cuando decidimos hacer el salto a GitHub Actions, ya que tenía la posibilidad de crear reportes por Pull Request.
Entonces, en el bucket teníamos dos directorios: uno llamado /production
(producción) y otro con /staging
. Antes de construir cada proyecto, tuvimos que cambiar las variables de entorno para apuntar a la API de staging también.
Flujo en Github
Este es un script similar al que utilizamos para desplegar el código.
DOMAIN_NAME
(nombre del dominio) se cambiaba entre entornos, ya que solíamos detectar cuál distribución de CloudFront queríamos apuntar para invalidar su caché una vez que hayamos desplegado el contenido en el bucket.
¿Qué mejoraría hoy en día?
¿Te ha pasado que has revisado tu código que escribiste años atrás y te sientes avergonzado? Es totalmente normal. Nosotros, como desarrolladores, una vez que obtenemos más conocimiento, veremos nuestros errores del pasado y pensaremos en maneras de cómo podrían haber sido evitados. Es por eso que creé una lista de mejoras que haría si tuviera que crear el mismo entorno actualmente.
Agregar WAF
Ya que la seguridad es tan importante hoy en día, agregar WAF sería obligatorio para mí. AWS WAF agrega una protección contra inyecciones SQL y cross-site scripting (XSS). Además, agrega una capa de protección contra bots. En el mundo actual, los bots continuarán usando tu sitio repetidamente, buscando alguna vulnerabilidad, o estarán “leyendo” (scraping) tu sitio, consumiendo recursos y memoria.
Usar un bucket diferente por proyecto
Por objetivos de simplicidad, estaba intentando poner todos los contenidos del despliegue dentro del mismo bucket. Así, una vez que tuviera que dar acceso a cada desarrollador, sería simple, ya que sólo tenía que lidiar con un solo recurso.
Mi meta será crear buckets separados por cada entorno, permitiéndome administrar cada permiso de desarrollador individualmente por recurso y restringir el acceso al bucket de producción al personal autorizado dentro de la empresa.
Si realmente necesitáramos un entorno diferente por desarrollador o por Pull Request
Si tuviéramos que tener más entornos fuera de staging o producción, preferiría desplegar esos entornos temporales dentro de un contenedor NGINX que serviría el contenido como un servidor web, ya que esperar por la creación de una distribución CloudFront puede ser costoso en temas de dinero y tiempo.
Acerca de la segunda parte
La parte 2 cubrirá el backend, donde la mayor complejidad estaba. Ya que teníamos que desplegar por contenedores, AWS ECS nos ayudó a reducir el mantenimiento de nuestros contenedores.
Voy a actualizar la referencia al post en la sección recomendados una vez que el post esté listo.