Blog de Amazon Web Services (AWS)

Cómo alojar tu sitio web estático en Amazon S3 y Amazon CloudFront

Israel López y Carlos Afonso, Arquitectos de Soluciones de AWS

Hoy en día es muy habitual tener que servir algún tipo de sitio web estático, desde sencillas páginas web para que tus clientes puedan conocerte hasta complejas aplicaciones basadas en frameworks como React, Vue.js o Angular. En un escenario tradicional, este contenido se ofrecería desde un servidor web como Apache o NGINX ejecutándose, por ejemplo, en una instancia de Amazon EC2. Por lo tanto, el operador de la infraestructura debe encargarse de tareas como la configuración de los servidores y de la aplicación de parches y actualizaciones, y asegurarse de que hay redundancia para seguir respondiendo peticiones aunque alguna de las máquinas tenga algún problema.

En este artículo veremos cómo utilizar nuestro servicio de almacenamiento de objetos, Amazon S3, y nuestra red de entrega de contenido (CDN), Amazon CloudFront, para liberarte de esa carga de trabajo y servir tus aplicaciones y sitios estáticos de manera eficiente, segura, rápida y económica. Además, haremos uso de Amazon Route 53 para gestionar los registros DNS, y mostraremos cómo desplegar nuevas versiones de tu contenido de manera automática y fiable con las herramientas para desarrolladores de AWS: AWS CodePipeline, AWS CodeCommit y AWS CodeBuild. Por último, haremos todo esto sin levantar un solo servidor.

Descripción del escenario

Supongamos que somos los fundadores de una pequeña start-up que, en un alarde de originalidad, hemos llamado AnyCompany. Una de las primeras tareas que nos hemos propuesto es crear un sitio web estático, con un fichero HTML (index.html) y una hoja de estilos CSS (styles.css).

Nuestro objetivo es implementar una solución que nos permita alojar este sitio en AWS, cumpliendo con los siguientes requisitos:

  • El sitio debe alojarse de manera sencilla y requerir el mínimo esfuerzo de operación por nuestra parte.
  • El sitio debe poder visitarse a través de HTTPS usando nuestro propio dominio (por ejemplo, anycompany.example).
  • El sitio debe estar disponible el máximo tiempo posible (alta disponibilidad).
  • El sitio debe servirse con baja latencia a todos nuestros clientes, que se encuentran repartidos por todos los países de habla hispana.
  • Los cambios realizados por nuestro equipo de desarrollo deben desplegarse de manera automática en cuanto se suban al repositorio de código.

El siguiente diagrama muestra la arquitectura de la solución:

Diagrama de arquitectura

Alojamiento del sitio en Amazon S3

Amazon S3 es el servicio de almacenamiento de objetos de AWS, que permite guardar y acceder a cualquier tipo de fichero de hasta 5 TB de tamaño de manera segura a través de una API. Los clientes no tienen que gestionar ningún tipo de servidor. Internamente, Amazon S3 se encarga de almacenar múltiples copias del objeto en diferentes Zonas de Disponibilidad dentro de la región seleccionada, razón por la que ofrece una durabilidad que alcanza los 11 nueves (99.999999999%).

La unidad lógica de almacenamiento en S3 es el bucket. El primer paso consiste en crear un bucket, dentro del cuál subiremos los archivos de nuestra web. En nuestro caso vamos a trabajar en la región de Irlanda, por lo que es importante asegurarse de que en la esquina superior derecha de la consola de administración de AWS aparece esa región seleccionada (en caso contrario basta con desplegar la lista de regiones y cambiar a la correcta).

Una vez ubicados en la región de Irlanda, ya podemos crear el bucket:

  1. Dirígete a la consola de administración de Amazon S3.
  2. En la lista de buckets, haz click en Create bucket.
  3. Dale un nombre arbitrario a tu bucket (recuerda que los nombres deben ser únicos dentro de S3 de manera global) y escoge la región de tu interés. Puedes dejar el resto de opciones como están.
  4. Haz click en Create bucket.
  5. Ve a la vista de detalle del bucket. Haz click en Upload, arrastra los dos ficheros (index.html y styles.css) al recuadro que dice “Drag and drop files and folders you want to upload here”, y
    finaliza haciendo click en Upload.

