Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Suggerimento
Questo contenuto è un estratto dell'eBook Architecting Cloud Native .NET Applications for Azure, disponibile in .NET Docs o come PDF scaricabile gratuito che può essere letto offline.
I contenitori e gli agenti di orchestrazione sono progettati per risolvere i problemi comuni agli approcci di distribuzione monolitica.
Sfide con le distribuzioni monolitiche
Tradizionalmente, la maggior parte delle applicazioni è stata distribuita come singola unità. Tali applicazioni vengono definite monolitiche. Questo approccio generale alla distribuzione di applicazioni come singole unità anche se sono composte da più moduli o assembly è nota come architettura monolitica, come illustrato nella figura 3-1.
Figura 3-1. Architettura monolitica.
Anche se hanno il vantaggio della semplicità, le architetture monolitiche affrontano molte sfide:
Distribuzione
Inoltre, richiedono un riavvio dell'applicazione, che può influire temporaneamente sulla disponibilità se non vengono applicate tecniche di tempo di inattività zero durante la distribuzione.
Scalabilità
Un'applicazione monolitica è ospitata interamente in una singola istanza del computer, spesso richiede hardware ad alta capacità. Se una parte del monolith richiede il ridimensionamento, è necessario distribuire un'altra copia dell'intera applicazione in un altro computer. Con un monolith, non è possibile ridimensionare singolarmente i componenti dell'applicazione, ovvero tutto o niente. I componenti di ridimensionamento che non richiedono la scalabilità comportano un utilizzo inefficiente e costoso delle risorse.
Ambiente
Le applicazioni monolitiche vengono in genere distribuite in un ambiente di hosting con un sistema operativo, un runtime e dipendenze di libreria preinstallate. Questo ambiente potrebbe non corrispondere a quello su cui è stata sviluppata o testata l'applicazione. Le incoerenze negli ambienti dell'applicazione sono una fonte comune di problemi per le distribuzioni monolitiche.
Accoppiamento
È probabile che un'applicazione monolitica abbia un accoppiamento elevato tra i componenti funzionali. Senza limiti rigidi, le modifiche al sistema spesso comportano effetti collaterali imprevisti e costosi. Le nuove funzionalità/correzioni diventano complicate, dispendiose in termini di tempo e costose da implementare. Gli aggiornamenti richiedono test completi. L'accoppiamento rende anche difficile effettuare il refactoring dei componenti o scambiare implementazioni alternative. Anche quando costruito con una rigorosa separazione dei compiti, l'erosione architetturale si verifica quando la base di codice monolitica si degrada con casi speciali senza fine.
Blocco della piattaforma
Un'applicazione monolitica viene costruita con un unico stack di tecnologie. Pur offrendo uniformità, questo impegno può diventare un ostacolo all'innovazione. Nuove funzionalità e componenti verranno compilati usando lo stack corrente dell'applicazione, anche quando le tecnologie più moderne possono essere una scelta migliore. Un rischio a lungo termine è che lo stack tecnologico diventi datato e obsoleto. Riprogettare un'intera applicazione a una nuova piattaforma più moderna è al massimo costoso e rischioso.
Quali sono i vantaggi dei container e degli orchestratori?
Sono stati introdotti i contenitori nel capitolo 1. È stato evidenziato come la Cloud Native Computing Foundation (CNCF) classifichi la containerizzazione come il primo passo nella loro Cloud-Native Trail Map - guida per le aziende che iniziano il loro percorso verso il cloud-native. In questa sezione vengono illustrati i vantaggi dei contenitori.
Docker è la piattaforma di gestione dei contenitori più diffusa. Funziona con i contenitori sia in Linux che in Windows. I contenitori forniscono ambienti applicazioni separati ma riproducibili che eseguono lo stesso modo in qualsiasi sistema. Questo aspetto li rende perfetti per lo sviluppo e l'hosting di servizi nativi del cloud. I contenitori sono isolati l'uno dall'altro. Due contenitori nello stesso hardware host possono avere versioni diverse del software, senza causare conflitti.
I contenitori sono definiti da semplici file basati su testo che diventano artefatti del progetto e vengono controllati nel controllo del codice sorgente. Anche se i server completi e le macchine virtuali richiedono un impegno manuale per l'aggiornamento, i contenitori sono facilmente versionati. Le app compilate per l'esecuzione in contenitori possono essere sviluppate, testate e distribuite usando strumenti automatizzati come parte di una pipeline di compilazione.
I contenitori non sono modificabili. Dopo aver definito un contenitore, è possibile ricrearlo ed eseguirlo esattamente nello stesso modo. Questa immutabilità si presta alla progettazione basata su componenti. Se alcune parti di un'applicazione si evolvono in modo diverso rispetto ad altre, perché ridistribuire l'intera app quando è sufficiente distribuire le parti che cambiano più di frequente? Diverse funzionalità e problematiche trasversali di un'app possono essere suddivise in unità separate. La figura 3-2 mostra come un'app monolitica può sfruttare i vantaggi dei contenitori e dei microservizi delegando determinate funzionalità o funzionalità. Anche le funzionalità rimanenti nell'app sono state incluse in contenitori.
Figura 3-2. Scomposizione di un'app monolitica per l'adozione di microservizi.
Ogni servizio nativo del cloud viene compilato e distribuito in un contenitore separato. Ognuno può aggiornare in base alle esigenze. I singoli servizi possono essere ospitati in nodi con risorse appropriate per ogni servizio. L'ambiente in cui viene eseguito ogni servizio è immutabile, condiviso tra gli ambienti di sviluppo, test e produzione e facilmente versionabile. L'accoppiamento tra aree diverse dell'applicazione avviene in modo esplicito come chiamate o messaggi tra servizi, non dipendenze in fase di compilazione all'interno del monolitico. È anche possibile scegliere la tecnologia più adatta a una determinata funzionalità senza richiedere modifiche al resto dell'app.
I servizi in contenitori richiedono la gestione automatizzata. Non sarebbe possibile amministrare manualmente un set di grandi dimensioni di contenitori distribuiti in modo indipendente. Si considerino ad esempio le attività seguenti:
- Come verrà effettuato il provisioning delle istanze del container all'interno di un cluster di numerosi computer?
- Dopo la distribuzione, in che modo i contenitori individuano e comunicano tra loro?
- In che modo i contenitori possono ridimensionarsi su richiesta?
- Come si monitora l'integrità di ogni contenitore?
- Come si protegge un contenitore da errori hardware e software?
- Come si aggiornano i contenitori per un'applicazione dinamica senza tempi di inattività?
Gli orchestratori dei contenitori gestiscono e automatizzano questi e altri problemi.
Nell'ecosistema cloud-native, Kubernetes è diventato l'orchestratore di container standard di fatto. Si tratta di una piattaforma open source gestita dalla Cloud Native Computing Foundation (CNF). Kubernetes automatizza la distribuzione, il ridimensionamento e i problemi operativi dei carichi di lavoro in contenitori in un cluster di computer. Tuttavia, l'installazione e la gestione di Kubernetes è notoriamente complessa.
Un approccio molto migliore consiste nell'usare Kubernetes come servizio gestito da un fornitore di servizi cloud. Il cloud di Azure offre una piattaforma Kubernetes completamente gestita denominata Servizio Azure Kubernetes. AKS astrae la complessità e il sovraccarico operativo della gestione di Kubernetes. Si usa Kubernetes come servizio cloud; Microsoft si assume la responsabilità della gestione e del supporto. AKS si integra strettamente con altri servizi e strumenti di sviluppo di Azure.
AKS è una tecnologia basata su cluster. Un pool di macchine virtuali federate, o nodi, viene distribuito nel cloud di Azure. Insieme formano un ambiente a disponibilità elevata o un cluster. Il cluster viene visualizzato come una singola entità trasparente per l'applicazione nativa del cloud. Nel backend, AKS distribuisce i servizi containerizzati su questi nodi seguendo una strategia predefinita che distribuisce uniformemente il carico.
Quali sono i vantaggi del ridimensionamento?
I servizi basati su contenitori possono sfruttare i vantaggi di ridimensionamento offerti da strumenti di orchestrazione come Kubernetes. I contenitori sono progettati per conoscere solo se stessi. Dopo aver creato più contenitori che devono collaborare, è necessario organizzarli a un livello superiore. L'organizzazione di un numero elevato di contenitori e delle relative dipendenze condivise, come ad esempio la configurazione di rete, è dove gli strumenti di orchestrazione entrano in gioco per risolvere la situazione. Kubernetes crea un livello di astrazione su gruppi di contenitori e li organizza in pod. I pod vengono eseguiti sulle macchine di lavoro denominate nodi. Questa struttura organizzata viene definita cluster. La figura 3-3 mostra i diversi componenti di un cluster Kubernetes.
Figura 3-3. Componenti del cluster Kubernetes.
Il ridimensionamento dei carichi di lavoro in contenitori è una funzionalità chiave degli agenti di orchestrazione dei contenitori. AKS supporta la scalabilità automatica su due fronti: istanze di container e nodi di calcolo. Insieme offrono al servizio Azure Kubernetes la possibilità di rispondere in modo rapido ed efficiente ai picchi di domanda e di aggiungere risorse aggiuntive. Più avanti in questo capitolo viene illustrato il ridimensionamento in AKS.
Dichiarativo e imperativo
Kubernetes supporta la configurazione dichiarativa e imperativa. L'approccio imperativo prevede l'esecuzione di vari comandi per indicare a Kubernetes cosa fare ad ogni passaggio. Esegui questa immagine. Eliminare questo pod. Rendere accessibile questa porta. Con l'approccio dichiarativo, si crea un file di configurazione, denominato manifesto, per descrivere ciò che si vuole invece di cosa fare. Kubernetes legge il manifesto e trasforma lo stato finale desiderato nello stato finale effettivo.
I comandi imperativi sono ideali per l'apprendimento e la sperimentazione interattiva. Tuttavia, sarà necessario creare dichiarativamente i file di manifesto di Kubernetes per adottare un approccio infrastrutturale al codice, garantendo distribuzioni affidabili e ripetibili. Il file manifesto diventa un artefatto di progetto e viene usato nella pipeline CI/CD per automatizzare le distribuzioni kubernetes.
Se il cluster è già stato configurato usando comandi imperativi, è possibile esportare un manifesto dichiarativo usando kubectl get svc SERVICENAME -o yaml > service.yaml
. Questo comando produce un manifesto simile a uno illustrato di seguito:
apiVersion: v1
kind: Service
metadata:
creationTimestamp: "2019-09-13T13:58:47Z"
labels:
component: apiserver
provider: kubernetes
name: kubernetes
namespace: default
resourceVersion: "153"
selfLink: /api/v1/namespaces/default/services/kubernetes
uid: 9b1fac62-d62e-11e9-8968-00155d38010d
spec:
clusterIP: 10.96.0.1
ports:
- name: https
port: 443
protocol: TCP
targetPort: 6443
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
Quando si usa la configurazione dichiarativa, è possibile visualizzare in anteprima le modifiche che verranno apportate prima di eseguirne il commit usando kubectl diff -f FOLDERNAME
nella cartella in cui si trovano i file di configurazione. Quando si è certi di voler applicare le modifiche, eseguire kubectl apply -f FOLDERNAME
. Aggiungere -R
per elaborare in modo ricorsivo una gerarchia di cartelle.
È anche possibile usare la configurazione dichiarativa con altre funzionalità di Kubernetes, una delle quali in fase di distribuzione. Le distribuzioni dichiarative consentono di gestire versioni, aggiornamenti e scalabilità. Istruiscono al controller di distribuzione Kubernetes su come implementare nuove modifiche, aumentare il carico o eseguire il rollback a una revisione precedente. Se un cluster è instabile, una distribuzione dichiarativa restituirà automaticamente il cluster a uno stato desiderato. Ad esempio, se un nodo si arresta, il meccanismo di distribuzione distribuirà un sostituto per ottenere lo stato desiderato.
L'uso della configurazione dichiarativa consente di rappresentare l'infrastruttura come codice che può essere archiviato e sottoposto a controllo delle versioni insieme al codice dell'applicazione. Offre un migliore controllo delle modifiche e un migliore supporto per la distribuzione continua usando una pipeline di compilazione e distribuzione.
Quali scenari sono ideali per contenitori e orchestratori?
Gli scenari seguenti sono ideali per l'uso di contenitori e orchestratori.
Applicazioni che richiedono tempi di attività e scalabilità elevati
Le singole applicazioni con requisiti di tempo di attività e scalabilità elevati sono candidati ideali per le architetture native del cloud che usano microservizi, contenitori e agenti di orchestrazione. Possono essere sviluppati in contenitori, testati in ambienti con controllo delle versioni e distribuiti nell'ambiente di produzione senza tempi di inattività. L'uso dei cluster Kubernetes garantisce che tali app possano anche essere ridimensionate su richiesta e ripristinate automaticamente da errori del nodo.
Numero elevato di applicazioni
Le organizzazioni che distribuiscono e gestiscono un numero elevato di applicazioni traggono vantaggio da contenitori e agenti di orchestrazione. Il lavoro iniziale di configurazione di ambienti in contenitori e cluster Kubernetes è principalmente un costo fisso. La distribuzione, la gestione e l'aggiornamento delle singole applicazioni comportano un costo che varia a seconda del numero di applicazioni. Oltre ad alcune applicazioni, la complessità della gestione manuale delle applicazioni personalizzate supera il costo di implementazione di una soluzione usando contenitori e agenti di orchestrazione.
Quando è consigliabile evitare di usare contenitori e strumenti di orchestrazione?
Se non è possibile compilare l'applicazione seguendo i principi dell'app Twelve-Factor, è consigliabile evitare contenitori e agenti di orchestrazione. In questi casi, prendere in considerazione una piattaforma di hosting basata su vm o eventualmente un sistema ibrido. Con questa tecnologia, è sempre possibile suddividere determinate funzionalità in container separati o in funzioni serverless.
Risorse di sviluppo
Questa sezione illustra un breve elenco di risorse di sviluppo che possono essere utili per iniziare a usare contenitori e agenti di orchestrazione per l'applicazione successiva. Per una guida su come progettare l'applicazione di architettura dei microservizi cloud-native, leggi il libro complementare, .NET Microservices: Architettura per applicazioni .NET in contenitori.
Sviluppo Kubernetes locale
Le distribuzioni Kubernetes offrono un ottimo valore negli ambienti di produzione, ma possono essere eseguite anche in locale nel computer di sviluppo. Anche se è possibile lavorare su singoli microservizi in modo indipendente, può verificarsi un momento in cui è necessario eseguire l'intero sistema in locale, esattamente come verrà eseguito durante la distribuzione nell'ambiente di produzione. Sono disponibili diversi strumenti che possono essere utili: Minikube e Docker Desktop. Visual Studio offre anche strumenti per lo sviluppo di Docker.
Minikube
Che cos'è Minikube? Il progetto Minikube dice "Minikube implementa un cluster Kubernetes locale in macOS, Linux e Windows". Gli obiettivi principali sono "essere lo strumento migliore per lo sviluppo di applicazioni Kubernetes locali e supportare tutte le funzionalità di Kubernetes adatte". L'installazione di Minikube è separata da Docker, ma Minikube supporta hypervisor diversi rispetto al supporto di Docker Desktop. Le funzionalità di Kubernetes seguenti sono attualmente supportate da Minikube:
- Sistema dei Nomi di Dominio (DNS)
- NodePorts
- ConfigMap e segreti
- Pannelli di controllo
- Runtime dei contenitori: Docker, rkt, CRI-O e containerd
- Abilitare la Container Network Interface (CNI)
- Dati in ingresso
Dopo aver installato Minikube, è possibile iniziare rapidamente a usarlo eseguendo il minikube start
comando , che scarica un'immagine e avvia il cluster Kubernetes locale. Una volta avviato il cluster, si interagisce con esso usando i comandi Kubernetes kubectl
standard.
Docker Desktop
È anche possibile usare Kubernetes direttamente da Docker Desktop in Windows. È l'unica opzione se si usano contenitori Windows ed è un'ottima scelta anche per i contenitori non Windows. La figura 3-4 illustra come abilitare il supporto di Kubernetes locale durante l'esecuzione di Docker Desktop.
Figura 3-4. Configurazione di Kubernetes in Docker Desktop.
Docker Desktop è lo strumento più diffuso per la configurazione e l'esecuzione di app in contenitori in locale. Quando si usa Docker Desktop, è possibile sviluppare in locale con lo stesso set esatto di immagini del contenitore Docker da distribuire nell'ambiente di produzione. Docker Desktop è progettato per "compilare, testare e spedire" app in contenitori in locale. Supporta sia i contenitori Linux che Windows. Dopo aver caricato le immagini in un registro, come Azure Container Registry o Docker Hub, AKS può scaricarle e distribuirle nell'ambiente di produzione.
Strumenti docker di Visual Studio
Visual Studio supporta lo sviluppo Docker per applicazioni basate sul Web. Quando si crea una nuova applicazione ASP.NET Core, è possibile configurarla con il supporto docker, come illustrato nella figura 3-5.
Figura 3-5. Abilitare il supporto di Docker in Visual Studio
Quando questa opzione è selezionata, il progetto viene creato con un Dockerfile
nella radice, che può essere usato per compilare e ospitare l'app in un contenitore Docker. Un dockerfile di esempio è illustrato nella figura 3-6.
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY ["eShopWeb/eShopWeb.csproj", "eShopWeb/"]
RUN dotnet restore "eShopWeb/eShopWeb.csproj"
COPY . .
WORKDIR "/src/eShopWeb"
RUN dotnet build "eShopWeb.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "eShopWeb.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "eShopWeb.dll"]
Figura 3-6. Dockerfile generato da Visual Studio
Dopo aver aggiunto il supporto, è possibile eseguire l'applicazione in un contenitore Docker in Visual Studio. La figura 3-7 mostra le diverse opzioni di esecuzione disponibili da un nuovo progetto core ASP.NET creato con il supporto docker aggiunto.
Figura 3-7. Opzioni di esecuzione di Docker in Visual Studio
Inoltre, in qualsiasi momento è possibile aggiungere il supporto Docker a un'applicazione ASP.NET Core esistente. Nell'Esplora soluzioni di Visual Studio, fare clic con il pulsante destro del mouse sul progetto e selezionare Aggiungi>supporto Docker, come illustrato nella figura 3-8.
Figura 3-8. Aggiunta del supporto Docker a Visual Studio
Strumenti Docker di Visual Studio Code
Sono disponibili molte estensioni per Visual Studio Code che supportano lo sviluppo Docker.
Microsoft fornisce l'estensione Docker per Visual Studio Code. Questa estensione semplifica il processo di aggiunta del supporto dei contenitori alle applicazioni. Esegue lo scaffolding dei file necessari, compila immagini Docker e consente di eseguire il debug dell'app all'interno di un contenitore. L'estensione include uno strumento di esplorazione degli oggetti visivi che semplifica l'esecuzione di azioni su contenitori e immagini, ad esempio avvio, arresto, ispezione, rimozione e altro ancora. L'estensione supporta anche Docker Compose che consente di gestire più contenitori in esecuzione come singola unità.