Quante volte dopo aver rilasciato un’applicazione web, un sito, un blog, un e-commerce o qualcosa di più complesso si scopre un bug, magari segnalato dagli utenti che stanno utilizzando il portale?
In questi casi talvolta si rende necessario un rapido fix e conseguente rilascio di una nuova versione.
Il punto è proprio questo: non sempre è possibile ripubblicare una nuova versione di un’applicazione web immediatamente, infatti la procedura causa sempre un down time che va da qualche secondo fino a 4-5 minuti in cui il servizio non sarà più online prima di essere nuovamente raggiungibile con la nuova versione.
Come Rilasciare nuove versioni senza down time?
La fortuna è che esiste una soluzione: si chiama Deployment Slot (tenendo in considerazione il cloud di Microsoft Azure).
Molto spesso un piccolo down non è problematico, ma ci sono alcune situazioni in cui questo può essere inaccettabile, poiché magari in quel momento ci sono moltissimi utenti che utilizzano il software.
Questo fa sì che sia necessaria l’esecuzione della pubblicazione in orario notturno o nei giorni festivi…non è il massimo, non trovi?
In particolare, invece di deployare l’applicazione direttamente su un servizio app service, andremo ad utilizzare due deployment slot (production e staging).
Quindi eseguiremo il deploy attraverso una pipeline CI/CD da Azure DevOps Pipelines sullo slot staging, successivamente, al termine del deploy, quando lo slot di staging tornerà raggiungibile online, eseguiremo lo scambio dello slot staging con quello production, questo scambierà il contenuto dei due slot in pochissimo tempo rendendo disponibile la nuova versione dell’app senza interruzioni del servizio.

