Archiviazione, distribuzione e orchestrazione di container utilizzando Azure Container Registry e Azure Kubernetes Service

Azure Container Registry è un servizio Azure che permette di archiviare le immagini dei container privati di Docker che gli sviluppatori possono utilizzare per distribuire i microservizi in Azure. Ho già diverse volte trattato l’argomento Container negli articoli Container – ICT Power e oggi voglio trattare la creazione di un registro privato di container e il suo utilizzo per distribuire immagini personalizzate e private utilizzando Azure Kubernetes Service.

Creazione di un nuovo Container Registry

Per creare un nuovo Container Registry in Azure è sufficiente collegarsi al portale e utilizzare +Create a Resource, scegliendo tra le categorie la voce Containers.

Figura 1: Creazione di un nuovo Container Registry

Inserite le informazioni richieste nella pagina Basics e scegliete il tipo SKU più data al vostro scopo. I diversi livelli di servizio e i piani tariffari (noti come SKU, Stock Keeping Unit) offrono opzioni e prezzi diversi. Attualmente sono disponibili 3 livelli: Basic, Standard e Premium. Alla pagina Livelli di servizio registro e funzionalità – Azure Container Registry | Microsoft Docs trovate maggiori informazioni a riguardo. Io ho scelto di utilizzare la versione Premium, che permette di utilizzare la replica geografica e l’utilizzo degli endpoint privati.

Figura 2: Pagina Basics del wizard di creazione di Azure Container Registry

Figura 3: Scelta della SKU Premium e utilizzo delle Availability Zones

Nella scheda Networking è possibile configurare il Registry per l’accesso Pubblico oppure per l’accesso Privato. Se decidete di permettere solo l’accesso Privato avrete poi la possibilità di creare anche un Private Endpoint.

Figura 4: Scheda Networking del wizard di creazione di un Azure Container Registry

Io ho scelto di permettere l’accesso Pubblico. Vi consiglio però di approfondire l’argomento dando un’occhiata all’interessante articolo Configurare un endpoint privato con collegamento privato – Azure Container Registry | Microsoft Docs se volete configurare un endpoint privato.

Figura 5: Scelta di permettere l’accesso pubblico all’Azure Container Registry

Nella schermata Encryption avete la possibilità di utilizzare una vostra chiave di cifratura per proteggere tutti i contenitori che conserverete nell’Azure Container Registry. Sono disponibili maggiori dettagli alla pagina Crittografare il registro con una chiave gestita dal cliente – Azure Container Registry | Microsoft Docs. La crittografia lato server con chiavi gestite dal cliente è supportata tramite l’integrazione con Azure Key Vault.

Figura 6: Crittografia dell’Azure Container Registry utilizzando le chiavi gestite dal cliente

Azure crittografa automaticamente il contenuto inattivo del registro con le chiavi gestite dal servizio. Pertanto ho deciso in questa guida di non utilizzare delle chiavi personali. Proseguite con il wizard e completate la creazione di Azure Container Registry.

Figura 7: Termine del wizard di creazione di Azure Container Registry

Nel giro di qualche secondo l’Azure Container Registry sarà creato.

Figura 8: Creazione di Azure Container Registry completata

Creazione di un docker file, realizzazione di un container e inserimento del container in Azure Container Registry

Il motore Docker include strumenti che automatizzano la creazione di immagini del container. Anche se è possibile creare immagini del container manualmente eseguendo il comando docker commit, l’adozione di un processo di creazione automatica delle immagini offre molti vantaggi, tra cui la ricreazione rapida e precisa delle docker images.

Dockerfile è un file di testo che contiene le istruzioni necessarie per creare una nuova immagine del contenitore. Queste istruzioni includono l’identificazione di un’immagine esistente da usare come base, i comandi da eseguire durante il processo di creazione delle immagini e un comando che verrà eseguito quando si distribuiscono nuove istanze dell’immagine contenitore.

La compilazione di Docker è il comando del motore Docker che utilizza un Dockerfile e attiva il processo di creazione dell’immagine. Trovate tutti i dettagli alla pagina Dockerfile reference | Docker Documentation