Ahora mismo, estos objetos son privados y no son accesibles por ningún usuario excepto nosotros. Este es el comportamiento por defecto de S3, aunque los clientes pueden ajustar los permisos de manera granular (idealmente, siguiendo siempre el principio de mínimo privilegio).

S3 ofrece una característica que permite acceder a los objetos de un bucket como si se tratara de un servidor web. Técnicamente esta solución nos podría servir, pero entonces obligaríamos a todos nuestros clientes a acudir a nuestro bucket, que, recordemos, está en la región de Irlanda. Un cliente en España vería un tiempo de respuesta aceptable, ya que está cerca, pero uno en Argentina tendría que esperar a que su petición cruce todo el Atlántico ida y vuelta. Por lo tanto, incumpliríamos uno de los requisitos que nos propusimos al inicio del artículo. Sin embargo, tenemos otras opciones.

Uso de Amazon CloudFront para servir el sitio

Las redes de entrega de contenidos (content delivery network, CDN) son servidores distribuidos por todo el mundo que actúan como caché para cierto contenido web, idealmente contenido estático que no varía de manera frecuente. Cuando un cliente visita un sitio que se sirve a través de una CDN, su petición acude primero a su caché local (que debería estar muy cerca de él, en su mismo país o incluso en la ciudad). Si el contenido no se encuentra en la caché, la CDN solicita el contenido al origen, lo devuelve al cliente y lo almacena en la caché. A partir de ahí, las peticiones de otros clientes se servirán desde la propia caché mientras el objeto siga ahí, reduciendo de manera notable la latencia de respuesta.

Amazon CloudFront es el servicio de CDN de AWS. Con más de 220 puntos de presencia (PoPs) en todo el mundo, podemos usar CloudFront para servir nuestro contenido a usuarios de manera global con una latencia baja, usando el bucket de S3 que creamos previamente como origen. Para ello tenemos que crear una distribución.

  1. Ve a la consola de administración de Amazon CloudFront.
  2. En la lista de distribuciones haz click en Create Distribution, y luego selecciona una distribución Web.
  3. En Origin Domain Name, introduce el nombre de dominio de tu bucket. Es importante que uses el dominio regional. Por ejemplo, si el bucket se llama “my-bucket” y está en la región de Irlanda (eu-west-1), entonces el dominio debe ser “my-bucket.s3-eu-west-1.amazonaws.com”.
  4. En Restrict Bucket Access selecciona Yes.
  5. En Origin Access Identity escoge Create a New Identity.
  6. En Grant Read Permissions on Bucket selecciona Yes, Update Bucket Policy.
  7. En Viewer Protocol Policy escoge Redirect HTTP to HTTPS.
  8. Por último, en Default Root Object introduce index.html.
  9. Haz click en Create Distribution.

Con los pasos 4, 5, y 6 nos aseguramos de que sólo CloudFront puede acceder a los objetos de S3, mediante una identidad de acceso a origen (origin access identity, OAI). Un cliente llamando directamente al bucket de S3 vería su acceso denegado.

Tras unos minutos, la distribución pasará a estado Deployed. Si visitas la URL que aparece bajo la columna Domain Name, verás nuestra página web dándote la bienvenida.

Dominio personalizado

Ya tenemos la distribución de CloudFront sirviendo nuestra web por todo el mundo. Sin embargo, el dominio por defecto que CloudFront nos asigna (ab12cd34ef56gh.cloudfront.net) no es, digamos, muy atractivo. Sería ideal poder usar nuestro propio dominio (por ejemplo, www.anycompany.example).

Amazon Route53 es el servicio de AWS para adquirir y gestionar nombres de dominio. Con un SLA del 100%, a través de Route 53 podemos configurar registros DNS que resolverán nuestro dominio a las IPs adecuadas. Además, usaremos AWS Certificate Manager (ACM) para generar de manera gratuita un certificado TLS para nuestro dominio, que asociaremos a CloudFront para poder servir tráfico HTTPS. De nuevo, esto se hará fácilmente y sin tener que gestionar servidores ya que se trata de servicios totalmente gestionados.

Creación de una hosted zone en Amazon Route 53