Qui sopra è riportato lo schema generale dell’infrastruttura necessaria e dell’integrazione con CI-CD.
Consideriamo inoltre che tutte le operazioni necessarie (frecce nell’immagine) verranno eseguite esclusivamente attraverso la pipeline senza intervento umano sull’infrastruttura, pertanto la pubblicazione e il relativo scambio di slot sarà un’operazione totalmente automatizzata.
Infrastruttura Cloud Azure
Per creare la corretta infrastruttura utilizzeremo un comodissimo tool di IaC (infrastructure as code) ovvero Terraform, un meraviglioso strumento che permette di codificare un’intera infrastruttura come se fosse del semplice codice da eseguire per creare o ricreare il tutto.
Di seguito le istruzioni per creare l’infrastruttura su Azure con Terraform
Per prima cosa creiamo una cartella con dentro 3 file (main.tf, variables.tf e variables.tfvars) composti così:
main.tf
#region Terraform configuration
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0.0"
}
}
required_version = ">= 0.14.9"
}
provider "azurerm" {
features {}
}
#endregion
#region Resource group
resource "azurerm_resource_group" "rg" {
name = "rg-${var.name}"
location = var.region
tags = {}
}
#endregion
#region Server farms (app service plans)
resource "azurerm_service_plan" "plan" {
name = "plan-${var.name}"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
os_type = var.operatingSystem
sku_name = var.sku
tags = {}
}
#endregion
#region App insights
resource "azurerm_application_insights" "app-insights" {
name = "insights-${var.name}-${var.region}"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
application_type = "web"
}
#endregion
#region Webapps
resource "azurerm_windows_web_app" "webapp" {
name = "app-${var.name}-${var.region}"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
service_plan_id = azurerm_service_plan.plan.id
https_only = true
site_config {
application_stack {
current_stack = try(var.stack, "dotnet")
dotnet_version = try(var.dotnetVersion, "v6.0")
}
}
app_settings = {
APPINSIGHTS_INSTRUMENTATIONKEY = azurerm_application_insights.app-insights.instrumentation_key
}
tags = {}
}
#endregion
#region Deloyment slots
resource "azurerm_windows_web_app_slot" "slot" {
name = "staging"
app_service_id = azurerm_windows_web_app.webapp.id
site_config {
application_stack {
current_stack = try(var.stack, "dotnet")
dotnet_version = try(var.dotnetVersion, "v6.0")
}
}
app_settings = {
APPINSIGHTS_INSTRUMENTATIONKEY = azurerm_application_insights.app-insights.instrumentation_key
}
tags = {}
}
#endregion
Terraformvariables.tfvars
name = "exampleapp" #project name
region = "westeurope" #region
dotnetVersion = "v6.0" dotnet version
stack = "dotnet" #stack
operatingSystem = "Windows" #operating system
sku = "S1" #B1, B2, B3, D1, F1, I1, I2, I3, I1v2, I2v2, I3v2, I4v2, I5v2, I6v2, P1v2, P2v2, P3v2, P0v3, P1v3, P2v3, P3v3, P1mv3, P2mv3, P3mv3, P4mv3, P5mv3, S1, S2, S3, SHARED, EP1, EP2, EP3, WS1, WS2, WS3, Y1
Terraformvariables.tf
variable "name" {
type = string
}
variable "region" {
type = string
}
variable "operatingSystem" {
type = string
}
variable "dotnetVersion" {
type = string
}
variable "stack" {
type = string
}
variable "sku" {
type = string
}
TerraformInstalliamo Terraform e Azure CLI, (se non è stato già fatto in precedenza), poi spostandoci con il terminale nella cartella appena creata, eseguiamo l’inizializzazione con il comando:
terraform init
Prompteseguiamo poi la validazione per testare la bontà del codice sopra con:
terraform validate
Promptl’output dovrebbe restituire un check positivo senza alcun errore, successivamente verifichiamo l’esecuzione del codice con i seguenti passaggi:
az login
Promptquindi effettuiamo il login su Microsoft Azure e poi eseguiamo:
terraform plan -var-file="variables.tfvars"
Promptse il risultato ci soddisfa possiamo eseguire il comando digitando:
terraform apply -var-file="variables.tfvars"
Prompte successivamente inseriamo “yes” quando richiesto, questo avvierà la creazione di quanto richiesto su Microsoft Azure e alla fine l’infrastruttura creata sarà simile a quella riportata nell’immagine sopra.
CI / CD
A questo punto manca solo l‘implementazione della pipeline di rilascio continuo.
Iniziamo creando una pipeline che permetta di compilare la nostra applicazione e rilasciare un pacchetto in un file zip (questa procedura differisce in base allo stack e alle tecnologie utilizzate).
Una volta ottenuto l’artefatto da pubblicare, creiamo una pipeline nella sezione release, utilizzando l’artefatto prodotto e creiamo 3 task, ovvero:
1. Deploy Azure App Service
Quindi selezioniamo la nostra app da Azure selezionando la connection type ARM e poi selezioniamo Deploy to Slot or App Service Environment scegliendo poi staging come slot di pubblicazione.
Questo rilascerà le nuove pubblicazioni sullo slot di staging.
2. Wait
Creiamo un task in cui fermiamo la pipeline per 5 minuti, questo darà il tempo allo slot di staging di tornare up e disponibile.
3. Swap slots
Infine creiamo un ultimo task che esegua lo scambio dello slot di produzione con quello di staging, selezioniamo quindi per la voce Source Slot il nostro slot di staging e selezionando Swap with production.
Ecco il codice YAML dei 3 task da inserire nella pipeline di rilascio:
steps:
- task: AzureRmWebAppDeployment@4
displayName: 'Deploy Azure App Service'
inputs:
azureSubscription: '$(Parameters.ConnectedServiceName)'
appType: '$(Parameters.WebAppKind)'
WebAppName: '$(Parameters.WebAppName)'
deployToSlotOrASE: true
ResourceGroupName: 'rg-exampleapp-dev'
SlotName: staging
- task: deadlydog.WaitBuildAndReleaseTask.Wait.Wait@1
displayName: 'Wait for 5 minutes'
inputs:
Value: 5
Unit: minutes
- task: AzureAppServiceManage@0
displayName: 'Swap Slots: app-exampleapp-dev-westeurope'
inputs:
azureSubscription: 'Crionet Azure (4d2e08a3-17bf-4a08-9cbe-4dbe2b2dc570)'
WebAppName: 'app-exampleapp-dev-westeurope'
ResourceGroupName: 'rg-exampleapp-dev'
SourceSlot: staging
YAMLQuesto è tutto!
Da adesso in poi ogni pubblicazione avverrà come di consueto ma senza alcun disservizio, mantenendo sempre il servizio disponibile e senza interruzioni.