Bucket privado con OAC
El bucket no se expone a internet. CloudFront firma cada petición al origen con Origin Access Control. Un bucket público sería más fácil y es la razón por la que existen tantas filtraciones de datos en S3.
Una aplicación de una sola página, servida por CDN sobre HTTPS con dominio propio, desplegada automáticamente desde GitHub, y con releases versionados por tag que se pueden revertir en un minuto.
Sin ninguna instancia que administrar.
Hoy despliegas aplicaciones TypeScript en instancias. Para una API con estado eso es una decisión razonable. Para una SPA no lo es, y vale la pena ver por qué antes de escribir código.
Una SPA compilada es un directorio de archivos estáticos: un index.html, uno o dos
bundles de JavaScript, CSS y algunos assets. No ejecuta nada en el servidor. Cuando
pones eso detrás de nginx en una EC2, lo que te queda es:
| EC2 con nginx | S3 con CloudFront | |
|---|---|---|
| Parches del sistema operativo | tuyos | no existen |
| Certificado TLS | tú lo renuevas | ACM lo renueva |
| Alta disponibilidad | otra instancia y un balanceador | incluida |
| Latencia fuera de la región | la del origen | punto de presencia cercano |
| Escalar a un pico de tráfico | escalar el grupo | nada que hacer |
| Costo con tráfico bajo | la instancia corre 24/7 | centavos |
┌──────────────────────────────┐ navegador ──────▶│ CloudFront (CDN + TLS) │ │ · certificado de ACM │ │ · 403/404 → /index.html │ │ · caché por tipo de archivo │ └──────────────┬───────────────┘ │ OAC (firma SigV4) ▼ ┌──────────────────────────────┐ │ S3 (bucket privado) │ │ · sin acceso público │ │ · assets con hash │ └──────────────────────────────┘Tres piezas que importan y que vas a configurar tú:
Bucket privado con OAC
El bucket no se expone a internet. CloudFront firma cada petición al origen con Origin Access Control. Un bucket público sería más fácil y es la razón por la que existen tantas filtraciones de datos en S3.
El fallback de rutas
Tu SPA maneja /tareas/abc en el cliente, pero ese objeto no existe en S3.
Sin configurar la respuesta de error, recargar la página da un 403. Lo vas a
provocar y luego a arreglar.
Caché por tipo de archivo
Los assets con hash en el nombre se cachean para siempre. El index.html
nunca. Si inviertes esto, tus despliegues tardan horas en verse y nadie
entiende por qué.
Releases por tag
Un push a main despliega a desarrollo. Solo un tag vX.Y.Z despliega a
producción, con el rol de IAM restringido a ese origen. Revertir es
redesplegar el tag anterior.
Vas a construir TaskFlow, un tablero de tareas. Es deliberadamente pequeño: crear, marcar como hecha, filtrar y borrar.
Sin framework. Vite para el build, TypeScript, y una sola dependencia de runtime:
@preact/signals-core, que aporta reactividad en poco más de un kilobyte. El puente
al DOM lo escribes tú en unas cuarenta líneas.
En este workshop la aplicación no tiene backend: las tareas viven en el navegador y
se guardan en localStorage. El backend con API Gateway, Lambda y DynamoDB es el
Workshop 02, y va a consumir exactamente esta misma SPA.
Preparar el entorno. Toolchain en contenedor, cuenta de AWS y presupuesto de gasto.
Crear tus dos repositorios. Uno para la infraestructura y otro para la aplicación, con los entornos de GitHub y la confianza OIDC.
Construir la SPA. Vite y TypeScript sin framework, con reactividad por signals y un router propio sobre la History API.
Crear la infraestructura con Terraform. Bucket privado, CloudFront con OAC, y el fallback de rutas que vas a provocar y arreglar.
Poner el dominio. Certificado en ACM validado por DNS, y tu subdominio en producción.
Automatizar el despliegue. GitHub Actions autenticando contra AWS con OIDC, sin claves de acceso guardadas en ningún sitio.
Versionar por tag. Release semántico que dispara producción, aprobación humana, y reversión en un minuto.
Cerrar. Costos, qué observar, y destruir todo lo creado.
Este workshop crea recursos que cuestan dinero, aunque poco. El módulo de cierre
incluye el terraform destroy y una revisión de que no quedó nada corriendo. No lo
saltes.
Empieza por los prerequisitos.