En Amazon Route 53 todos los registros DNS pertenecientes a un dominio se configuran en una hosted zone. El primer paso es, por lo tanto, crear una:

  1. Dirígete a la consola de administración de Amazon Route 53.
  2. En el menú de la izquierda, haz click en Hosted zones.
  3. En la lista de hosted zones, haz click en Create hosted zone.
  4. En Domain name introduce el dominio raíz que quieres gestionar (en nuestro caso, anycompany.example).
  5. Asegúrate de que el tipo es Public hosted zone, y haz click en Create hosted zone.

Todas las hosted zones vienen con un registro NS. Éste contiene los servidores de nombres autoritativos para esta hosted zone a los que debes apuntar desde tu registrador, en caso contrario los cambios que realices a continuación no tendrán efecto. En esta sección de nuestra documentación podrás encontrar diversos artículos que indican cómo hacerlo.

Creación de un registro DNS hacia la distribución de Amazon CloudFront

En la vista de detalle de la hosted zone, sigue estos pasos para añadir un registro DNS que permita resolver nuestro subdominio a la distribución de CloudFront:

  1. Haz click en Create record, selecciona Switch to wizard y escoge Simple routing.
  2. Haz click en Define simple record.
  3. En Record name, escribe el prefijo del subdominio. En nuestro caso, www.
  4. En Value/Route traffic to selecciona Alias to CloudFront distribution, e introduce el dominio de tu distribución (similar a “ab12cd34ef56gh.cloudfront.net”) en el campo Choose distribution.
  5. Haz click en Define simple record, y a continuación en Create records.

Si ahora resuelves el subdominio completo con una herramienta como nslookup o dig, verás que Route 53 proporciona un conjunto de IPs correspondientes al PoP de CloudFront más cercano a ti.

Creación de un certificado TLS con AWS Certificate Manager

Antes de poder servir tráfico HTTPS usando nuestro dominio personalizado necesitamos crear un certificado TLS. AWS Certificate Manager (ACM) permite generar certificados de manera gratuita. Además, ACM se encarga de aplicarlos a servicios como CloudFront e incluso de renovarlos automáticamente antes de la fecha de expiración. Una vez más, de forma totalmente gestionada.

Antes de emitir el certificado, ACM comprobará que nosotros somos los propietarios del dominio. ACM se integra con Route 53 para crear automáticamente los registros DNS necesarios, haciéndolo si cabe aún más sencillo:

  1. Ve a la consola de administración de AWS Certificate Manager.
  2. En la lista de certificados, haz click en Request a certificate.
  3. Selecciona Request a public certificate y haz click en Request a certificate.
  4. En Add domain names escribe el subdominio que hemos registrado en Route 53 (en nuestro ejemplo, www.anycompany.example) y haz click en Next.
  5. Selecciona DNS validation y haz click en Next.
  6. Haz click en Review, y posteriormente en Confirm and request.
  7. El dominio estará en el estado Pending validation. Para añadir automáticamente los registros de Route 53 necesarios para que ACM los valide, despliega el detalle del dominio y haz click en Create record in Route 53.
  8. Haz click en Create tras revisar el registro que será creado.
  9. Por último, haz click en Continue.

Tras pocos minutos, el certificado pasará al estado Issued y ya podrá usarse en CloudFront.

Modificación de la distribución de Amazon CloudFront para usar nuestro dominio

El último paso consiste en modificar la distribución de CloudFront para que acepte tráfico dirigido a nuestro dominio:

  1. Ve, de nuevo, a la consola de administración de Amazon CloudFront y selecciona la distribución creada previamente.
  2. En la pestaña General, haz click en Edit.
  3. En el campo Alternate Domain NAmes (CNAMEs), que antes dejamos en blanco, añade tu dominio (en nuestro caso, www.anycompany.example).
  4. En SSL Certificate, escoge Custom SSL Certificate (example.com), y en el campo de texto selecciona el certificado que has creado en ACM.

Cuando el cambio se propague a todos los PoPs, podrás visitar el sitio web con el dominio personalizado.

Despliegue automático de cambios y cache busting

Ya tenemos nuestro sitio en línea y sirviéndose a usuarios de todo el mundo con latencias bajas gracias al uso de Amazon CloudFront. Sin embargo, nuestro equipo de desarrollo no está del todo satisfecho porque cada vez que cambian algo deben subir a mano las modificaciones al bucket de S3.

