En esta entrada vamos a presentar brevemente algunos conceptos claves para la compilación y despliegue de imágenes de contenedores desde Azure DevOps; para esto, primero tener claros los conceptos de contenedores y Docker; luego abordaremos temas como el DockerFile y su importancia para la compilación y por último veremos el uso de herramientas como Azure DevOps para la integración de todo el ciclo de vida del producto, enfocando en el ciclo de compilación y despliegue.
¿Qué es un Contenedor?
Un contenedor es una unidad estándar de software que empaqueta código y todas sus dependencias; esto es equivalente a tener un entorno aislado para cada aplicación, en el cual podemos ejecutar paquetes de software y compilaciones. Estos paquetes y se encapsulan en una imagen de contenedor la cual servirá posteriormente para ejecutar un servicio.
Una imagen de contenedor es un paquete de software ejecutable ligero, independiente y flexible que incluye todo lo necesario para ejecutar una aplicación, por ejemplo:
- Código
- Tiempo de ejecución
- Herramientas del sistema
- Librerías del sistema
- Configuración.
La creación de estos contenedores es un método de virtualización que se centra en el software, diferente a la virtualización clásica de máquinas virtuales, que se centra en hardware; así, los contenedores se pueden ejecutar en distintos ambientes, en hardware físico o en la nube e incluso en distintos sistemas operativos.
Ahora bien, uno de los estándares en la creación de imágenes de contenedores es Docker; ésta es una plataforma para la creación y distribución de imágenes de contenedores.
El software empaquetado en contenedores no hace referencia únicamente a la aplicación o servicio a desplegar; por el contrario, este incluye todo el ambiente necesario para el adecuado funcionamiento de la aplicación, lo cual elimina la necesidad de configurar y afinar cada nuevo ambiente que se necesite para la aplicación o servicio, y que sea el adecuado para el buen funcionamiento del mismo.
Pero, ¿qué certeza tenemos que esta imagen no se pueda alterar? Actualmente, las imágenes de contenedores son inmodificables, por lo cual para hacer un cambio se debe volver a compilar una nueva imagen; ésto asegura que la imagen es la misma en los diferentes ambientes, lo cual es garantía de que si funciona en nuestros ambientes de desarrollo, así mismo debe funcionar en calidad y producción.
Creación de contenedores
Ahora que tenemos claro el concepto, para construir un contenedor, se necesita de un archivo que guarde las instrucciones y diferentes capas que se requieren para crear una imagen de contenedor; a este archivo se le conoce como Dockerfile; este es un archivo de texto plano que contiene las instrucciones de compilación y ejecución de la imagen. En este archivo se deben definir aspectos como:
- Imagen base o primaria.
- Comandos para actualizar el SO o añadir software adicional.
- Artefactos de compilación a incluir.
- Servicios que se expondrán: puertos, configuración de red y tipo de almacenamiento.
- Comandos a ejecutar para iniciar el servicio.
Este es un ejemplo de un archivo Dockerfile para un sitio Web de ASP.NET Core [1]
# Step 1: Specify the parent image for the new image
FROM ubuntu:18.04
# Step 2: Update OS packages and install additional software
RUN apt -y update && apt install -y wget nginx software-properties-common apt-transport-https \
&& wget -q https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb \
&& dpkg -i packages-microsoft-prod.deb \
&& add-apt-repository universe \
&& apt -y update \
&& apt install -y dotnet-sdk-3.0
# Step 3: Configure Nginx environment
CMD service nginx start
# Step 4: Configure Nginx environment
COPY ./default /etc/nginx/sites-available/default
# STEP 5: Configure work directory
WORKDIR /app
# STEP 6: Copy website code to container
COPY ./website/. .
# STEP 7: Configure network requirements
EXPOSE 80:8080
# STEP 8: Define the entry point of the process that runs in the container
ENTRYPOINT ["dotnet", "website.dll"]
En esta entrada no entraremos en el detalle de cada comando, pero la idea detrás de este ejemplo es ilustrar cómo se genera una imagen de un contenedor, añadiendo cada capa necesaria para la implementación de la aplicación.
Compilación y Despliegue de contenedores
Con esto claro, vamos a ilustrar cómo sería un proceso de compilación y despliegue automático de un contenedor; para llevar a cabo estos procesos se usará como herramienta principal Azure DevOps Services y para la orquestación de los contenedores se empleará Azure Kubernetes Services (AKS) junto Azure Container Registry (ACR) para guardar las imágenes compiladas.
Azure DevOps será nuestra herramienta principal para el ciclo de vida de la aplicación (CVA o ALM por sus siglas en inglés); en primer lugar, usaremos Azure Repos para almacenar el código fuente de nuestro contenedor y de nuestra aplicación; luego, en Azure Pipelines se creará un pipeline de compilación y un pipeline de reléase; estos pipelines harán uso de herramientas como Docker y Helm que nos ayudarán a completar todo el proceso.
En el siguiente esquema se ilustran las herramientas a usar durante este flujo de compilación y despliegue.
Para este demo se empleará uno de los ejemplos provistos por Microsoft [2]; este repositorio se puede clonar en Azure DevOps importándolo en las opciones de los repositorios; posteriormente estará todo listo para empezar la configuración.
Antes de empezar con la configuración de los pipelines, mostraremos la arquitectura de la aplicación que hemos tomado como ejemplo [3].
En este diagrama podemos observar cómo estos servicios están todos en un mismo clúster, en este caso un AKS, y se integran con un container registry; estos servicios exponen un API para su integración con un sitio web [4].
Compilación
Antes de comenzar con la compilación, debemos crear el Container Registry en Azure (ACR); se recomienda crearlo en una suscripción de Azure que este previamente conectada con Azure DevOps, para facilitar la conectividad entre los servicios.
En la compilación se hará uso principalmente de las tareas de Docker, las cuales van a generar la imagen del contenedor y posteriormente la publicarán en el ACR; para este caso se usará la tarea DockerCompose; esta tarea permite la compilación de varias imágenes en conjunto por medio de un archivo de nombre docker-compose.yml que se encuentra en la siguiente ruta: “Source/Backend/src/docker-compose.yml”. Para la configuración de esta tarea se deben seleccionar las cadenas de conexión adecuadas, escogiendo el tipo de Container Registry; en este caso es Azure, y posteriormente la suscripción y el registro creado en el paso previo.
Adicionalmente es necesario configurar parámetros como la ruta del archivo “docker-compose.yml”, además se debe de tener en cuenta si es necesario agregar variables de entorno u otros archivos Docker adicionales; en la sección de acciones se deberá configurar la acción a realizar, la primera será de compilación y luego se deben establecer los tags a usar.
Posteriormente se debe agregar otra tarea con una configuración similar, sólo que se la acción que ésta va a realizar es la publicación de las imágenes compiladas en el paso anterior; para lograr esto, se realiza el cambio a Push Service images, en el campo Action.
En seguida ejecutamos el pipeline y si todo fue configurado correctamente, tendremos las imágenes en el ACR para la etapa de despliegue.
Despliegue
Para el pipeline de despliegue usaremos el set de herramientas de Helm; en éste se deben destacar dos tipos de tareas:
La primera tarea realiza la instalación y configuración de la versión a utilizar; aquí es importante destacar que entre la versión 2.x y la versión 3.x existen diferencias a la hora de realizar la instalación y configuración de los servicios contenerizados. Para este ejemplo usamos las versiones 3.x como se muestra en la siguiente imagen :
En segundo lugar, se debe realizar la configuración de las tareas para la instalación de cada servicio; en esta configuración debemos tener en cuenta los parámetros de conexión al AKS, los cuales se asigna por medio de un Kubernetes Service Connection que se debe configurar por medio de la conexión a Azure. Posterior a esto se elige el namespace en donde se realizará la instalación del servicio. Respecto al comando a ejecutar se recomienda que sea vía actualización y se active la opción de instalar si no se encuentra una instalación previa.
Este ejemplo de Microsoft incluye los archivos de chart de Helm; éstos son los que se deben de configurar en el parámetro Chart Path, luego de seleccionar File Path como tipo de chart.
Posteriormente se debe de realizar la configuración del nombre de release, y los parámetros en Set Values; en éste se debe realizar la configuración del nombre de la imagen, el nombre del tag, el repositorio que contiene la imagen, el nombre de la aplicación y el tipo de ingress que se va a utilizar; para esto es importante que el repo ACR y el AKS estén conectados previamente como se indica en la documentación de Microsoft [5]
Por último, en los argumentos podemos incluir los archivos ingress_values.yml e infrastructure_values.yml que se encuentran en el repositorio de código en la ruta “Source/Backend/deploy/k8s”. Estos archivos se deben modificar según la infraestructura asignada a los servicios desplegados.
Hasta aquí hemos visto cómo Azure DevOps se integra perfectamente con los servicios de Azure para compilar y desplegar aplicaciones en la nube. Esta es apenas una de las innumerables funcionalidades de Azure Devops para administrar el Ciclo de Vida de desarrollo.
Si deseas más información acerca de cómo realizar compilación y despliegue desde Azure DevOps no dudes en contactarnos.
[1] Mas informacion sobre como funcionan las imagenes de Docker
[2] Mas informacion sobre el Backend de Smart Hotel 360 de Microsoft
[3] Imagen extraida de GitHub
[4] Mas informacion sobre el Backend de Smart Hotel 360 de Microsoft
[5] Mas informacion sobre la integracion entre AKS y ACR