Despliegue CI/CD de un sitio web estático a un bucket AWS S3 a través de Github Actions

Despliegue CI/CD de un sitio web estático a un bucket AWS S3 a través de Github Actions

por Rebeca Murillo • 18 noviembre 2023


Versión en video de este artículo


Introducción

Bienvenidos a este tutorial donde exploraremos el proceso de configuración de Integración Continua y Despliegue Continuo (CI/CD) para desplegar un sitio web estático en un bucket de Amazon S3 directamente desde nuestro repositorio de GitHub. Repasaremos el proceso de configuración de las acciones de GitHub en nuestro proyecto, la configuración de nuestra cuenta de AWS y nos aseguraremos de que nuestro sitio web funcione correctamente después de cada despliegue.

Esta configuración puede llevar algo de tiempo para establecerla, ¡pero te ahorrará tiempo en el futuro! Nuestro sitio web se actualizará automáticamente con un solo clic, simplemente ejecutando el flujo de trabajo de GitHub en nuestra rama principal.

Este tutorial se divide en tres pasos:

  1. Flujo de trabajo de las acciones de GitHub
  2. Configuración del rol de AWS para conectarse a las acciones de GitHub
  3. Configuración de los secretos de las acciones de GitHub y ejecución del flujo de trabajo

Requisitos previos

Para seguir este tutorial, deberás configurar previamente:

Con esta guía, obtendrás una comprensión profunda de la construcción, verificación de calidad y despliegue de nuestro sitio web estático, y finalmente, la verificación del sitio en producción. ¡Comencemos esta emocionante aventura de automatizar el despliegue de nuestro sitio web!

Flujo de trabajo de las acciones de GitHub

Para iniciar el proceso, configuraremos las acciones de GitHub en el proyecto.

  • Agrega un archivo llamado .github/workflows/build-deploy.yml en el directorio de nuestro proyecto.
  • Copia el siguiente contenido, adaptándolo según tus necesidades.
name: Build and deploy to S3 bucket

on: workflow_dispatch

permissions:
  id-token: write
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout Repository
        uses: actions/checkout@v4
      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 18
      - name: Install Dependencies
        run: npm install
      - name: Build and Output to Dist
        run: npm run build
      - name: Upload Build Artifacts
        uses: actions/upload-artifact@v3
        with:
          name: build-artifacts
          path: dist

  quality:
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v4
      - name: Download Build Artifacts
        uses: actions/download-artifact@v3
        with:
          name: build-artifacts
          path: dist
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: "3.10"
      - name: Run localhost server
        run: |
          cd dist ; ls -la ;
          python -m http.server 8080 &
      - name: Wait for Local Server to Start
        run: sleep 3
      - name: Check homepage status in localhost
        run: |
          URLs=("http://localhost:8080/" "http://localhost:8080/en/" "http://localhost:8080/es/" "http://localhost:8080/fr/")
          for url in "${URLs[@]}"; do
              response_code=$(curl -w "%{http_code}" -I "$url" -o /dev/null)
              echo -e "e HTTP Status Code for $url: $response_code"
              echo "HTTP Status Code for $url: $response_code"
                
              if [ "$response_code" -ne 200 ]; then
                  echo -e "::error::Error: Unexpected status code for $url"
                  exit 1
              fi
          done

  aws_deploy:
    runs-on: ubuntu-latest
    needs: [build, quality]
    steps:
      - name: Download Build Artifacts
        uses: actions/download-artifact@v3
        with:
          name: build-artifacts
          path: dist
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: ${{ secrets.AWS_REGION }}
      - name: Empty S3 Bucket
        run: aws s3 rm s3://${{ secrets.AWS_S3_BUCKET }} --recursive
      - name: Upload to S3 bucket
        run: |
          ls -la dist/
          aws s3 cp dist/. s3://${{ secrets.AWS_S3_BUCKET }} --recursive
      - name: Clean CloudFront Cache
        run: aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_CLOUDFRONT_DISTRIBUTION_ID }} --paths "/*"

  healthcheck:
    runs-on: ubuntu-latest
    needs: aws_deploy
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v4
      - name: Check homepage status @ https://mywebsite
        run: |
          URLs=("https://rebeca.murillo.link/" "https://rebeca.murillo.link/en/" "https://rebeca.murillo.link/es/" "https://rebeca.murillo.link/fr/")
          for url in "${URLs[@]}"; do
              response_code=$(curl -w "%{http_code}" -I "$url" -o /dev/null)
              echo -e "e HTTP Status Code for $url: $response_code"
              echo "HTTP Status Code for $url: $response_code"
              
              if [ "$response_code" -ne 200 ]; then
                  echo -e "::error::Error: Unexpected status code for $url"
                  exit 1
              fi
          done

