Herramientas de desarrollo: Azure Pipelines (CD) y ARM

Tiempo de lectura: 8 minutos
La imagen muestra el logo de Azure Pipelines

Todo lo que empieza tiene un final, y hoy es el final de la serie sobre «Integración Continua y Despliegue Continuo» (CI/CD). Hoy vamos a hablar sobre cómo hacer una integración y despliegue completos sobe Azure Web Apps de un proyecto ASP NET Core con Azure Pipelines.

Para poder hacerlo, lo primero que necesitamos es aprovisionar los recursos en Azure, y hay varias maneras de hacerlo. Hablo de ellas en Variable Not Found:

En este caso, vamos a utilizar el sistema de aprovisionamiento desde AzureRM para crear el plan, la webapp y una segunda ranura que será donde hagamos el deploy de nuestro proyecto para cambiarlo después a producción. Utilizando este sistema vamos a conseguir que nuestro proyecto esté siempre online incluso mientras se hace la publicación, ya que vamos a publicar sobre una segunda ranura y solo después de que nuestro proyecto este publicado y online, haremos el cambio entre ranuras (internamente hace un cambio de DNS, por lo que el cambio es instantáneo). Una vez dicho esto, ¡vamos a meternos en faena!

Creando la integración continua

Dentro de esta serie, publique como hacer la integración de nuestro proyectos en Azure Pieplines, así que nos vamos a saltar esa parte. El fichero .yml podría ser algo como esto:

# Configuramos el pool con la imagen
pool:
  vmImage: 'ubuntu-16.04'

# Variables de trabajo
variables:
  buildConfiguration : 'Release'
  project : '**/*.csproj'
 
# Pasos
steps:
# Restore Nuget
- task: DotNetCoreCLI@2
  displayName: Restore
  inputs:
    command: restore
    projects: '$(project)'
# Compilamos el proyecto
- task: DotNetCoreCLI@2
  displayName: Build
  inputs:
    projects: '$(project)'
    arguments: '--configuration $(buildConfiguration)'

# Aquí van las pruebas unitarias
# ------

# Publicamos la solución indicándole que queremos zipearla
- task: DotNetCoreCLI@2
  displayName: Publish
  inputs:
    command: publish
    publishWebProjects: True
    arguments: '--configuration $(buildConfiguration) --output $(build.artifactstagingdirectory)'
    zipAfterPublish: True

# Subimos los artefactos
- task: PublishBuildArtifacts@1
  displayName: 'Publish Artifact'
  inputs:
    PathtoPublish: '$(build.artifactstagingdirectory)'

Creando el despliegue continuo en Azure Pipelines

Con esto, ya tenemos nuestra integración funcionando y subiendo los artefactos, ahora solo falta el despliegue. La manera más sencilla quizás sea desde la propia ventana de resultados de la integración, pulsando sobre el botón Release:

La imagen muestra el botón "Release" dentro de la ventana de resultados de la integración de Azure Pipelines

Eso nos va a lanzar a una nueva página donde vamos a crear el pipeline. Por defecto nos va a dar muchas opciones, pero vamos a hacerlo desde 0, así que elegimos «empty job»:

La imagen indica "empty job" en el asistente de Azure Pipelines

Esto va a crear un job vacío, en el cual vamos a añadir todos los pasos que necesitamos. En nuestro caso son 3:

  1. Aprovisionar los recursos.
  2. Desplegar nuestro proyecto a una ranura de staging
  3. Cambiar las ranuras de staging y producción.

Para eso, vamos a pulsar sobre el Stage 1, para añadir tareas al job:

La imagen muestra el enlace que hay que pulsar para abrir la configuración

Esto nos lleva a las tareas que tiene el job, y ahí vamos a pulsar en «+» para añadir las nuestras:

La imagen señala el botón "+" para añadir tareas al job

Aquí vamos a tener que añadir 3 tareas, una por cada una de las 3 acciones que queremos hacer. La primera, será una tarea de aprovisionamiento, así que vamos a la pestaña «Deploy» y seleccionamos «Azure Resurce Group Deployment»:

La imagen muestra el recurso Azure Resurce Group Deployment

El siguiente que vamos a introducir, es «Azure App Service Deploy»:

La imagen muestra el recurso Azure App Service Deploy

Y por último, vamos a añadir «Azure App Service Manage»:

La imagen muestra el recurso Azure App Service Manage

Una vez añadidos los 3 recursos, nos quedará algo como esto:

La imagen muestra el resultado de añadir las 3 tareas al job de Azure Pipelines

Ahora vamos a configurarlos, para ello, vamos al primero y al pulsar sobre él, nos aparecen sus opciones:

La imagen enseña el asistente de aprovisionamiento de Azure Pipelines

Simplemente, tenemos que seleccionar nuestra suscripción y grupo de recursos donde queremos que se añadan, así como la zona para el despliegue. Después le indicamos que el template va a estar en una url y le pasamos las direcciones «raw» de los ficheros. Por ejemplo, en GitHub existe un botón para ir al fichero raw y luego basta con copiar la url:

La imagen señala el botón raw de github