L’istruzione FROM , ad esempio, imposta l’immagine contenitore che verrà usata durante il processo di creazione di una nuova immagine.

Con il comando echo FROM nginx > Dockerfile viene creato un Dockerfile che utilizza l’immagine nginx presa direttamente dal registry pubblico Docker Hub.

Figura 9: Creazione di un Dockerfile

Per creare una nuova docker image direttamente dal DockerFile creato è sufficiente recuperare il nome dell’Azure Container Registry e lanciare il comando

 

Attenti al punto alla fine del comando e ovviamente sostituite i valori del registry e del file in base a quelli che avete creato o di cui già disponete.

Figura 10: Creazione di una nuova docker image e inserimento nell’Azure Container Registry

Nella scheda Repositories del vostro Azure Container Registry potrete visualizzare la nuova docker image creata e la sua relativa versione.

Figura 11: Immagini create in Azure Container Registry

Figura 12: Versione della docker image creata

Creazione di un cluster di Azure Kubernetes Services

Come ho già avuto modo di spiegare nell’articolo Distribuire Azure Kubernetes Service (AKS) – ICT Power, QUANDO lo sviluppo di applicazioni si sposta verso un approccio basato sui containers, è importante la necessità di orchestrare e gestire le risorse. Kubernetes è la piattaforma leader che offre programmazione affidabile di carichi di lavoro applicativi.

È possibile compilare ed eseguire applicazioni basate su microservizi moderne e portabili che sfruttano la capacità di Kubernetes di orchestrare e gestire la disponibilità di tali componenti applicativi.

Azure Kubernetes Service (AKS) è un servizio Kubernetes gestito che riduce la complessità delle attività di distribuzione e delle attività principali di gestione, tra cui il coordinamento degli aggiornamenti.

Per creare un Azure Kubernetes Service (AKS) è sufficiente dal portale di Azure selezionare +Create a resource e nella categoria Containers selezionare la voce Kubernetes Service.

Figura 13: Creazione di un Azure Kubernetes Service (AKS)

Per creare un Kubernetes Cluster vi verranno richieste diverse informazioni. Inserite il nome del cluster, la regione dove volete distribuirlo, la versione di Kubernetes e il tipo di macchine virtuali che saranno utilizzate nel Pool di nodi. I nodi della stessa configurazione sono raggruppati in pool di nodi. Un cluster Kubernetes contiene uno o più pool di nodi, che verranno utilizzati per far girare le vostre applicazioni. Il numero e la dimensione iniziale dei nodi sono definiti quando si crea un cluster Azure Kubernetes Service; infatti, viene creato un pool di nodi predefinito. Questo pool di nodi predefinito in AKS contiene le macchine virtuali che eseguono i nodi agent.

Per garantire il funzionamento affidabile del cluster e l’alta disponibilità è consigliabile eseguire 3 o più nodi nel pool di nodi predefinito. Io ho scelto di utilizzare l’autoscale. Il componente di scalabilità automatica del cluster può cercare i pod del cluster che non possono essere pianificati a causa di vincoli delle risorse. Quando vengono rilevati problemi, il numero di nodi di un pool viene incrementato per soddisfare la richiesta delle applicazioni. Inoltre i nodi vengono controllati regolarmente per rilevare la mancanza di pod in esecuzione, con la riduzione del numero di nodi in base alle esigenze. Questa capacità di incrementare o ridurre il numero di nodi nel cluster del servizio Azure Kubernetes permette di gestire il cluster in modo efficace e conveniente.

Maggiori informazioni sono disponibili alla pagina Usare il componente di scalabilità automatica del cluster nel servizio Azure Kubernetes (AKS) – Azure Kubernetes Service | Microsoft Docs

Figura 14: Schermata Basics del wizard id creazione di Azure Kubernetes Service

Per poter offrire scalabilità alla vostra applicazione è possibile definire un VM scale set e definire i Virtual nodes. Ho già avuto modo di parlare dei Virtual Machine Scale Set (VMSS) nell’articolo Configurare Azure Virtual Machine Scale Set

