In questa guida verrà mostrata la procedura di pubblicazione di un’applicazione Web su Azure utilizzando il servizio AKS (Azure Kubernetes Service).
Verrà fatta inoltre la distinzione sulla tecnica di pubblicazione nel caso in cui l’applicazione venga sviluppata utilizzando .NET Framework, oppure dotNET Core.
Le applicazioni Web verranno containerizzate utilizzando Docker.
Premessa
A seconda della tecnologia utilizzata per lo sviluppo dell’applicazione Web, .NET Framework o dotNET Core, è necessario scegliere un sistema operativo compatibile da utilizzare all’interno del contenitore Docker: Windows Server Core per applicazioni .NET Framework, oppure Windows Nano Server o Linux per applicazioni dotNET Core.
Di seguito uno schema con le differenze per una scelta adeguata allo scopo.
Caso 1: Applicazione sviluppata in .NET Framework
Come anticipato all’interno della premessa, nel caso in cui vogliamo pubblicare un’applicazione sviluppata utilizzando .NET Framework, è necessario creare un’immagine Docker con sistema operativo Windows Server Core.
Vediamo nello specifico i vari passaggi:
- Scaricare ed installare Docker Desktop for Windows sulla macchina locale (link);
- Una volta installato, impostare il contenitore sulla piattaforma Windows dall’icona di Docker presente all’interno della barra delle notifiche;
- Aprire il progetto della WebApp che si intende pubblicare con Visual Studio ed effettuare la compilazione della soluzione;
- Aggiungere il supporto per Docker Compose alla WebApp appena compilata: tasto destro del mouse sul progetto della WebApp, Aggiungi, Supporto per l’agente di orchestrazione del container e selezionare Docker Compose;
- All’interno del Dockerfile modificare la riga:
FROM mcr.microsoft.com/dotnet/framework/aspnet:4.7.2-windowsservercore-ltsc2019
con la seguente riga:
FROM microsoft/aspnet:4.7.1-windowsservercore-10.0.14393.1884
Questa operazione è necessaria perché gli ACI (Azure Container Instance) non supportano una versione di Windows superiore alla 10.0.14393 nel caso di applicazione .NET Framework;
- Aggiungere quindi un nuovo profilo di pubblicazione: tasto destro del mouse sul progetto della WebApp, Pubblica, selezionare come target Cartella, impostare il percorso di output ed effettuare la pubblicazione della soluzione;
- Editare il file docker-compose.yml modificando il valore del campo image con uno a proprio piacimento, per esempio:
image: webapp452
Il nome scelto verrà salvato all’interno della variabile $DOCKER_IMAGE;
- Avviare Windows PowerShell e, una volta posizionati al percorso in cui è contenuto il file docker-compose.yml, lanciare il seguente comando:
docker-compose build
- Accedere al Portale Azure ed aggiungere un nuovo gruppo risorse: Resource Groups, Add e successivamente impostare un nome (nel nostro caso “RG_Webapp4.5.2”). Il nome della risorsa verrà salvato nella variabile $RESOURCE_GROUP;
- Sempre dal Portale Azure, aggiungere un nuovo contenitore impostando un nome, il gruppo risorse creato in precedenza, ed abilitando l’utente amministratore.Ad esempio: Container registries, Add ed impostare come segue:
Name: “containerwebapp452” ($CONTAINER_NAME)
Resource Group: $RESOURCE_GROUP
Admin User: Enable - Restando sul Portale Azure, entrare all’interno del contenitore appena creato ed accedere alla sezione Access keys. Qui prelevare le informazioni dei campi Username e Password;
- Tornare alla finestra PowerShell e digitare il comando:
docker login <$CONTAINER_NAME>.azurecr.io -u <$CONTAINER_USERNAME> -p <$CONTAINER_PASSWORD>
Dove <$CONTAINER_USERNAME> e <$CONTAINER_PASSWORD> sono i campi prelevati dal Portale Azure nel punto precedente;
- Lanciare quindi il comando:
docker tag <$DOCKER_IMAGE> <$CONTAINER_NAME>.azurecr.io/<$DOCKER_IMAGE>
per applicare il TAG all’immagine, e successivamente:
docker push <$CONTAINER_NAME>.azurecr.io/<$DOCKER_IMAGE>
per effettuare il PUSH dell’immagine su Docker;
- Al termine dell’operazione di PUSH, è necessario procedere con la creazione di un utente di servizio che avrà accesso al cluster AKS (Azure Kubernetes Service) che andremo a creare successivamente. Lanciare quindi il comando:
az ad sp create-for-rbac --skip-assignment
e, dal risultato ottenuto appuntarsi i valori di appId e password;
- A questo punto bisogna assegnare il ruolo di Reader all’utente appena creato per permettergli di effettuare l’operazione di PULL, della risorsa oggetto dell’operazione di PUSH precedente. In sequenza ecco i due comandi necessari:
az acr show --resource-group <$RESOURCE_GROUP> --name <$CONTAINER_NAME> --query "id" --output tsv
Appuntarsi quindi il parametro <$acrId> ed utilizzarlo all’interno del seguente comando:
az role assignment create --assignee <$appId> --scope <$acrId> --role Reader
- Ora si può procedere con la creazione del cluster AKS mediante il seguente comando:
az aks create --resource-group <$RESOURCE_GROUP> --name <$CLUSTER_NAME> --node-count 1 --service-principal <$appId> --client-secret <$password> --generate-ssh-keys --kubernetes-version 1.11.5
In questo caso verrà creato un cluster con singolo nodo. La configurazione rimane comunque editabile anche in un secondo momento e si può inoltre cambiare il numero di nodi iniziali agendo sul valore del parametro “–node-count“.
N.B. Nel caso venga riscontrato l’errore:
az aks create: error: Incorrect padding
all’interno della PowerShell, è possibile lanciare il comando di creazione del cluster direttamente dal Portale Azure mediante l’uso della Cloud Shell.
Se all’interno della Kubernates services del Portale Azure appare il cluster appena creato con lo stato “Succeeded“, l’operazione è terminata con successo ed è possibile continuare con la pubblicazione.
- Lanciare il comando:
az aks install-cli
e successivamente effettuare la connessione al cluster:
az aks get-credentials --resource-group <$RESOURCE_GROUP> --name <$CLUSTER_NAME>
Installazione Virtual Kubelet
AKS (Azure Kubernetes Service) riesce a gestire in modo automatico dei nodi virtuali verso delle ACI (Azure Container Instance) solamente se il contenitore usa un’immagine Linux. Per Windows invece, è necessario l’utilizzo di uno strumento chiamato Virtual Kubelet.
N.B. Per installare Virtual Kubelet bisogna installare anche Helm.
-
- Creare un file di testo vuoto chiamato rbac-virtual-kubelet.yaml e copiarci il seguente testo:
apiVersion: v1 kind: ServiceAccount metadata: name: tiller namespace: kube-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: tiller roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - kind: ServiceAccount name: tiller namespace: kube-system
- Posizionarsi all’interno della cartella nel quale è stato creato il file e lanciare i seguenti comandi:
kubectl apply -f rbac-virtual-kubelet.yaml helm init --service-account tiller az aks install-connector --resource-group <$RESOURCE_GROUP> --name <$CLUSTER_NAME> --connector-name virtual-kubelet --os-type Windows
A questo punto, mediante il comando:
kubectl get nodes
si dovrebbero vedere due nodi: l’agent, creato al momento della creazione del cluster AKS, e il nuovo nodo virtual-kubelet appena creato, da utilizzare come connettore verso i contenitori Windows;
- L’utilizzo dei Virtual Kubelet rende necessaria la creazione di una chiave segreta, questo per poter effettuare il PULL dell’immagine. Tramite PowerShell, usare quindi la seguente sequenza di comandi, sostituendo le variabili con i valori assegnati precedentemente:
$ACR_NAME="<$CONTAINER_NAME>" $SERVICE_PRINCIPAL_NAME="<$CONTAINER_USERNAME>" $ACR_LOGIN_SERVER=$(az acr show --name $ACR_NAME --query loginServer --output tsv) $ACR_REGISTRY_ID=$(az acr show --name $ACR_NAME --query id --output tsv) $SP_PASSWD=$(az ad sp create-for-rbac --name $SERVICE_PRINCIPAL_NAME --role acrpull --scopes $ACR_REGISTRY_ID --query password --output tsv) $CLIENT_ID=$(az ad sp show --id http://$SERVICE_PRINCIPAL_NAME --query appId --output tsv) kubectl create secret docker-registry acr-auth --docker-server $ACR_LOGIN_SERVER --docker-username $SERVICE_PRINCIPAL_NAME --docker-password <$CONTAINER_PASSWORD> --docker-email <$ACCOUNT_EMAIL>
- Creare un file di testo vuoto chiamato rbac-virtual-kubelet.yaml e copiarci il seguente testo:
Pubblicazione dell’immagine
A questo punto siamo pronti per la pubblicazione, sul cluster, dell’immagine caricata all’interno del contenitore ACI (Azure Container Instance).
- Per procedere, va creato un file di testo vuoto chiamato, ad esempio, virtual-kubelet-windows.yaml contenente:
apiVersion: apps/v1 kind: Deployment metadata: name: <$DOCKER_IMAGE> spec: replicas: <$PODS_NUMBER> selector: matchLabels: app: <$DOCKER_IMAGE> template: metadata: labels: app: <$DOCKER_IMAGE> spec: containers: - name: <$DOCKER_IMAGE> image: <$CONTAINER_NAME>.azurecr.io/<$DOCKER_IMAGE> ports: - containerPort: 80 imagePullPolicy: Always imagePullSecrets: - name: acr-auth nodeSelector: beta.kubernetes.io/os: windows kubernetes.io/role: agent type: virtual-kubelet tolerations: - key: virtual-kubelet.io/provider operator: Equal value: azure effect: NoSchedule --- apiVersion: v1 kind: Service metadata: name: <$DOCKER_IMAGE> spec: type: LoadBalancer ports: - port: 80 selector: app: <$DOCKER_IMAGE>
Le variabili: <$DOCKER_IMAGE>, <$PODS_NUMBER> e <$CONTAINER_NAME> vanno sostituite con i parametri di configurazione assegnati precedentemente;
- Tramite PowerShell, posizionarsi all’interno del percorso che contiene il file appena creato e lanciare il seguente comando:
kubectl apply -f virtual-kubelet-windows.yaml
Così facendo, verranno originati diversi POD, ognuno dei quali punterà ad una ACI, e saranno tutti gestiti dal Virtual Kubelet creato in precedenza.
Il completamento dell’operazione dura in genere alcuni minuti, anche in base al numero di POD che deve generare.
Per monitorarne lo stato di creazione è possibile accedere alla sezione Container Instances del Portale Azure. Lo stato “Creating” indica che l’operazione è ancora in corso.
Dal Portale Azure è anche possibile conoscere l’IP pubblico con cui accedere al set di POD appena creato. Per farlo:
- Cliccare su uno dei POD appena creato;
- Tramite il link Resource Group si apriranno gli elementi associati al gruppo di risorse, selezionare quello con il parametro Type assegnato come Load Balancer;
- Tra le molteplici informazioni mostrate, vi è anche Public IP address, l’indirizzo pubblico con cui accedere al cluster creato.
Caso 2: Applicazione sviluppata in .NET Core
Nel caso avessimo a che fare con un applicazione sviluppata con tecnologia .NET Core, all’interno del contenitore Docker è possibile utilizzare sia il sistema operativo Windows Nano Server, oppure Linux.
Vediamo anche per questo caso i vari passaggi:
- Seguire i passaggi 1 e 2 descritti nel Caso 1, per scaricare ed installare Docker Desktop for Windows;
- Aprire il progetto WebApp su Visual Studio ed aggiungere un file di nome Dockerfile all’interno della root;
- All’interno del Dockerfile aggiungere il seguente testo:
FROM microsoft/dotnet:2.2-aspnetcore-runtime ARG source=. WORKDIR /app EXPOSE 80 COPY $source . ENTRYPOINT ["dotnet", "<$DLL_STARTUP_PROJECT>"]
Dove il parametro <$DLL_STARTUP_PROJECT> dovrà essere sostituito con il nome della dll del progetto di partenza.
Inoltre il Dockerfile deve sempre essere copiato all’interno della directory di output (“Copy Always” nelle proprietà del file);
- Creare un profilo di pubblicazione della WebApp, impostare l’output su FileSystem e chiamarlo DockerProfile.
Come impostazioni di pubblicazione usare le seguenti:
Configurazione: Release
Modalità destinazione: Dipendente del framework
Runtime: Linux-64
Opzioni pubblicazione: Elimina tutti i file esistenti - Avviare quindi la pubblicazione ed attendere l’esito;
- Aprire PowerShell e creare l’immagine Docker tramite il seguente comando:
docker build <$PUBLISH_FOLDER> -t <$CONTAINER_NAME>.azurecr.io/<$DOCKER_IMAGE>:0
- Al termine della creazione dell’immagine, effettuarne il PUSH verso ACI (Azure Container Instance) con il comando:
docker push <$CONTAINER_NAME>.azurecr.io/<$DOCKER_IMAGE>:0
- Seguire dal punto 14 al 17 mostrati all’interno del Caso 1;
- A questo punto l’immagine caricata sul contenitore ACI è pronta per la pubblicazione.Creare quindi un nuovo file chiamato webapp.yaml e copiarci al suo interno il seguente testo:
apiVersion: apps/v1 kind: Deployment metadata: name: <$DOCKER_IMAGE> spec: replicas: <$PODS_NUMBER> selector: matchLabels: app: <$DOCKER_IMAGE> template: metadata: labels: app: <$DOCKER_IMAGE> spec: containers: - name: <$DOCKER_IMAGE> image: <$CONTAINER_NAME>.azurecr.io/<$DOCKER_IMAGE> ports: - containerPort: 80 imagePullPolicy: Always imagePullSecrets: - name: acr-auth --- apiVersion: v1 kind: Service metadata: name: <$DOCKER_IMAGE> spec: type: LoadBalancer ports: - port: 80 selector: app: <$DOCKER_IMAGE>
Sostituire le variabili: <$DOCKER_IMAGE>, <$PODS_NUMBER> e <$CONTAINER_NAME> con i parametri opportuni di configurazione;
- Tramite PowerShell posizionarsi al percorso nel quale è stato creato il file webapp.yaml e lanciare il seguente comando per procedere con la pubblicazione finale:
kubectl apply -f webapp.yaml