Resumen de contenidos
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:
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»:
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:
- Aprovisionar los recursos.
- Desplegar nuestro proyecto a una ranura de staging
- Cambiar las ranuras de staging y producción.
Para eso, vamos a pulsar sobre el Stage 1, para añadir tareas al job:
Esto nos lleva a las tareas que tiene el job, y ahí vamos a pulsar en «+» para añadir las nuestras:
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»:
El siguiente que vamos a introducir, es «Azure App Service Deploy»:
Y por último, vamos a añadir «Azure App Service Manage»:
Una vez añadidos los 3 recursos, nos quedará algo como esto:
Ahora vamos a configurarlos, para ello, vamos al primero y al pulsar sobre él, nos aparecen sus opciones:
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:
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:
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:
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:
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:
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»:
Esto lanzará un ciclo de despliegue como podemos ver en la pestaña «Releases»:
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:
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.