nodi virtuali consentono di effettuare rapidamente il provisioning dei pod, senza dover aspettare che la funzionalità di scalabilità automatica crei delle nuove macchine virtuali per eseguire pod aggiuntivi. I nodi virtuali sono supportati solo con i pod e i nodi Linux. Per maggiori informazioni vi rimando alla lettura della guida Creare nodi virtuali usando il portale nel servizio Azure Kubernetes (AKS) – Azure Kubernetes Service | Microsoft Docs

Figura 15: Schermata Node pools del wizard di creazione di Azure Kubernetes Service

Nella pagina Authentication configurate il Service principal da utilizzare per le autenticazioni. Si può creare un nuovo Service principal oppure utilizzarne uno esistente. Nel caso si voglia utilizzare un Service principal esistente sarà necessario specificarne il client ID e il secret.

Per ottenere un controllo più capillare sull’accesso alle risorse Kubernetes distribuite nel cluster di AKS abilitate l’opzione per il controllo degli accessi in base al ruolo (RBAC) di Kubernetes.

Figura 16: Schermata Authentication del wizard di creazione di Azure Kubernetes Service

Per consentire l’accesso alle applicazioni o per consentire ai componenti dell’applicazione di comunicare tra loro, Kubernetes offre un livello di astrazione per funzionalità di rete virtuale (virtual networks). I nodi Kubernetes vengono connessi a una rete virtuale e possono fornire la connettività in ingresso e in uscita per i pod. Il componente kube-proxy viene eseguito in ogni nodo per fornire queste funzionalità di rete. In Kubernetes, i servizi raggruppano i pod dal punto di vista logico per consentire l’accesso diretto tramite un indirizzo IP o nome DNS e su una porta specifica. È anche possibile distribuire il traffico usando un bilanciamento del carico. Si può anche ottenere un routing più complesso del traffico dell’applicazione con i controller di ingresso. La sicurezza e il filtraggio del traffico di rete per i pod sono possibili con i criteri di rete di Kubernetes.

Nella pagina Networking è possibile configurare in che modo deve essere collegato il cluster di Kubernetes alla rete. Io ho deciso di utilizzare Azure Container Networking Interface (CNI) e di creare una nuova Azure Virtual Network, in modo tale che il cluster del servizio Azure Kubernetes viene connesso alle risorse di rete virtuale e alle configurazioni esistenti. Potete approfondire il networking di Azure Kubernetes Service alla pagina Concetti – Funzionalità di rete nel servizio Azure Kubernetes – Azure Kubernetes Service | Microsoft Docs

Figura 17: Scheda Networking del wizard di creazione di Azure Kubernetes Service

È possibile integrare il cluster di Azure Kubernetes Service con un Azure Container Registry esistente oppure è possibile creare un registry nuovo. Io ho deciso di non collegare il registry che abbiamo creato e lo farò in un secondo momento. Configurate il monitoraggio dei definendo il Log Analytics Workspace.

Figura 18: Schermata Integration del wizard di creazione di Azure Kubernetes Service

Figura 19: Schermata finale del wizard di creazione di Azure Kubernetes Service, con il riepilogo delle impostazioni scelte

La creazione del cluster del servizio Azure Kubernetes richiede alcuni minuti. Al termine della distribuzione, fate clic su Vai alla risorsa oppure individuate il gruppo di risorse che avete utilizzato e selezionate la risorsa Azure Kubernetes Service.

Figura 20: Scheda Overview dell’Azure Kubernetes Service creato

Figura 21: Node Pool creato

Figura 22: Risorse create nel Resource Group

Figura 23: Resource Group con tutte le risorse create per Azure Kubernetes Service

Connessione al cluster AKS

Per connettervi e gestire il cluster AKS potete utilizzare il comando kubectl presente in Azure Cloud Shell nella shell Bash.

Come prima operazione collegatevi al cluster, scaricate le credenziali di accesso e configurate la Kubernetes CLI per utilizzarle. Il comando da lanciare è:

 

Nel mio caso ho utilizzato il comando

 

Integrazione dell’Azure Container Registry con il cluster di Azure Kubernetes Service

Ho quindi provveduto a collegare l’Azure Container Registry creato in precedenza (chiamato nicferr) con il comando:

 

Figura 24: Connessione dell’Azure Container Registry all’Azure Kubernetes Service

