Cómo implementar continuamente un sitio web estático con estilo usando GitHub y AWS

En esta publicación, aprenderemos cómo usar AWS CodePipeline y CodeDeploy para recuperar automáticamente el código fuente de un sitio web estático desde GitHub e implementar ese sitio web en S3.

Configuraremos la implementación para que se realice en cualquier nueva confirmación en nuestra rama maestra.

Comenzamos creando una canalización de código con una etapa fuente vinculada a nuestro repositorio GitHub. Cuando se envía un nuevo commit a nuestra rama maestra, la canalización verifica automáticamente el último código. Entonces podemos activar un paso de compilación en nuestra tubería. Este paso puede instalar dependencias, ejecutar pruebas y empaquetar nuestro sitio para su implementación. Nuestro paso final luego despliega nuestro sitio web estático en nuestro depósito del sitio web estático S3.

Ahora que sabemos lo que vamos a construir, comencemos y aprendamos aún más al desarrollar esto.

Requisitos previos de AWS CodePipeline

Para colocar una AWS CodePipeline en nuestra cuenta que se comunica con nuestro repositorio de GitHub, hay algunos requisitos previos que debe tener en cuenta.

  1. Debería tener una cuenta de AWS ya configurada.
  2. Debe tener acceso a la CLI configurado para su cuenta.
  3. Su sitio web estático ya debería estar alojado fuera de AWS S3. Si no, mira este enlace.

Configuración de comunicación GitHub y AWS

Para que AWS pueda sondear los cambios en nuestra rama maestra en GitHub, necesitamos poder generar un token de acceso para nuestro repositorio de GitHub. Podemos generar un token de acceso personal completando los siguientes pasos desde GitHub.

  1. Mientras está conectado a GitHub, haga clic en su foto de perfil en la esquina superior derecha, luego haga clic en Configuración.
  2. A la izquierda, haz clic en Configuración del desarrollador.
  3. A la izquierda, haz clic en Tokens de acceso personal.
  4. Haga clic en Generar nuevo token e ingrese AWSCodePipeline para el nombre.
  5. Para permisos, seleccione repositorio.
  6. Haz clic en Generar token.
  7. Copie el token en algún lugar para que podamos usarlo más tarde.

Creando nuestro CodePipeline a mano

Lo primero que debemos aprovisionar es CodePipeline. Nuestra canalización consistirá en dos etapas, una etapa de origen conectada a GitHub y una etapa de construcción que implementa nuestro sitio web estático.

Avancemos y creemos nuestra CodePipeline a través de la consola de AWS:

  1. Navegue a CodePipeline en la consola de AWS.
  2. Haga clic en Crear canalización.
  3. Ingrese un nombre para su Pipeline.
  4. Seleccione GitHub como el proveedor de origen.
  5. Haz clic en Conectar a GitHub. Esto abrirá una ventana separada donde inicie sesión en su cuenta de GitHub. Una vez que haya iniciado sesión, debe otorgar acceso de repositorio a AWS CodePipeline. Este es el enlace de comunicación entre su repositorio de GitHub y CodePipeline.
  6. Seleccione el repositorio que desea usar en esta canalización.
  7. Ingrese master, o su rama predeterminada, en la entrada de la rama.
  8. Haga clic en Siguiente.
  9. Para el proveedor de Build vamos a elegir AWS CodeBuild.
  10. Seleccione Crear un nuevo proyecto de compilación.
  11. Ingrese un nombre para su proyecto Build.
  12. Para la imagen del entorno, utilizaremos una imagen proporcionada por AWS CodeBuild.
  13. Seleccione Ubuntu como sistema operativo.
  14. Seleccione Node.js como Runtime y nodejs: 6.3.1 como Versión.
  15. Deje la especificación Build como la opción buildspec.yml.
  16. En la sección de rol de servicio de CodeBuild, queremos crear un nuevo rol de servicio.
  17. Ingrese un nombre para el rol de servicio que CodeBuild usará.
  18. Deje el resto de los valores en su configuración predeterminada.
  19. Haga clic en Guardar proyecto de compilación y luego en Siguiente.
  20. Para el proveedor de implementación no queremos ninguna implementación.
  21. Haga clic en Siguiente.