Como se puede intuir, estos ficheros los genera el proyecto de «Grupo de Recursos de Azure» que hemos creado en Visual Studio, y los parámetros que hayamos puesto al validar nuestro script de aprovisionamiento:

La imagen señala los valores indicados en Visual Studio para el aprovisionamiento

Con esto, ya tenemos listo el aprovisionamiento de recursos. Ahora, vamos a configurar el despliegue de nuestro proyecto. Azure Pipelines nos da un asistente como este:

La imagen señala los campos del asistente de publicación

Aquí tenemos que tener especial cuidado, ya que vamos a «ciegas». Me explico, como los recursos aún no están creados en Azure, vamos a tener que introducir los datos a mano porque los desplegables van a estar vacíos. Para eso, vamos a escribir los datos que hemos puesto al validar nuestro script de despliegue. También hay que tener cuidado con cuál es el nombre real que aplica nuestro despliegue, por ejemplo, la plantilla que hemos seleccionado para el aprovisionamiento añade «Portal» al nombre que hallamos elegido:

"webAppPortalName": "[concat(parameters('baseResourceName'), 'Portal')]"

Lo mismo pasa con la ranura (Slot) al que vamos a desplegar, realmente no existe mientras estamos configurando el despliegue, así que la escribimos a mano.

Este «problema» se puede solucionar si utilizamos las variables del pipeline, de manera que siempre sean los mismos valores. Si hacemos el aprovisionamiento de recursos antes de configurar esta parte también se evitaría porque si los recursos ya están creados, aparecerán todos correctamente en los desplegables.

Una vez que lo hemos terminado de configurar, lo último que nos falta es cambiar la ranura de «Staging» por la de «Produccion», de manera que se haga un despliegue completo, y nuestro proyecto este accesible en producción. Para eso, configuramos el último apartado:

La imagen señala los campos del asistente de swap de ranuras

Igual que en el caso anterior, vamos a «ciegas» porque los recursos aun no existen. Sabiendo esto, escribimos manualmente el nombre del servicio que estamos operando, el grupo de recursos al que pertenece, y la ranura que queremos poner en producción. Puede que no queramos poner en producción la ranura y solo cambiarla por otra, esto lo podríamos conseguir si desmarcamos la casilla «Swap whit Production», lo cual nos hace aparecer un segundo campo para rellenar con la ranura por la que queremos cambiar.

Una vez que hemos configurado las 3 tareas, pulsamos sobre el botón «Save» para guardar nuestro job:

La imagen muestra el botón de guardar el job

Ya tenemos nuestro despliegue listo. Ahora cada vez que hagamos un cambio en el repositorio, automáticamente se publicará. Antes de hacer cambios, podemos comprobar que nuestro pipeline funciona pulsando sobre el botón «Create a release»:

El la imagen señala el botón para lanzar un ciclo de release

Esto lanzará un ciclo de despliegue como podemos ver en la pestaña «Releases»:

La imagen muestra la nueva release en marcha

Una vez termine, si vamos a https://{nombre de nuestro servicio}.azurewebsites.net debería estar disponible nuestro proyecto. Si añadimos algún cambio y lo subimos a github, de que acabe la integración y el despliegue deberíamos poder ver en la url el cambio que hemos hecho. Por ejemplo:

La imagen muestra el resultado de hacer un cambio en el repositorio

Como es habitual en este tipo de entradas, el código esta disponible en GitHub para poder consultarlo.

Aviso a navegantes

No es oro todo lo que reluce, poner webs en producción de manera automática puede ser peligroso ya que puede haber errores de interfaz gráfica (o cualquier otro tipo) que no se hayan testeado mediante pruebas automáticas. Una mejor practica puede ser hacer despliegue sobre una ranura de «develop» donde poder comprobar la funcionalidad completa de la web, y una vez que estamos seguros de que todo esta bien, hacer el swap desde la ranura que hemos testeado de manera que los posibles errores estén comprobados ya.

Si por el contrario, lo único que estamos publicando es una API RESTful que hemos desarrollado con buenas prácticas y que no tiene estado (no hay datos que son necesarios mantener en memoria entre peticiones), no debería ser un problema hacer despliegue continuo automáticamente, ya que las peticiones de aquello que consuma nuestro servicio se lanzaran contra la versión establecida, y solo cuando cambiemos los clientes a la nueva versión se aplicará el cambio.

Conclusión sobre CI/CD

Aunque quizás el despliegue continuo a producción es algo idílico y difícil de alcanzar por la cantidad de posibles problemas y errores que podemos introducir, este solo es el paso final de una serie de buenas prácticas y metodologías de trabajo. Quedarse en cualquiera de los puntos anteriores al despliegue en producción sigue siendo algo muy válido, ya que estamos haciendo que no exista una dependencia tan fuerte entre código y programador, lo cual siempre facilita el trabajo y la mantenibilidad de los proyectos. Esto añadido a una buena política de pruebas unitarias,
evita los problemas asociados a realizar proyectos en equipos de trabajo, donde cada persona no tiene por qué conocer el alcance del trabajo del resto. Con la variedad de proveedores que ofrecen estos servicios, no buscar uno que se adapte a nuestras necesidades e implementarlo puede ser una decisión que a la larga salga cara.

Deja un comentario