ICM / how-to-do / Desplegar una aplicación .Net con Pipeline CI/CD en AWS ECS y Fargate. Parte 3.

Desplegar una aplicación .Net con Pipeline CI/CD en AWS ECS y Fargate. Parte 3.

13 octubre 2021 | Carlos Calvo

La entrada de hoy es la última publicación del caso práctico para realizar el despliegue de una aplicación desarrollada en ASP.NET MVC 5 en un clúster de AWS Fargate mediante el uso de Terraform. Una vez explicado el entorno y preparado la infraestructura, vamos a proseguir con el último apartado: preparar la Pipeline CI/CD.

Así, llegados a este punto, ya deberíamos tener toda la infraestructura montada. Tenemos el proyecto en nuestro repositorio Git y tenemos los servicios necesarios para compilar y ejecutar nuestro código. Además, hemos configurado todos los aspectos que necesitamos de red, balanceadores de carga y clústers. Así que lo único que nos quedaría es dar de alta el mecanismo por el cual un push de código a nuestro repositorio de Git cause la compilación, guardado de imagen y ejecución de nuestro proyecto web.

Para llevar a cabo esta tarea, necesitamos únicamente 2 servicios de AWS más: un proyecto de CodeBuild y la Pipeline propiamente dicha. CodeBuild simplemente definirá las características de compilación de nuestro proyecto que serán usadas por la Pipeline para llevar a cabo sus funciones, como veremos a continuación.

Puedes consultar la documentación del servicio aquí.

CodeBuild

En este punto vamos a configurar de qué manera se debe compilar nuestra aplicación. Para hacerlo, usaremos un código como el siguiente:

Lo primero que haremos será darle un nombre y una descripción. Estableceremos un tiempo máximo de compilación de 60 segundos mediante la propiedad build_timeout. Esto significa que, si el proceso de compilado de la aplicación supera ese tiempo, nos retornará un error y el proceso no continuará.

Asimismo, mediante el uso de la propiedad “source_version” le indicamos al servicio de qué rama debe descargarse el código que va a compilar. También especificamos los permisos del servicio con el uso de la propiedad service_role.

Luego usamos dos bloques de código para definir los artefactos que la compilación generará y el dispositivo de almacenamiento S3 que usaremos como caché durante el proceso. Los artefactos no se empaquetarán o comprimirán y se guardarán directamente en la canalización o Pipeline.

Configuramos si queremos logs o no del proceso y dónde se deben guardar estos y, por último, estableceremos el entorno de ejecución del proceso. Esto es básicamente la capacidad que tendrá la instancia que AWS levanta para compilar nuestro código. Lo haremos configurando las propiedades “compute_type”, “image”, type”, “image_pull_credentials_type” y “privileged_mode”. También agregaremos una variable de entorno que podrá ser leída posteriormente durante la fase de compilación usando los bloques de código de tipo “environment_variable”.

Ahora podemos configurar, como último paso, la Pipeline para que uses estas características de compilación que acabamos de definir. Puedes consultar la documentación oficial en este enlace.

Pipeline

Las Pipeline la tendremos dividida en fases. Puede haber tantas como uno quiera, pero para este laboratorio hemos definido 3 fases. La primera se encarga de capturar las nuevas subidas de código al repositorio de Git: la segunda se encarga de compilar ese código, generar una imagen y publicarla en el registro y la última fase se encarga de coger la imagen subida por la fase anterior y publicarla en el clúster Fargate.

Aunque originalmente se trata de un único bloque de código en Terraform, en este documento lo vamos a partir en varios trozos para que sea más sencillo de explicar. Los fragmentos “stage {}” son snippets que explicaremos a continuación.

Inicialmente le daremos un nombre a la Pipeline, le asignaremos un rol de ejecución que nos garantice que tiene los permisos necesarios para realizar todas sus funciones (acceso a ECS Registry, al Target Groups, al bucket S3, etc.) y le indicaremos la ubicación donde guardar los artefactos producto de la compilación.