Configurare le funzionalità di rete di Azure CNI in Azure Kubernetes Service

Per impostazione predefinita, i cluster del servizio Azure Kubernetes usano kubenet e la creazione di una rete virtuale e di una subnet avviene automaticamente. Con kubenet i nodi ottengono un indirizzo IP da una subnet della rete virtuale. Il protocollo NAT (Network Address Translation) viene quindi configurato nei nodi e i pod ricevono un indirizzo IP “nascosto” dietro l’indirizzo IP del nodo. Questo approccio riduce il numero di indirizzi IP che è necessario riservare ai pod nello spazio degli indirizzi della rete.

Con Azure Container Networking Interface (CNI) ogni pod ottiene un indirizzo IP dalla subnet in modo che vi si possa accedere direttamente. Questi indirizzi IP devono essere univoci nello spazio di indirizzi della rete e devono essere pianificati in anticipo.

Per poter permettere la corretta interazione tra il cluster AKS e la Virtual Network è necessario modificare i permessi di accesso RBAC e consentirgli almeno il ruolo di Contributor utilizzando il comando:

 

Figura 25: Assegnazione del ruolo di contributo sulla Virtual Network all’Azure Kubernetes Service

Figura 26: Ruolo assegnato correttamente alla Virtual Network

Esecuzione di un’applicazione (container) in Azure Kubernetes Cluster

Un Kubernetes Manifest file definisce lo stato desiderato di un cluster, ad esempio le Docker images da eseguire. Una distribuzione rappresenta pod identici gestiti dal controller di distribuzione Kubernetes. Una distribuzione definisce il numero di repliche di pod da creare. L’utilità di pianificazione di Kubernetes garantisce che siano pianificati pod aggiuntivi nei nodi integri se si verificano problemi nei pod o nei nodi.

È possibile aggiornare le distribuzioni per modificare la configurazione dei pod, l’immagine del contenitore utilizzata o la risorsa di archiviazione collegata. Le distribuzioni vengono in genere create e gestite con kubectl create o kubectl apply. Creare una distribuzione definendo un file manifesto nel formato YAML. È possibile creare applicazioni più complesse includendo servizi (ad esempio servizi di bilanciamento del carico) all’interno del manifesto YAML.

Figura 27: Esempio di file YAML

Per altre informazioni, vedere le distribuzioni Kubernetes.

Potete anche approfondire sintassi ed utilizzi di YAML guardando un video molto interessante YAML – Sintassi ed utilizzi – YouTube a cura di Emiliano Musso.

Io ho utilizzato il file YAML che vedete sotto per distribuire un deployment chiamato nginxexternal e pubblicarlo in Azure Kubernetes Service, con un bilanciatore di carico ed un indirizzo IP pubblico.

Figura 28: YAML per la distribuzione dei container basati sull’immagine creata in precedenza

Figura 29: Caricamento del file YAML di configurazione

Ho quindi eseguito il comando kubectl apply -f nginxexternal.yaml per distribuire il deployment.

Figura 30: Distribuzione del deployment utilizzando il file YAML

Con il comando kubectl get service nginxexternal ho verificato il corretto deployment e mi sono procurato l’indirizzo IP pubblico con cui è stato pubblicato.

Figura 31: Verifica del deployment e recupero dell’indirizzo IP pubblico

In alternativa l’indirizzo IP pubblico del bilanciatore è visibile anche dal portale di Azure.

Figura 32: Recupero dell’indirizzo IP pubblico utilizzato dal bilanciatore di Azure Kubernetes Service

Figura 33: Connessione all’indirizzo IP pubblico e verifica dell’esecuzione del container

Distribuzione di un servizio interno con Azure AKS

il file YAML che vedete sotto per distribuire un deployment chiamato nginxinternal e pubblicarlo in Azure Kubernetes Service, con un bilanciatore di carico ed un indirizzo IP pubblico.

Figura 34: File YALM per la creazione di un servizio interno in Azure AKS

Dopo aver caricato il file YAML utilizzando la Azure Cloud Shell ho eseguito il comando kubectl apply -f nginxinternal.yaml per distribuire il deployment.