Podemos ayudarles con un pipeline de despliegue continuo, que automáticamente aplicará los cambios desde el momento en el que se incorporen al repositorio de código. Para ello usaremos los servicios para desarrolladores de AWS: AWS CodeCommit, que ofrece repositorios Git seguros, escalables y durables; AWS CodeBuild, que permite compilar código y ejecutar pruebas unitarias, y AWS CodePipeline, que permite orquestar pipelines de CI/CD. Por supuesto, todos ellos son servicios gestionados que, de nuevo, no requieren levantar ningún servidor.

En nuestro caso, el pipeline es muy sencillo:

Pipeline de CodePipeline con stages source build y deploy

  1. El desarrollador realiza un cambio en el repositorio de AWS CodeCommit.
  2. AWS CodeCommit dispara un evento que lanza una nueva ejecución de AWS CodePipeline.
  3. AWS CodePipeline se descarga el código del repositorio y lo pone a disposición de la siguiente etapa del pipeline.
  4. Un job de AWS CodeBuild empaqueta los ficheros y los almacena como artefactos para el siguiente paso.
  5. El pipeline sube los artefactos al bucket del sitio web, estableciendo las cabeceras de caché adecuadas.

El job de CodeBuild se define en un fichero YAML llamado buildspec.yml, incluyendo todos los comandos a realizar:


version: 0.2

phases:
  build:
    commands:
      - export CACHEBUST_SUFFIX=$(date +"%y%m%d%H%M%S")
      - mv styles.css styles-${CACHEBUST_SUFFIX}.css
      - sed -i.bak s/styles.css/styles-${CACHEBUST_SUFFIX}.css/g index.html
      - rm index.html.bak
artifacts:
  secondary-artifacts:
    IndexFileArtifact:
      files:
        - 'index.html'
    RestOfFilesArtifact:
      files:
        - 'styles-*.css'

Si te fijas, en este paso estamos añadiendo un sufijo al nombre del fichero styles.css con la fecha y hora del despliegue (por ejemplo, styles-20201119085431.css). Con este mecanismo conseguimos que el cliente siempre vea la última versión, aunque la anterior siga cacheada en CloudFront. Esto se conoce como cache busting.

Además, definimos dos artefactos separados: uno contiene index.html y el otro contiene el resto de ficheros (en este caso, el fichero CSS renombrado). Hacemos esto porque, al desplegarlos en S3, les asignamos distintos valores para la cabecera Cache-Control, que es la que determina el tiempo máximo que los objetos deben permanecer tanto en la caché de CloudFront como en la de los navegadores web. En el caso de index.html usamos un valor pequeño (por ejemplo, 60 segundos), mientras que la hoja de estilos puede estar cacheada de manera prácticamente indefinida (en nuestro caso, 1 año).

El despliegue propiamente dicho se realiza directamente desde CodePipeline, ya que tiene una integración nativa con S3. Para ello sólo hace falta añadir a la última etapa una acción de tipo Deploy y especificar el bucket de destino, así como las cabeceras de caché del párrafo anterior.

Conclusión

En este artículo hemos visto cómo servir un sitio web estático de manera sencilla y usando servicios totalmente gestionados, sin necesidad de administrar ni un solo servidor. Al alojarse en Amazon S3 y servirse con Amazon CloudFront, nuestro sitio puede procesar con muy baja latencia cantidades masivas de peticiones procedentes de todo el mundo. Gracias a AWS Certificate Manager protegemos en tránsito el tráfico de nuestros clientes con certificados TLS gratuitos y que se renuevan de manera automática. Y, además, los cambios realizados por nuestros desarrolladores se ponen en producción de manera prácticamente instantánea en cuanto se suben al repositorio de código, gracias a AWS CodeCommit, AWS CodeBuild y AWS CodePipeline.

 


Sobre los autores

Israel López es Arquitecto de Soluciones para Startups basado en Barcelona. Disfruta pudiendo trabajar de cerca con las Startups ayudando a empujar la innovación desde los servicios en la nube.

 

 

 

 

Carlos Afonso es Arquitecto de Soluciones para Startups. En una vida previa trabajó como ingeniero de desarrollo en diferentes startups, y siempre que tiene oportunidad implementa pruebas de concepto y soluciones para nuestros clientes usando los últimos servicios y tecnologías de AWS.