La finalidad de este laboratorio sobre DevOps es la practica sobre los procesos de Integración Contínua (CI) y Despliegue Continuo (CD) utilizando las funcionalidades ofrecidas por la herramienta Azure DevOps.
Se utilizará como base para las prácticas el código fuente de la aplicación de ejemplo creada por Ben Coleman y disponible en el este repositorio GutHub.
Esta aplicación web de ejemplo permite monitorizar en tiempo real los recursos de CPU y memoria utilizados por la aplicación, así como forzar el uso de recursos y lanzar excepciones que serán recogidas por aplicaciones de monitorización, como App Insights.
-
Una suscripción de Azure, de pago por uso, MSDN o Trial.
-
Para completar este laboratorio, asegúrate de que tu cuenta tenga los siguientes roles y permisos:
- El rol Owner para la suscripción que vayas a utilizar.
- Ser un usuario Miembro del directorio Azure AD que vayas a utilizar. (Los usuarios invitados no tendrán los permisos necesarios).
- No tener restringida la creación de Aplicaciones Azure AD. Es posible que el administrador del Directorio Azure AD lo haya limitado.
Nota Si no cumples estos requisitos, es posible que tengas que pedir a otro usuario miembro con derechos de propietario de suscripción que inicie sesión en el portal y ejecute con antelación el paso de creacion de la aplicación Azure AD.
-
-
Máquina local o una máquina virtual (Windows o Linux) configurada con:
- Un navegador, preferiblemente Chrome o Microsoft Edge.
- dotnet core sdk instalado, para las pruebas de compilación y ejecucion locales.
- Azure CLI para la ejecución de algunos comandos en Azure.
- Visual Studio Code o cualquier otro IDE o editor.
Duracion: 10 minutes
Como primer ejercicio probaremos a ejecutar la aplicación de forma local, para comprobar su correcto funcionamiento y familiarizarnos con la la funcionalidad que nos ofrece.
Para ello nos descargaremos el codigo fuente desde el repositorio de referencia de este laboratorio, ejecutando el siguiente comando.
git clone https://github.com/fnietoga/netcoredevops.git
Eliminaremos las referencias al repositorio Git desde el que hemos descargado el contenido.
Remove-Item -Recurse -Force ./netcoredevops/.git
Nota En entornos linux el comando a ejecutar sería
rm -rf netcoredevops/.git
Realizaremos una compilacion de la aplicacion web, y la ejecutaremos de forma local, ejecutando
cd netcoredevops/src
dotnet restore
dotnet run
La aplicación web escuchará en los puertos 5000 (http) y 5001 (https), habituales de Kestrel, pero esto puede cambiarse configurando la variable de entorno ASPNETCORE_URLS
o con el parámetro --urls
(ver docs).
Probaremos la aplicacion web en el navegador, accediendo a alguna de las siguientes URLs:
http://localhost:5000
https://localhost:5001
Probaremos la opciones de la aplicación:
- 'Info' - Mostrará información del sistema y del entorno de ejecución, incluyendo las variables de entorno.
- 'Tools' - Algunas herramientas útiles en demos, como la carga forzada de CPU, y las páginas de error/excepción para su uso con App Insights u otra herramienta de monitorización.
- 'Monitoring' - Muestra la carga de la CPU y memoria en tiempo real en formato gráfico, obtenidos utilizando una API REST (/api/monitoringdata)y mostrados utilizando chart.js. Recomendable abrirlo en una pestaña adicional del navegador para poder observar en tiempo real la carga generada utilizando las herramientas anteriores.
Duracion: 20 minutes
El propósito de este ejercicio es familiarizarnos con las principales opciones de Azure DevOps y preparar el entorno para qie se realice un proceso de compilacion de forma automática cada vez que se sube un cambio en el codigo fuente por algun desarrollador.
Para comenzar abre una nueva pestaña del navegador para visitar Azure DevOps, y a continuación inicia sesión en tu cuenta.
Si es la primera vez que inicias sesion con esa cuenta, Azure DevOps te guiará a través de un asistente:
- Confirma tu informacion de contacto y selecciona "Siguiente"
- Selecciona "Continuar" para crear una nueva cuenta en Azure DevOps
- Introduce el nombre que deseas para tu nueva Organización y selecciona "Continuar"
También puedes crear una nueva organizacion utilizando el enlace "Nueva Organizacion", en la parte superior izquierda, o trabajar sobre una de tus organizaciones ya existentes.
Si hemos creado una nueva organización nos mostrará automáticamente las opciones para la creación del primero proyecto.
Si hemos seleccionado una organización ya existente, tendremos que hacer uso del botón "Nuevo Proyecto" que se nos muestra en la parte superior derecha.
Especificaremos el nombre, descripción (opcional) y seleccionaremos la metodología que utilizaremos en nuestro nuevo proyecto para la gestión de tareas. Podemos dejar los valores por defecto para el ámbito de este laboratorio.
En nuestro nuevo proyecto encontraremos varios opciones, en el menú situado en el lado izquierdo, y una de ellas es la relacionada con los repositorios de código (Repos).
Comprobaremos que se ha creado de forma automática un repositorio de código con el mismo nombre del proyecto, y desplegando en la parte superior tendremos la posibilidad de crear otros repositorios. Para este laboratorio haremos uso del creado por defecto.
La primera acción que tenemos que realizar sobre nuestro proyecto de DevOps en subir el código fuente de la aplicación que actualmente tenemos en nuestra maquina de trabajo. Para ello realizaremos la siguientes acciones:
-
Nos posicionaremos en la carpeta raiz de nuestro proyecto (netcoredevops), ejecutando
cd ..
-
Configuraremos nuestro entorno local de git, especificando los datos del usuario que se incluiran en los cambios realizados.
git config --global user.email "you@example.com" git config --global user.name "Your Name" git config --global credential.helper cache
-
Inicializaremos un repositorio git local
git init
-
Añadiremos a nuestro repositorio local todo el contenido de nuestra carpeta actual y subcarpetas
git add . git commit -m "Initial Commit"
-
Configuraremos el repositorio remoto de git, apuntando al repositorio en nuestro proyecto de Azure DevOps
-
Antes de continuar, y para simplificar la autenticación con el repositorio Git, pulsaremos sobre el botón
Generate Git Credential
, y copiaremos el Personal Access Token generado, almacenándolo para su uso posterior. -
Y ejecutaremos el siguiente comando para configurarlo como repositorio remoto (sustituyendo la url del repositorio por la copiada en el punto anterior).
git branch -M master git remote add origin https://[UserName]@dev.azure.com/[OrganizationName]/netcore_devops/_git/netcore_devops
-
-
Subiremos todos los cambios al repositorio remoto
git push -u origin master
Nota La primera vez que subamos cambios al repositorio remoto nos solicitará credenciales, y podremos utilizar como password el Personal Access Token generado anteriormente.
-
Por ultimo, comprobaremos a través del navegador que los ficheros se han subido correctamente a nuestro repositorio en DevOps.
Procederemos a crear una definicion de pipeline que nos permitirá compilar nuestra aplicacion y publicar un artefacto con el contenido de la aplicación listo para ser desplegado. Para ello seguiremos los siguientes pasos
-
En nuestro proyecto de Azure DevOps accederemos al apartado de Pipelines, y pulsaremos sobre el botón "Crear Pipeline"
-
Seleccionaremos utilizar el editor clásico, en el enlace de la parte inferior.
-
En la configuración del repositorio de codigo a utilizar, dejaremos la configuracion por defecto, y pulsaremos en el botón "Continuar"
-
Buscaremos el template con nombre "ASP.NET Core", ya que es el que mas se asemeja a nuestra aplicacion web .Net Core.
-
La pipeline creada sería operativa, pero haremos algunos pequeños ajustes:
-
Pulsaremos sobre el boton "Save & queue" en la parte superior, que grabará y pondrá en ejecución una instancia de nuestra definición de pipeline.
-
Pulsando sobre el job en ejecucion de la pipeline podremos revisar el avance y visualizar los logs generados por cada una de las tareas.
-
Esperaremos a que finalice la ejecucion de la pipeline y comprobaremos que se ha generado un artefacto, que podremos descargarnos y visualizar su contenido.
En esta tarea crearemos la definición de nuestra pipeline haciendo uso de un fichero en formato YAML, que podremos guardar y versionar junto con el resto del codigo fuente de nuestra aplicación, en el repositorio Git.
Cada una de las tareas y acciones que se pueden realizar a través del editor clásico tienen una codificación en YAML. En los siguientes enlaces se encuentra toda la documentación relativa a:
- Referencia del esquema YAML para Azure pipelines.
- Referencia en YAML de las tareas de compilacion y despliegue incluidas por defecto en Azure DevOps.
En esta parte del ejercicio crearemos una pipeline a partir de un fichero de definicion en YAML.
Para ello simplemente tenemos que añadir a nuestro repositorio de código un fichero denominado azure-pipelines.yml
, y se nos habilitará un asistente que nos permitirá crear automáticamente una pipeline basada en ese archivo de definición.
La principal ventaja de crear las pipelines de compilación y despliegue a partir de un fichero de definición es que estas quedan versionadas junto con el resto del código de nuestra aplicación, y de esta forma permiten que estén alineadas con la versión de nuestra aplicación, ya que según vaya evolucionando es posible que requiera de cambios en la pipeline.
En nuestro editor de código local, crearemos el fichero azure-pipelines.yml
, y como contenido incluiremos lo siguiente, que se corresponde con las mismas tareas que la pipeline creada en el paso anterior utilizando el asistente.
name: $(Date:yyyyMMdd)$(Rev:.r)
# Build and Test
stages:
- stage: BuildPublish
displayName: Build and Publish Artifact
variables:
BuildConfiguration: Release
RestoreBuildProjects: "**/*.csproj"
TestProjects: "**/*[Tt]ests/*.csproj"
jobs:
- job: Build
displayName: Build and Publish artifact
pool:
vmImage: 'ubuntu-16.04'
steps:
- task: DotNetCoreCLI@2
displayName: Restore
inputs:
command: restore
projects: '$(RestoreBuildProjects)'
- task: DotNetCoreCLI@2
displayName: Build
inputs:
projects: '$(RestoreBuildProjects)'
arguments: '--configuration $(BuildConfiguration)'
- task: DotNetCoreCLI@2
displayName: Test
inputs:
command: test
projects: '$(TestProjects)'
arguments: '--configuration $(BuildConfiguration)'
- task: DotNetCoreCLI@2
displayName: Publish
inputs:
command: publish
publishWebProjects: True
arguments: '--configuration $(BuildConfiguration) --output $(build.artifactstagingdirectory)'
zipAfterPublish: True
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact'
inputs:
PathtoPublish: '$(build.artifactstagingdirectory)'
ArtifactName: demoapp
condition: succeededOrFailed()
Grabaremos nuestro fichero de definición y haremos commit y push al repositorio de código remoto.
Desde la interfaz web, al visualizar nuestro repositorio, visualizaremos el nuevo fichero subido, y tendremos habilitado el botón "Set up build",
Al pulsarlo se nos creará automáticamente una nueva pipeline, lista para ser ejecutada
Podremos visualizar el proceso de ejecución del proceso de compilación, con los mismos pasos y resultado que en el caso anterior.
El proceso de integración continua consiste en que las tareas de compilación y pruebas unitarias se ejecuten tras cada nuevo commit de cambios sobre la rama de código, para permitir comprobar que los nuevos cambios realizados se integran con el resto del código existente sin provocar errores, y que se mantiene la retro-compatibilidad con las versiones anteriores, al ejecutarse las tests sin errores.
Para activarla en nuestra pipeline unicamente tendremos que incluir un pequeño fragmento en su archivo de definición, para habilitar un disparador
que esté atento ante cualquier cambio en el repositorio de código, en las ramas que indiquemos, pudiendo utilizar caracteres comodín.
El fragmento a incluir es el siguiente:
trigger:
branches:
include:
- master
- develop/*
paths:
exclude:
- tests/*
- README.md
Y nuestro archivo de definición debería quedar de la sigiente forma:
Tras guardar y subir los cambios al repositorio, podremos comprobar que la pipeline se lanza de forma automática tras el cambio realizado.
Duracion: 30 minutes
El propósito de este ejercicio es ampliar la definición del proceso de compilación para que de forma automática se despliegue en un entorno de pruebas, de forma automática, la versión de la aplicación que se genera tras el proceso de compilación haciendo uso de infraestructura cloud para su ejecución.
El primer paso será crear un fichero de definición para la infraestructura necesaria. Para poder ejecutar nuestra aplicación de ejemplo haremos uso del servicio PaaS Azure App Service
, que permite la ejecución de aplicaciones de distintos tipos y en varios lenguajes de programación sin requerir desplegar ninguna infraestructura de servidores, es la arquitectura conocida como serverless
.
Utilizaremos un fichero de definición ARM Template de Azure, y en el mismo incluiremos el despliegue de los siguientes servicios:
- Azure App Service. Alojará y ejecutará nuestra aplicación web.
- Application Insigths. Lo utilizaremos mas adelante para monitorizar la actividad de nuestra aplicacion web.
En nuestro proyecto local crearemos una carpeta iac
para alojar los ficheros necesarios para el despliegue de infraestructura, y en su interior crearemos un fichero con el nombre azuredeploy.json
. El contenido del fichero para desplegar la infraestructura mencionada es el siguiente
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
},
"variables": {
"hostingPlanName": "netcoredevopsplan",
"appName": "netcoredevopsapp",
"insightsName": "netcoredevopsinsights",
"location": "[resourceGroup().location]"
},
"resources": [
{
"apiVersion": "2020-06-01",
"name": "[variables('appName')]",
"type": "Microsoft.Web/sites",
"location": "[variables('location')]",
"tags": {},
"dependsOn": [
"[concat('microsoft.insights/components/',variables('insightsName'))]",
"[concat('Microsoft.Web/serverfarms/', variables('hostingPlanName'))]"
],
"properties": {
"name": "[variables('appName')]",
"siteConfig": {
"appSettings": [
],
"metadata": [
{
"name": "CURRENT_STACK",
"value": "dotnetcore"
}
],
"phpVersion": "OFF",
"alwaysOn": false
},
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
"clientAffinityEnabled": true
}
},
{
"apiVersion": "2020-06-01",
"name": "[variables('hostingPlanName')]",
"type": "Microsoft.Web/serverfarms",
"location": "[variables('location')]",
"properties": {
"targetWorkerCount": "1"
},
"sku": {
"Tier": "Shared",
"Name": "D1"
}
},
{
"apiVersion": "2020-02-02-preview",
"name": "[variables('insightsName')]",
"type": "microsoft.insights/components",
"location": "[variables('location')]",
"tags": {},
"properties": {
"Application_Type": "web",
"Request_Source": "IbizaWebAppExtensionCreate"
}
}
]
}
El template ha sido creado sin parámetros de entrada, pero si se precisara que el usuario pudiera especificar algun valor de configuración sobre los recursos desplegados, como el nombre de los mismos, simplmemente tendriamos que incorporarlos en el apartado parameters
y posteriormente, durante el despliegue, se podrá especificar un valor. Mas información sobre parámetros
En esta tarea incluiremos el despliegue de la infraestructura Azure y la publicación del contenido de nuestro sitio web en la misma pipeline de despliegue que creamos anteriormente.
Necesitamos crear una conexión en nuestro proyecto de Azure DevOps que nos permita interactuar con nuestra suscripción de Azure.
Para simplificar el proceso haremos uso de un asistente que incluye el editor clásico que nos permite seleccionar una de nuestras suscripciones y crear de forma automática todos los recursos necesarios.
Editaremos la pipeline que creamos en primera instancia, utilizando el editor clásico, y que quedó guardada con el nombre netcore_devops-ASP.NET Core-CI
(o similar).
Añadiremos una nueva tarea, buscando la tarea con nombre ARM template deployment
y la añadiremos a la pipeline.
En la nueva tarea, en el parámetro Azure Resource Manager connection
, seleccionaremos en la lista la suscripción que utilizaremos para realizar el despliegue, y pulsaremos sobre el botón "Authorize".
El asistente nos pedirá las credenciales que se utilizarán para el proceso de creación y configuración de los elementos Azure y Azure AD necesarios. Tras finalizar el asistente de autorización, podremos descartar los cambios sobre la pipeline.
Este proceso generará de forma automática los siguientes elementos:
- Azure Service Principal. En el Azure Active Directory asociado a la suscripción seleccionada se creará un Service Principal al que se le asignarán el rol Contributor sobre la suscripción para permitir el despliegue de recursos.
- Azure DevOps Service Connection. En el proyecto actual de Azure DevOps se creará una conexión y quedará configurada con los valores de la suscripción, y utilizando el Service Principal creado para la autenticación.
Haremos unos pequeños ajustes a la conexión creada, para utilizar un nombre mas facil de recordar, y para permitir su uso por las distintas pipelines que creemos sin necesidad de autorizar expresamente.
En el apartado Project Settings
de nuestro proyecto, accederemos al elemento Service connections
y editaremos la conexión.
Le cambiaremos el nombre por azurerm
y marcaremos el check Grant access permission to all pipelines
, guardando los cambios.
Editaremos el fichero azure-pipelines.yml
para incluir al final un nuevo elemento stage
con un elemento deployment
que incluye varias tareas adicionales.
# Deploy and Publish
- stage: DeployPublish
displayName: Deploy Infra and Publish app
jobs:
- deployment: DeployInfraAndPublishApp
displayName: 'Deploy infra to Azure and Publish App'
workspace:
clean: all
environment: demoapp-dev
variables:
resourceGroupName: netcoredevops
webAppName: netcoredevopsapp
location: 'West Europe'
connectionName: 'azurerm'
strategy:
runOnce:
deploy:
steps:
- checkout: self
- task: AzureResourceManagerTemplateDeployment@3
displayName: 'Deploy Azure infra using ARM Template deployment'
inputs:
azureResourceManagerConnection: '${{variables.connectionName}}'
action: 'Create Or Update Resource Group'
resourceGroupName: $(resourceGroupName)
location: $(location)
templateLocation: 'Linked artifact'
csmFile: iac/azuredeploy.json
deploymentMode: 'Incremental'
- task: AzureWebApp@1
displayName: 'Azure Web App Deploy: $(webAppName)'
inputs:
azureSubscription: '${{variables.connectionName}}'
appType: webApp
appName: $(webAppName)
package: $(Pipeline.Workspace)/**/*.zip
deploymentMethod: auto
ATENCION a la tabulación del nuevo contenido, el nuevo elemento
stage
debe de quedar alineado verticalmente con el existente, y el resto de elementos con la identación mostrada.
Prepararemos un commit & push de nuestros cambios, para subirlos al repositorio remoto, y observaremos como nuestra pipeline comienza a ejecutarse de forma automática. Esperaremos a que finalice su ejecución.
Al finalizar observaremos en el Portal de Azure que nuestros recursos han quedado perfectamente desplegados.
Y si accedemos a la URL del App Service nuestra aplicación se mostrará correctamente.
Con estos cambios, cada vez que se realice un cambio por parte del equipo de desarrollo sobre el codigo de la aplicación, se compilará y desplegará de forma automática la nueva version sobre el entorno de desarrollo, lo que se denomina "Despliegue Continuo"
Al haber hecho uso del elemento deployment
en la definición de la pipeline, se habrá creado de forma automática un elemento en la seccion Environments
de Azure DevOps, donde podremos visualizar el historia de despliegues sobre nuestra infraestructura de desarrollo, contando con la trazabilidad de las distintas versiones desplegadas.
En esta última tarea habilitaremos la integración entre el App Service y el App Insights, para que todas las operaciones, indicadores y excepciones que se produzcan durante la ejecución de la aplicación sean recogidas y almacenados por el servicio de Monitorización de Azure.
Para ello el servicio App Service necesita conocer el Instrumentation Key asociado al servicio App Insights, y con ello de forma automática se trazarán todos los valores obtenidos desde los contadores de rendimiento estandard del servidos y del propio servicio web, así como la informacion de las excepciones que se produzcan. También permite la gestion de eventos y métricas personalizados, si la aplicación está codificada para ello.
El valor del Instrumentation Key necesario se puede configurar en el servicio App Service a través de dos App Settings, que pueden especificarse en tiempo de despliegue de la insfraestructura. Los valores necesarios de configurar se pueden extraer del servicio Insigths desplegado en el mismo ARM template. Incluiremos el siguiente fragmento en el fichero azuredeploy.json
.
El fragmento a añadir es el siguiente:
{
"name": "APPINSIGHTS_INSTRUMENTATIONKEY",
"value": "[reference(concat('microsoft.insights/components/',variables('insightsName')), '2020-02-02-preview').InstrumentationKey]"
},
{
"name": "APPLICATIONINSIGHTS_CONNECTION_STRING",
"value": "[reference(concat('microsoft.insights/components/',variables('insightsName')), '2020-02-02-preview').ConnectionString]"
},
{
"name": "ApplicationInsightsAgent_EXTENSION_VERSION",
"value": "~2"
}
Y en la siguiente imagen se muestra donde debe ser insertado.
Tras guardar y subir los cambios al repositorio, la pipeline de despliegue se lanzará de forma automática.
Esperaremos a que termine y comprobaremos en el Portal de Azure que se han aplicado los cambios correctamente, visualizando las propiedades del App Service, en el apartado Application Insights
, podremos visualizar asociado el recurso con nombre netcoredevopsinsights
, como se muestra en la imagen.
Accederemos a la URL de la aplicación web y generaremos eventos haciendo uso de TODAS las herramientas que facilita la aplicación de ejemplo utilizada.
A través del portal de Azure visualizaremos los valores mostrados en la App Insigths desplegada con nombre netcoredevopsinsights
en el cuadro de mando por defecto.
Algunas opciones de interes del servicio son:
- Live Metrics: Permite monitorizar en tiempo real las métricas y contadores de rendimiento de la aplicación.
- Failures: Permite analizar los errores y excepciones ocurridos en la aplicación, según su tipología y la página que los ha originado.
- Search: Permite buscar y explorar elementos de telemetría individuales, como visitas de páginas, excepciones o solicitudes en la web.
- Metrics: Permite la monitorización basada en métricas pre-agregadas y obtenidas desde logs.
- Alerts: Permite especificar alertas basadas en umbrales sobre valores obtenidos en las métricas y búsquedas predefinidas sobre los logs
Es recomendable que elimines los recursos Azure generados para evitar generar costes innecesarios.
Para ello será suficiente con borrar el grupo de recursos con nombre netcoredevops
.
Con la única finalidad de conocer tu opinión sobre este laboratorio y poder generar mas contenidos que se adapten a vuestras necesidades de formación e inquietudes tecnológicas, te pedimos que rellenes esta breve encuesta anónima.