Figura 35: Distribuzione di un secondo deployment in Azure AKS utilizzando il file YAML

Urilizzando il comando kubectl get service nginxinternal mi sono procurato l’indirizzo IP interno associato al load balance di Azure AKS.

Figura 36: Recupero dell’indirizzo IP interno assegnato al load balancer interno del service nginxinternal

La stessa operazione può essere effettuata verificando l’indirizzo IP dal portale di Azure.

Figura 37: Indirizzo IP del bilanciatore di carico interno di Azure AKS

Collegandosi all’indirizzo IP del bilanciatore del servizio esposto da Azure AKS da una VM che è collegata alla stessa Virtual Network sarà possibile visualizzare il container di nginx.

Figura 38: Connessione al servizio pubblicato internamente sulla Virtual Network

Tutti i servizi creati e le loro configurazioni sono anche visibili nel portale di Azure nel nodo Services and ingresses del cluster di Azure Kubernetes Service.

Figura 39: Servizi pubblicati da Azure Kubernetes Service

AKS Service Security

Per proteggere i dati dei clienti durante l’esecuzione di carichi di lavoro dell’applicazione in Azure Kubernetes Service, la sicurezza del cluster è un fattore fondamentale. Kubernetes include componenti di sicurezza che vengono combinati in modo che il cluster del servizio Azure Kubernetes esegua sempre gli aggiornamenti della sicurezza del sistema operativo e le versioni di Kubernetes più recenti, con il traffico di pod e l’accesso sicuri alle credenziali sensibili.

I nodi del servizio Azure Kubernetes sono macchine virtuali di Azure gestite dall’utente. I nodi Linux eseguono una distribuzione Ubuntu ottimizzata usando il runtime di Moby container. I nodi di Windows Server (attualmente in anteprima in AKS) eseguono una versione ottimizzata di Windows Server 2019 e usano anche il runtime di Moby container.

La piattaforma Azure applica automaticamente le patch di sicurezza del sistema operativo ai nodi Linux su base giornaliera. Se un aggiornamento della sicurezza del sistema operativo Linux richiede un riavvio dell’host, il riavvio non viene eseguito automaticamente. È possibile riavviare manualmente i nodi Linux oppure usare KURED, un daemon di riavvio open source per Kubernetes. Kured viene eseguito come DaemonSet e monitora ogni nodo per verificare se è presente un file che indichi che è necessario un riavvio.

Per i nodi di Windows Server, i Windows Update non vengono eseguiti automaticamente e applicati gli aggiornamenti più recenti. In base a una pianificazione regolare per il ciclo di rilascio Windows Updat, è necessario eseguire un aggiornamento sui pool di nodi di Windows Server nel cluster AKS. Questo processo di aggiornamento crea nodi che eseguono la versione più recente dell’immagine e delle patch di Windows Server, quindi rimuove i nodi precedenti.

Per la sicurezza e la conformità o per usare le funzionalità più recenti, Azure offre strumenti per orchestrare l’aggiornamento di un cluster e dei componenti del Azure Kubernetes Service. Questa orchestrazione dell’aggiornamento include sia il master che i componenti agent di Kubernetes. È possibile visualizzare un elenco delle versioni di Kubernetes disponibili per il cluster del servizio Azure Kubernetes. Per avviare il processo di aggiornamento, si specifica una di queste versioni disponibili. Azure quindi blocca e svuota in modo sicuro ogni nodo del servizio Azure Kubernetes ed esegue l’aggiornamento.

Conclusioni

Quando lo sviluppo di applicazioni si sposta verso un approccio basato su contenitori, è importante la necessità di orchestrare e gestire le risorse. Kubernetes è la piattaforma leader che offre programmazione affidabile di carichi di lavoro applicativi. La piattaforma è incentrata sui carichi di lavoro applicativi e non sui componenti dell’infrastruttura sottostante ed il servizio AKS riduce la complessità delle attività di distribuzione e delle attività principali di gestione, tra cui il coordinamento degli aggiornamenti. Grazie all’utilizzo di Azure Container Registry possiamo anche utilizzare un nostro registry personalizzato dove poter caricare le docker images da utilizzare per i nostri deployment.