El flujo de trabajo puede ser desencadenado manualmente o puedes personalizarlo para que se ejecute en función de condiciones como un push de código a la rama principal. Consulta la documentación Eventos de GitHub Actions que desencadenan flujos de trabajo para obtener más información.

El flujo de trabajo se divide en cuatro tareas diferentes:

  1. Tarea de construcción del código fuente
  2. Verificación de calidad local
  3. Despliegue en el bucket S3
  4. Verificación del sitio de producción

Veamos en detalle cada tarea.

¡Ten en cuenta que los permisos son obligatorios para que aws-actions/configure-aws-credentials pueda autenticarse!

permissions:
 id-token: write
 contents: read

1. Tarea de construcción

El primer paso es construir el código fuente del sitio web estático, siguiendo la configuración y el framework de tu sitio web. En este tutorial, el proyecto se construye con el framework Astro, por lo que ejecutamos un script para instalar las dependencias de Node.js y luego construir el código fuente.

  • Configuración del entorno con Node.js e instalación de dependencias con el comando npm install, utilizando GitHub Actions setup Node
  • Construcción de las fuentes finales del sitio web estático con el comando npm run build, por defecto el framework Astro guarda las fuentes construidas en la carpeta dist/.
  • El contenido de la carpeta de destino de la construcción del código fuente se guarda y se transfiere a las tareas siguientes del flujo de trabajo. Para esto, utilizamos la acción actions/upload-artifact de GitHub Actions.

Las tareas siguientes utilizarán la acción actions/download-artifact de GitHub Actions para recuperar el contenido de la carpeta dist/.

2. Verificación de calidad local

En este paso, puedes agregar el comando de prueba de tu proyecto, si tienes uno. Por ejemplo, npm run tests

La segunda tarea consiste en realizar una verificación de la calidad del código generado. Para nuestro ejemplo de sitio web estático, la verificación de calidad implica alojar el sitio localmente y llamar a la página de inicio, por ejemplo.

  • Se utilizan los archivos fuente de la carpeta dist/ de nuestra primera tarea, recuperados con la acción actions/download-artifact de GitHub Actions.
  • Ejecución de un servidor local con el contenido del sitio web estático utilizando la acción Python setup GitHub Actions.
  • Verificación del correcto funcionamiento de algunas páginas del sitio web. Si alguna de las páginas no responde correctamente (código de estado 200), se activa la instrucción exit 1, lo que provoca el fallo del flujo de trabajo de GitHub. Esto evitará desplegar el código fuente en nuestro bucket S3.

3. Despliegue en el bucket S3

Después de verificar que el código fuente generado funciona correctamente, la tercera tarea consiste en desplegar el código fuente en el bucket AWS S3.

  • Se utilizan los archivos fuente de la carpeta dist/ de nuestra primera tarea, recuperados con la acción actions/download-artifact de GitHub Actions.
  • La acción AWS configure-aws-credentials permite la conexión al bucket AWS S3 a través de un rol de AWS. La configuración de este rol se explica en el siguiente capítulo.
  • Se actualiza el bucket AWS que aloja el sitio web estático eliminando primero el contenido anterior y luego cargando los nuevos archivos fuente.
  • Por último, se limpia la caché de CloudFront, ya que puede no reflejar inmediatamente los cambios en el sitio web.

4. Verificación del sitio de producción

La última tarea repite la verificación de calidad de la segunda tarea, pero esta vez en la URL final del sitio. He configurado esta tarea para verificar cuatro páginas, la página de inicio en tres idiomas diferentes, para asegurarnos de que el sitio esté operativo después del despliegue.

En la siguiente sección, examinaremos las configuraciones de AWS necesarias para esta configuración de CI/CD.