Esta es la configuración básica. Ahora debemos continuar con la definición de las fases propiamente dichas que, como habíamos comentado serán 3.

Fase 1: CodeCommit

Durante la fase de CodeCommit le decimos a la Pipeline de dónde coger el código fuente que posteriormente deberá compilar. Para hacerlo, se lo indicamos del siguiente modo:

Como comentábamos anteriormente, esta es la primera fase y es la encargada de recoger el código que se ha subido a Git mediante un push a la rama indicada.

Fase 2: CodeBuild

Ahora, el código obtenido se debe compilar. Por ello, generaremos una nueva fase en la que compilaremos el código con las reglas y propiedades de compilación que indicamos previamente al definir el proyecto CodeBuild. Definiremos la fase del siguiente modo:

Lo más destacable de este bloque de código es la relación entre la fase y el proyecto de compilación que creamos en el punto anterior. Serán las propiedades de compilación que definimos en el punto anterior las que regirán el comportamiento de esta fase.

Es importante conocer que durante la fase de compilación se hace uso de un fichero que debe estar en la raíz del proyecto. El fichero se debe llamar buildspec.yml y es un fichero que debe contener estructurado en fases los pasos que se deben seguir para dar por buena la compilación. A continuación, mostraremos un fichero de ejemplo que define 3 fases para el Build del proyecto y su posterior publicación en el Container Registry.

Cada una de las variables de entorno (las que son tipo $VAR) las definimos en el servicio CodeBuild del paso anterior. Puedes ver más ejemplos y consultar la documentación oficial de las especificaciones de compilado en la siguiente página.

Básicamente las instrucciones que contiene nuestro buildspec.yml hacen lo siguiente:

  • Verbose en general. Cada echo es una línea que veremos en los logs ubicados en cloudwatch.
  • Login en GitHub y en el Container Registry de AWS.
  • Construcción de la imagen de Docker.
  • Etiquetado de la imagen construida de Docker.
  • Publicar la imagen en el Container Registry de AWS.
  • Generar un artefacto con la URI completa de la imagen a publicar en el Container Registry. Este paso es importante porque en la siguiente fase, Deploy, usaremos ese artefacto para saber qué imagen debemos publicar.

Fase 3: Deploy de la imagen

Si las fases 1 y 2 han funcionado correctamente, AWS se habrá descargado el código fuente, lo habrá compilado y usando el fichero Dockerfile habrá generado una imagen que finalmente ha subido a un Container Registry.

Ahora llega el momento de publicar la imagen en el clúster de Fargate. Para hacerlo debemos definir con Terraform la última de las 3 fases que indicamos al comienzo de esta sección:

Como se puede observar, esta fase será la encargada de realizar el Deploy. Para saber qué imagen debe deployar, obtendrá la URL del fichero imageDetail.json generado en la fase de Build dentro del fichero buildspec.yml. Además de indicarle la imagen a deployar, le decimos en qué clúster y bajo qué servicio se debe publicar.

Aplicar los cambios

Ahora que ya tenemos los ficheros de Terraform referentes a la Pipeline CI/CD escritos, podemos aplicarlos para que Terraform genere la infraestructura que hemos definido en los ficheros. Nuevamente, lo haremos:

Conclusión

En este tutorial orientativo os hemos guiado por los pasos básicos para desplegar un proyecto de desarrollo a través de una Pipeline CI/CD en una infraestructura de AWS Fargate.

Como ya comentamos, se trataba de los pasos básicos, por lo que sería conveniente extender el tutorial de modo que nos familiaricemos tanto con el uso de Terraform como con los posibles problemas que el montaje en AWS nos puede dar. Aunque pueda parecerlo, no es un tema sencillo por lo que conviene practicar para conseguir funcionalidades un poco más avanzadas de los servicios de AWS.