¿A quién le gusta hacer clic en los botones? Yo no.

Eso fue un montón de clic de botón, ¿verdad? ¿Podrías hacerlo de nuevo sin mirar los 21 pasos? Sé que no pude.

¡Buenas noticias! Hay una forma mucho mejor de crear y administrar sus canalizaciones de código, o cualquier infraestructura de AWS para el caso. Es posible que haya oído hablar del término Infraestructura como código, y es casi exactamente como suena. Representa tu infraestructura como código para que puedas crearla, mantenerla y destruirla sin tener que abrir una GUI.

No hay nada de malo en comenzar con la GUI si es nuevo en AWS o en cualquier nuevo proveedor de la nube. Pero queremos apuntar a la automatización a medida que escalamos.

Existen muchas herramientas que hacen que esto sea muy fácil de hacer. AWS proporciona CloudFormation, que le permite definir sus recursos dentro de plantillas JSON o YAML.

CloudFormation es excelente, pero también hay otras herramientas disponibles. Uno que he estado usando mucho recientemente es Terraform. Es independiente del proveedor de la nube y admite una variedad de proveedores a través de módulos desarrollados por la comunidad.

Para esta publicación de blog, armé una plantilla rápida de Terraform que aprovisiona nuestra infraestructura de AWS.

Hagamos un recorrido rápido por lo que está haciendo esta plantilla.

En la parte superior, estamos definiendo las variables que se pasarán a la plantilla. Para aprovisionar los recursos, necesitamos pasar lo siguiente:

  • nombre de nuestra tubería
  • nuestro nombre de usuario de GitHub
  • nuestro token GitHub de antes
  • el repositorio de GitHub que queremos vincular a nuestra tubería

Luego especificamos que queremos usar AWS como nuestro proveedor con la región pasada como variable. Como veremos en un minuto, esto carga un proveedor de Terraform que admite la mayoría de los recursos de AWS. Puede consultar qué recursos de AWS admite Terraform aquí.

El siguiente conjunto de recursos que estamos creando son para nuestra AWS CodePipeline.

  • Creamos un cubo S3 que contendrá los artefactos / salidas de cada etapa en nuestra tubería.
  • Necesitamos crear una política de IAM que permita a CodePipeline asumir un rol que creamos aquí en nuestra plantilla, codepipeline_role. Ese rol tiene una política adjunta, attach_codepipeline_policy. La política otorga acceso a los servicios de AWS que necesitamos llamar durante una invocación de nuestra tubería.
  • Configuramos los recursos necesarios para que CodeBuild funcione como se espera. Definimos una política de asumir roles que permite a CodeBuild asumir un rol y acceder a los servicios a través de codebuild_policy.
  • Creamos nuestro proyecto CodeBuild real, build_project, que ejecuta la etapa de compilación de nuestra CodePipeline. Observe aquí que especificamos que la fuente sea codepipeline y que buildspec sea buildspec.yml.

Para aprovisionar nuestro CodePipeline, asignamos el almacén de artefactos para que sea el depósito S3 que aprovisionamos anteriormente. El rol es el rol de codepipeline que también definimos anteriormente. La etapa de origen utiliza el proveedor de GitHub y el token que generamos en GitHub. Queremos enviar la salida de esta etapa a una etiqueta llamada código a través de la propiedad output_artifacts.

La última etapa en nuestro recurso CodePipeline es la etapa de construcción. Aquí el proveedor es CodeBuild y hemos definido nuestros input_artifacts como el código output_artifacts de nuestra etapa Source. Luego especificamos el ProjectName para el proyecto CodeBuild que será responsable de ejecutar la etapa Build.