Configuración del rol de AWS para conectar con GitHub Actions

Ahora configuraremos nuestra cuenta de AWS para conectarla con GitHub Actions. Para ello, debemos crear un rol en AWS. Este rol se utilizará para configurar las credenciales en el flujo de trabajo de GitHub Actions.

Consulta la documentación de AWS para obtener más detalles sobre este proceso: Usar roles de IAM para conectar GitHub Actions con acciones en AWS

1. Crear un proveedor OIDC para GitHub

Para empezar, debemos crear un proveedor de identidad para GitHub.

  • En la consola de AWS, ve a Identity and Access Management (IAM), en Access Management > Identity Providers.
  • Haz clic en “Agregar un nuevo proveedor”:
    • Tipo de proveedor: “Proveedor Open ID Connect”
    • URL del proveedor: token.actions.githubusercontent.com
    • Audiencia: sts.amazonaws.com

Una vez establecido nuestro proveedor de identidad, podemos pasar a la creación del rol.

2. Crear el rol de AWS

  • En la consola de AWS, ve a Identity and Access Management (IAM), en Access Management > “Roles”.
  • Haz clic en “Crear un nuevo rol”:
    • Tipo de entidad: Identidad web.
    • Selecciona el proveedor de identidad que creamos en el paso anterior. La audiencia será la predeterminada.
    • La organización de GitHub es esencialmente tu cuenta de GitHub donde se encuentra el proyecto.
    • El nombre de tu repositorio de GitHub.
    • La rama de tu repositorio de GitHub que estará autorizada para ejecutar el flujo de trabajo de GitHub Actions.
  • En la etapa de políticas de autorización, configura las reglas según las acciones que se ejecutarán en el flujo de trabajo de GitHub Actions. Para este tutorial, los permisos requeridos son:
    • Gestión del bucket AWS S3: para eliminar y cargar contenido en el bucket.
    • Gestión de CloudFront AWS: para invalidar la caché de CDN.
  • Guarda el nuevo rol.

En la política de confianza, puedes verificar información importante sobre los permisos de tu repositorio de GitHub y la rama correspondiente. Puedes adaptarlo a tu caso de uso. Después de crear el rol, ábrelo para copiar el ARN (Amazon Resource Name), que será necesario en el siguiente paso para configurar los secretos en GitHub.

Configuración de los secretos de GitHub Actions y ejecución del flujo de trabajo

En tu proyecto de GitHub, configura los secretos necesarios para el flujo de trabajo.

  1. Ve a la configuración de tu proyecto de GitHub,
  2. Luego a “Secretos y variables” bajo “Acciones”,
  3. Por último, crea un secreto para cada variable con “Crear un nuevo secreto de repositorio”. En este tutorial, los secretos utilizados en el flujo de trabajo incluyen:
    • AWS_ROLE_ARN: el ARN del rol de AWS creado en el paso anterior.
    • AWS_REGION: la región de AWS para el bucket.
    • AWS_S3_BUCKET: el nombre del bucket AWS S3.
    • AWS_CLOUDFRONT_DISTRIBUTION_ID: el ID de distribución de AWS CloudFront.

Con todas estas configuraciones en su lugar, tu cuenta de AWS está ahora configurada y lista para conectarse a GitHub Actions.

Para ejecutar el flujo de trabajo, ve a la pestaña “Acciones” en el repositorio de GitHub, selecciona el flujo de trabajo y ejecútalo desde la rama correspondiente.

Una vez que se inicie el flujo de trabajo, puedes seguir su progreso. Ejecutará secuencialmente las cuatro tareas que hemos descrito anteriormente: construcción, verificación de calidad local, despliegue en el bucket S3 y verificación del sitio de producción.

Ten en cuenta que seleccionar la rama incorrecta causará errores en el flujo de trabajo. La rama debe estar autorizada en las políticas de roles de AWS.

Espero que este tutorial te haya dado una comprensión clara de cómo el rol de AWS y GitHub Actions funcionan juntos para asegurar despliegues seguros y controlados de tu sitio web. El proceso ahora está completamente automatizado y las autorizaciones están configuradas para evitar despliegues no autorizados, asegurando que tu sitio web se mantenga seguro y actualizado con un solo clic.