Todo lo que necesitamos para aprovisionar nuestra tubería de implementación continua se encuentra en esta plantilla. Si solo está buscando comenzar a usar AWS, entonces configurar esto a mano puede ser más rápido que escribir su primera plantilla Terraform. Pero a la larga, definir su infraestructura como código tiene enormes beneficios:

  • Su definición de infraestructura vive en el control de la fuente. Se puede iterar como lo sería el código.
  • Su infraestructura ahora es repetible. Si necesita moverlo a otra región de AWS, puede ejecutar la plantilla en esa región.
  • Puede realizar cambios rápidamente cambiando la plantilla y aplicando actualizaciones.

Ahora que sabemos lo que está haciendo esta plantilla y tenemos Terraform instalado, podemos ejecutar esta plantilla desde la línea de comandos.

Primero ejecutamos terraform init desde el directorio donde vive nuestra plantilla. Esto atrae las dependencias que Terraform necesita para ejecutar la plantilla.

Una vez que nuestra plantilla Terraform se ha inicializado, podemos usar el comando "plan" para ver qué se va a crear exactamente.

¿Lo notas todo en verde? Estos son los recursos que se crearán. Si hubiera recursos en amarillo, estos serían recursos que se iban a actualizar. Luego, si hubiera recursos en rojo, se eliminarían.

Luego puede ejecutar el comando de aplicación para crear realmente todo en la plantilla. Hay un mensaje de confirmación, simplemente escriba yes.

Una vez que la plantilla se haya completado, deberíamos ver que todos nuestros recursos de AWS se han creado para admitir nuestra canalización de implementación continua.

Espera, ¿qué acabamos de hacer?

Esos son muchos pasos y una infraestructura bastante importante para AWS. Así que veamos a un alto nivel lo que acabamos de crear.

En la parte superior de nuestra cartera tenemos la Fuente, en nuestro caso este es nuestro repositorio de GitHub. Configuramos nuestra tubería para sondear periódicamente la rama maestra de nuestro repositorio. Si hay nuevas confirmaciones en la rama maestra, la tubería se activa para iniciar un nuevo proceso de compilación. Esto es lo que a menudo se denomina desencadenante de nuestra canalización de compilación.

Cuando comienza una nueva compilación, nuestra canalización verifica las últimas confirmaciones de la rama maestra. Una vez que los cambios están desprotegidos, se pasan al siguiente paso en nuestra tubería, el paso Construir. Para este paso, estamos utilizando otro servicio de AWS, CodeBuild. Hemos configurado nuestro proyecto CodeBuild para usar una imagen Node.js proporcionada por Amazon. Esta imagen viene con Node.js ya instalado, por lo que la máquina de compilación que construye nuestro repositorio tiene acceso a él.

Pero, ¿cómo sabe AWS CodeBuild cómo construir nuestro repositorio? Ahí es donde entra en juego buildspec.yml. Este es un archivo especial que colocaremos en la raíz de nuestro repositorio. En él configuramos diferentes fases del proceso de compilación como pre_install, build y post_build. Para nuestro caso de uso, solo vamos a configurar el proceso de compilación en el archivo buildspec. Esto consistirá en copiar los contenidos de nuestra Fuente a nuestro depósito del sitio web S3, implementando efectivamente nuestro sitio web estático.

Pasemos a nuestro repositorio de sitios web estáticos y configuremos nuestro archivo buildspec.

Configurando nuestro archivo buildspec

Vamos a comenzar agregando un archivo buildspec.yml a la raíz de nuestro sitio web estático.

Este archivo será la plantilla que AWS CodeBuild utiliza para compilar y, en nuestro caso, implementar nuestro sitio web estático. Consiste en etapas de preinstalación, instalación, compilación y posterior a la compilación. Para nuestro caso de uso, vamos a aprovechar la etapa de construcción.

Lo que estamos haciendo en la especificación de compilación anterior es bastante sencillo, de hecho, es solo una línea. Tomamos el contenido de nuestro sitio web estático y lo copiamos al bucket S3 que aloja nuestro sitio a través de la interfaz de línea de comandos de AWS. Para los otros pasos, estamos haciendo eco de qué paso se ejecutó en nuestro proceso de compilación.

Por supuesto, podríamos hacer aún más aquí si tuviéramos que hacerlo. Por ejemplo, si necesitáramos ejecutar un proceso de compilación en nuestro package.json, los pasos de compilación y post_build se verían así:

Ahora estamos ejecutando npm build dentro de nuestro paso de compilación y guardando el comando s3 sync para nuestro paso posterior a la compilación. Nuestra especificación de compilación nos brinda la capacidad de realizar scripts no solo de las implementaciones de nuestro sitio, sino también de cómo se crean y prueban. También podríamos aprovechar las otras etapas, como la instalación, para agregar cualquier dependencia que nuestro proceso de compilación necesite.

Por ahora, sigamos con nuestro archivo buildspec original que está copiando nuestro sitio estático en S3. Asegúrese de que esté en la raíz de su repositorio, ya que aquí es donde CodeBuild lo buscará. Verifíquelo en su repositorio para que podamos activar nuestra CodePipeline.

Activando nuestro CodePipeline

Anteriormente vinculamos la etapa Fuente de nuestra CodePipeline con el repositorio GitHub de nuestro sitio web estático. Está configurado para observar los cambios en nuestra rama maestra. Por lo tanto, cualquier cambio nuevo enviado a esa rama desencadena una nueva ejecución de CodePipeline. Como acabamos de registrar en nuestro archivo buildspec.yml, ahora deberíamos ver una invocación de nuestra CodePipeline en ejecución.

Vemos aquí que nuestra etapa de origen se ha invocado debido a los nuevos cambios en la rama maestra de nuestro repositorio. Esto completó y envió sus artefactos a la etapa de construcción. La etapa de compilación tomó esos artefactos y ejecutó el archivo buildspec.yml para implementarlos en nuestro depósito S3.

Si hiciéramos clic en el enlace de detalles en nuestra etapa DeployToS3, podemos ver los registros que está generando nuestro proceso de compilación.

Una vez que nuestra etapa DeployToS3 haya tenido éxito, deberíamos poder volver a cargar nuestro sitio web estático en el navegador y ver nuestros cambios.

Bam! Tenemos despliegue continuo

Canalizando mi Emeril interno aquí, ahora tenemos un despliegue continuo para nuestro sitio web estático. Con cualquier nueva confirmación en nuestra rama maestra, se activa una nueva ejecución de CodePipeline. Esto comprueba el último código de GitHub y lo pasa a CodeBuild. Nuestro proyecto de compilación luego ejecuta lo que está en nuestro archivo buildspec.

Actualmente, nuestro archivo buildspec solo está copiando el contenido de nuestro sitio web estático en nuestro bucket S3. Pero, podríamos extender esto para hacer más cosas. Podríamos ejecutar tareas npm para construir nuestro sitio o ejecutar pruebas. Si también estamos utilizando CloudFront frente a nuestro sitio web estático, podemos emitir una solicitud de invalidación cuando implementemos nuestro nuevo sitio.

Hay tantas cosas que puedes aprender al sumergirte y usar AWS. Un sitio web estático puede parecer un caso de uso simple, pero es increíble para aprender una gran variedad de cosas.

¿Tienes hambre de aprender más sobre Amazon Web Services?

Llevo más de seis años usando AWS y siempre estoy aprendiendo nuevos servicios y nuevas formas de usar los existentes. Es una plataforma masiva con mucha documentación. Pero, hay momentos en que esa documentación puede sentirse como un mar masivo de información. Hasta el punto de perderte en él.

Inspirado por este problema, recientemente publiqué un libro electrónico y un curso de video que atraviesa el mar de información. Se centra en alojar, proteger e implementar sitios web estáticos en AWS. El objetivo es aprender servicios relacionados con este problema a medida que los usa. Si ha querido aprender AWS, pero no está seguro de por dónde empezar, consulte mi curso.

¡Si disfrutaste esto, no olvides ofrecer aplausos para mostrar tu apoyo!