1. Panoramica
Microsoft .NET Core è una versione open source e multipiattaforma di .NET che può essere eseguita in modo nativo nei container. .NET Core è disponibile su GitHub ed è gestito da Microsoft e dalla community .NET. In questo lab eseguirai il deployment di un'app .NET Core containerizzata in Google Kubernetes Engine (GKE).
Questo lab segue un tipico pattern di sviluppo in cui le applicazioni vengono sviluppate nell'ambiente locale degli sviluppatori e poi ne viene eseguito il deployment in produzione. Nella prima parte del lab, un'app .NET Core di esempio viene convalidata utilizzando un container in esecuzione in Cloud Shell. Una volta convalidata, l'app viene quindi distribuita su Kubernetes utilizzando GKE. Il lab include i passaggi per creare un cluster GKE.
Nella seconda parte del lab, viene apportata una piccola modifica all'app che mostra il nome host del container che esegue l'istanza dell'app. L'applicazione aggiornata viene quindi convalidata in Cloud Shell e il deployment viene aggiornato per utilizzare la nuova versione. La seguente illustrazione mostra la sequenza di attività in questo lab:

Costi
Se esegui questo lab esattamente come scritto, verranno applicati i costi normali per i seguenti servizi
2. Configurazione e requisiti
Prerequisiti
Per completare questo lab, sono necessari un account e un progetto Google Cloud. Per istruzioni più dettagliate su come creare un nuovo progetto, consulta questo codelab.
Questo lab utilizza Docker in esecuzione in Cloud Shell, disponibile tramite la console Google Cloud e preconfigurato con molti strumenti utili, come gcloud e Docker. Di seguito viene mostrato come accedere a Cloud Shell. Fai clic sull'icona di Cloud Shell in alto a destra per visualizzarla nel riquadro inferiore della finestra della console.

Opzioni di configurazione alternative per il cluster GKE (facoltativo)
Questo lab richiede un cluster Kubernetes. Nella sezione successiva viene creato un cluster GKE con una configurazione semplice. Questa sezione mostra alcuni comandi gcloud che forniscono opzioni di configurazione alternative da utilizzare durante la creazione di un cluster Kubernetes con GKE. Ad esempio, utilizzando i comandi riportati di seguito è possibile identificare diversi tipi di macchine, zone e persino GPU (acceleratori).
- Elenca i tipi di macchine con questo comando
gcloud compute machine-types list - Elenca le GPU con questo comando
gcloud compute accelerator-types list - Elenca le zone di computing con questo comando
gcloud compute zones list - Ricevi assistenza su qualsiasi comando gcloud
gcloud container clusters --help- Ad esempio, fornisce dettagli sulla creazione di un cluster Kubernetes
gcloud container clusters create --help
- Ad esempio, fornisce dettagli sulla creazione di un cluster Kubernetes
Per un elenco completo delle opzioni di configurazione per GKE, consulta questo documento
Preparati a creare il cluster Kubernetes
In Cloud Shell, è necessario impostare alcune variabili di ambiente e configurare il client gcloud. A questo scopo, utilizza i seguenti comandi.
export PROJECT_ID=YOUR_PROJECT_ID
export DEFAULT_ZONE=us-central1-c
gcloud config set project ${PROJECT_ID}
gcloud config set compute/zone ${DEFAULT_ZONE}
Crea un cluster GKE
Poiché questo lab esegue il deployment dell'app .NET Core su Kubernetes, è necessario creare un cluster. Utilizza il seguente comando per creare un nuovo cluster Kubernetes in Google Cloud utilizzando GKE.
gcloud container clusters create dotnet-cluster \
--zone ${DEFAULT_ZONE} \
--num-nodes=1 \
--node-locations=${DEFAULT_ZONE},us-central1-b \
--enable-stackdriver-kubernetes \
--machine-type=n1-standard-1 \
--workload-pool=${PROJECT_ID}.svc.id.goog \
--enable-ip-alias
--num-nodesè il numero di nodi da aggiungere per zona e può essere scalato in un secondo momento--node-locationsè un elenco di zone separato da virgole. In questo caso, vengono utilizzate la zona identificata nella variabile di ambiente precedente eus-central1-b- NOTA: questo elenco non può contenere duplicati
--workload-poolstabilisce Workload Identity in modo che i workload GKE possano accedere ai servizi Google Cloud
Durante la creazione del cluster, viene visualizzato quanto segue:
Creating cluster dotnet-cluster in us-central1-b... Cluster is being deployed...⠼
Configura kubectl
L'interfaccia a riga di comando kubectl è il modo principale per interagire con un cluster Kubernetes. Per utilizzarlo con il nuovo cluster appena creato, deve essere configurato per l'autenticazione nel cluster. Per farlo, utilizza il seguente comando.
$ gcloud container clusters get-credentials dotnet-cluster --zone ${DEFAULT_ZONE}
Fetching cluster endpoint and auth data.
kubeconfig entry generated for dotnet-cluster.
Ora dovrebbe essere possibile utilizzare kubectl per interagire con il cluster.
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
gke-dotnet-cluster-default-pool-02c9dcb9-fgxj Ready <none> 2m15s v1.16.13-gke.401
gke-dotnet-cluster-default-pool-ed09d7b7-xdx9 Ready <none> 2m24s v1.16.13-gke.401
3. Esegui test in locale e verifica la funzionalità desiderata
Questo lab utilizza le seguenti immagini container del repository .NET ufficiale su Docker Hub.
Esegui il container in locale per verificare la funzionalità
In Cloud Shell, verifica che Docker sia attivo e funzionante correttamente e che il container .NET funzioni come previsto eseguendo questo comando Docker:
$ docker run --rm mcr.microsoft.com/dotnet/samples
Hello from .NET!
__________________
\
\
....
....'
....
..........
.............'..'..
................'..'.....
.......'..........'..'..'....
........'..........'..'..'.....
.'....'..'..........'..'.......'.
.'..................'... ......
. ......'......... .....
. ......
.. . .. ......
.... . .......
...... ....... ............
................ ......................
........................'................
......................'..'...... .......
.........................'..'..... .......
........ ..'.............'..'.... ..........
..'..'... ...............'....... ..........
...'...... ...... .......... ...... .......
........... ....... ........ ......
....... '...'.'. '.'.'.' ....
....... .....'.. ..'.....
.. .......... ..'........
............ ..............
............. '..............
...........'.. .'.'............
............... .'.'.............
.............'.. ..'..'...........
............... .'..............
......... ..............
.....
Environment:
.NET 5.0.1-servicing.20575.16
Linux 5.4.58-07649-ge120df5deade #1 SMP PREEMPT Wed Aug 26 04:56:33 PDT 2020
Conferma la funzionalità dell'app web
Un'applicazione web di esempio può essere convalidata anche in Cloud Shell. Il comando Docker run riportato di seguito crea un nuovo container che espone la porta 80 e la mappa alla porta 8080 di localhost. Ricorda che in questo caso localhost si trova in Cloud Shell.
$ docker run -it --rm -p 8080:80 --name aspnetcore_sample mcr.microsoft.com/dotnet/samples:aspnetapp
warn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60]
Storing keys in a directory '/root/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.
warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
No XML encryptor configured. Key {64a3ed06-35f7-4d95-9554-8efd38f8b5d3} may be persisted to storage in unencrypted form.
info: Microsoft.Hosting.Lifetime[0]
Now listening on: http://[::]:80
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
Content root path: /app
Poiché si tratta di un'app web, deve essere visualizzata e convalidata in un browser web. La sezione successiva mostra come farlo in Cloud Shell utilizzando l'anteprima web.
4. Accedi ai servizi da Cloud Shell utilizzando "Anteprima web"
Cloud Shell offre l'Anteprima web, una funzionalità che consente di utilizzare un browser per interagire con i processi in esecuzione nell'istanza di Cloud Shell.
Utilizza "Anteprima web" per visualizzare le app in Cloud Shell
In Cloud Shell, fai clic sul pulsante Anteprima web e scegli "Anteprima sulla porta 8080" (o qualsiasi porta impostata per l'utilizzo di Anteprima web).

Si aprirà una finestra del browser con un indirizzo simile al seguente:
https://8080-cs-754738286554-default.us-central1.cloudshell.dev/
Visualizza l'applicazione di esempio .NET utilizzando l'anteprima web
L'app di esempio avviata nell'ultimo passaggio può ora essere visualizzata avviando l'anteprima web e caricando l'URL fornito. Il sito dovrebbe avere il seguente aspetto:

5. Esegui il deployment in Kubernetes
Crea il file YAML e applicalo
Il passaggio successivo richiede un file YAML che descriva due risorse Kubernetes, un deployment e un servizio. Crea un file denominato dotnet-app.yaml in Cloud Shell e aggiungi i seguenti contenuti.
apiVersion: apps/v1
kind: Deployment
metadata:
name: dotnet-deployment
labels:
app: dotnetapp
spec:
replicas: 3
selector:
matchLabels:
app: dotnetapp
template:
metadata:
labels:
app: dotnetapp
spec:
containers:
- name: dotnet
image: mcr.microsoft.com/dotnet/samples:aspnetapp
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: dotnet-service
spec:
selector:
app: dotnetapp
ports:
- protocol: TCP
port: 8080
targetPort: 80
Ora utilizza kubectl per applicare questo file a Kubernetes.
$ kubectl apply -f dotnet-app.yaml
deployment.apps/dotnet-deployment created
service/dotnet-service created
Osserva i messaggi che indicano che le risorse desiderate sono state create.
Esplora le risorse risultanti
Possiamo utilizzare l'interfaccia a riga di comando kubectl per esaminare le risorse create sopra. Innanzitutto, esaminiamo le risorse di deployment e confermiamo che il nuovo deployment sia presente.
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
dotnet-deployment 3/3 3 3 80s
Ora dai un'occhiata ai ReplicaSet. Dovrebbe essere presente un ReplicaSet creato dal deployment precedente.
$ kubectl get replicaset
NAME DESIRED CURRENT READY AGE
dotnet-deployment-5c9d4cc4b9 3 3 3 111s
Infine, dai un'occhiata ai pod. Il deployment indicava che dovevano essere presenti tre istanze. Il comando seguente dovrebbe mostrare che sono presenti tre istanze. L'opzione -o wide viene aggiunta in modo che vengano visualizzati i nodi in cui sono in esecuzione le istanze.
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
dotnet-deployment-5c9d4cc4b9-cspqd 1/1 Running 0 2m25s 10.16.0.8 gke-dotnet-cluster-default-pool-ed09d7b7-xdx9 <none> <none>
dotnet-deployment-5c9d4cc4b9-httw6 1/1 Running 0 2m25s 10.16.1.7 gke-dotnet-cluster-default-pool-02c9dcb9-fgxj <none> <none>
dotnet-deployment-5c9d4cc4b9-vvdln 1/1 Running 0 2m25s 10.16.0.7 gke-dotnet-cluster-default-pool-ed09d7b7-xdx9 <none> <none>
Esamina la risorsa Service
Una risorsa Service in Kubernetes è un bilanciatore del carico. Gli endpoint sono determinati dalle etichette sui pod. In questo modo, non appena vengono aggiunti nuovi pod al deployment tramite l'operazione kubectl scale deployment descritta sopra, i pod risultanti sono immediatamente disponibili per le richieste gestite dal servizio.
Il comando seguente dovrebbe mostrare la risorsa Service.
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dotnet-service ClusterIP 10.20.9.124 <none> 8080/TCP 2m50s
...
È possibile visualizzare ulteriori dettagli sul servizio con il seguente comando.
$ kubectl describe svc dotnet-service
Name: dotnet-service
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=dotnetapp
Type: ClusterIP
IP: 10.20.9.124
Port: <unset> 8080/TCP
TargetPort: 80/TCP
Endpoints: 10.16.0.7:80,10.16.0.8:80,10.16.1.7:80
Session Affinity: None
Events: <none>
Tieni presente che il servizio è di tipo ClusterIP. Ciò significa che qualsiasi pod all'interno del cluster può risolvere il nome del servizio, dotnet-service, nel relativo indirizzo IP. Le richieste inviate al servizio verranno bilanciate con bilanciamento del carico tra tutte le istanze (pod). Il valore Endpoints riportato sopra mostra gli IP dei pod attualmente disponibili per questo servizio. Confrontali con gli IP dei pod restituiti in precedenza.
Verifica l'app in esecuzione
A questo punto, l'applicazione è attiva e pronta per le richieste degli utenti. Per accedervi, utilizza un proxy. Il seguente comando crea un proxy locale che accetta le richieste sulla porta 8080 e le passa al cluster Kubernetes.
$ kubectl proxy --port 8080
Starting to serve on 127.0.0.1:8080
Ora utilizza l'anteprima web in Cloud Shell per accedere all'applicazione web.
Aggiungi quanto segue all'URL generato da Anteprima web: /api/v1/namespaces/default/services/dotnet-service:8080/proxy/. Il risultato sarà simile a questo:
https://8080-cs-473655782854-default.us-central1.cloudshell.dev/api/v1/namespaces/default/services/dotnet-service:8080/proxy/
Complimenti per aver eseguito il deployment di un'app .NET Core su Google Kubernetes Engine. Ora apporteremo una modifica all'app e ne rifaremo il deployment.
6. Modifica l'app
In questa sezione, l'applicazione verrà modificata per mostrare l'host su cui è in esecuzione l'istanza. In questo modo sarà possibile verificare che il bilanciamento del carico funzioni e che i pod disponibili rispondano come previsto.
Recupera il codice sorgente
git clone https://github.com/dotnet/dotnet-docker.git
cd dotnet-docker/samples/aspnetapp/
Aggiorna l'app in modo che includa il nome host
vi aspnetapp/Pages/Index.cshtml
<tr>
<td>Host</td>
<td>@Environment.MachineName</td>
</tr>
Crea una nuova immagine container ed esegui test in locale
Crea la nuova immagine container con il codice aggiornato.
docker build --pull -t aspnetapp:alpine -f Dockerfile.alpine-x64 .
Come prima, testa la nuova applicazione in locale
$ docker run --rm -it -p 8080:80 aspnetapp:alpine
warn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60]
Storing keys in a directory '/root/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.
warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
No XML encryptor configured. Key {f71feb13-8eae-4552-b4f2-654435fff7f8} may be persisted to storage in unencrypted form.
info: Microsoft.Hosting.Lifetime[0]
Now listening on: http://[::]:80
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
Content root path: /app
Come prima, è possibile accedere all'app utilizzando l'anteprima web. Questa volta il parametro Host dovrebbe essere visibile, come mostrato qui:

Apri una nuova scheda in Cloud Shell ed esegui docker ps per verificare che l'ID container corrisponda al valore Host mostrato sopra.
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ab85ce11aecd aspnetapp:alpine "./aspnetapp" 2 minutes ago Up 2 minutes 0.0.0.0:8080->80/tcp relaxed_northcutt
Tagga l'immagine ed eseguine il push in modo che sia disponibile per Kubernetes
L'immagine deve essere taggata e sottoposta a push affinché Kubernetes possa eseguirne il pull. Inizia elencando le immagini container e identifica l'immagine desiderata.
$ docker image list
REPOSITORY TAG IMAGE ID CREATED SIZE
aspnetapp alpine 95b4267bb6d0 6 days ago 110MB
Successivamente, tagga l'immagine ed eseguine il push in Google Container Registry. Utilizzando l'ID IMMAGINE riportato sopra, il comando sarà simile al seguente:
docker tag 95b4267bb6d0 gcr.io/${PROJECT_ID}/aspnetapp:alpine
docker push gcr.io/${PROJECT_ID}/aspnetapp:alpine
7. Rifai il deployment dell'applicazione aggiornata
Modifica il file YAML
Torna alla directory in cui è salvato il file dotnet-app.yaml. Trova la seguente riga nel file YAML
image: mcr.microsoft.com/dotnet/core/samples:aspnetapp
Questo deve essere modificato per fare riferimento all'immagine container creata e di cui è stato eseguito il push in gcr.io in precedenza.
image: gcr.io/PROJECT_ID/aspnetapp:alpine
Non dimenticare di modificarlo per utilizzare il tuo PROJECT_ID. Al termine, l'aspetto dovrebbe essere simile a questo:
image: gcr.io/myproject/aspnetapp:alpine
Applica il file YAML aggiornato
$ kubectl apply -f dotnet-app.yaml
deployment.apps/dotnet-deployment configured
service/dotnet-service unchanged
Nota che la risorsa Deployment mostra lo stato aggiornato e la risorsa Service mostra lo stato invariato. I pod aggiornati possono essere visualizzati come prima con il comando kubectl get pod, ma questa volta aggiungeremo -w, che osserverà tutte le modifiche man mano che si verificano.
$ kubectl get pod -w
NAME READY STATUS RESTARTS AGE
dotnet-deployment-5c9d4cc4b9-cspqd 1/1 Running 0 34m
dotnet-deployment-5c9d4cc4b9-httw6 1/1 Running 0 34m
dotnet-deployment-5c9d4cc4b9-vvdln 1/1 Running 0 34m
dotnet-deployment-85f6446977-tmbdq 0/1 ContainerCreating 0 4s
dotnet-deployment-85f6446977-tmbdq 1/1 Running 0 5s
dotnet-deployment-5c9d4cc4b9-vvdln 1/1 Terminating 0 34m
dotnet-deployment-85f6446977-lcc58 0/1 Pending 0 0s
dotnet-deployment-85f6446977-lcc58 0/1 Pending 0 0s
dotnet-deployment-85f6446977-lcc58 0/1 ContainerCreating 0 0s
dotnet-deployment-5c9d4cc4b9-vvdln 0/1 Terminating 0 34m
dotnet-deployment-85f6446977-lcc58 1/1 Running 0 6s
dotnet-deployment-5c9d4cc4b9-cspqd 1/1 Terminating 0 34m
dotnet-deployment-85f6446977-hw24v 0/1 Pending 0 0s
dotnet-deployment-85f6446977-hw24v 0/1 Pending 0 0s
dotnet-deployment-5c9d4cc4b9-cspqd 0/1 Terminating 0 34m
dotnet-deployment-5c9d4cc4b9-vvdln 0/1 Terminating 0 34m
dotnet-deployment-5c9d4cc4b9-vvdln 0/1 Terminating 0 34m
dotnet-deployment-85f6446977-hw24v 0/1 Pending 0 2s
dotnet-deployment-85f6446977-hw24v 0/1 ContainerCreating 0 2s
dotnet-deployment-5c9d4cc4b9-cspqd 0/1 Terminating 0 34m
dotnet-deployment-5c9d4cc4b9-cspqd 0/1 Terminating 0 34m
dotnet-deployment-85f6446977-hw24v 1/1 Running 0 3s
dotnet-deployment-5c9d4cc4b9-httw6 1/1 Terminating 0 34m
dotnet-deployment-5c9d4cc4b9-httw6 0/1 Terminating 0 34m
L'output riportato sopra mostra l'aggiornamento in sequenza mentre viene eseguito. Innanzitutto, vengono avviati i nuovi container e, quando sono in esecuzione, i container precedenti vengono terminati.
Verifica l'app in esecuzione
A questo punto, l'applicazione è aggiornata e pronta per le richieste degli utenti. Come in precedenza, è possibile accedervi utilizzando un proxy.
$ kubectl proxy --port 8080
Starting to serve on 127.0.0.1:8080
Ora utilizza l'anteprima web in Cloud Shell per accedere all'applicazione web.
Aggiungi quanto segue all'URL generato da Anteprima web: /api/v1/namespaces/default/services/dotnet-service:8080/proxy/. Il risultato sarà simile a questo:
https://8080-cs-473655782854-default.us-central1.cloudshell.dev/api/v1/namespaces/default/services/dotnet-service:8080/proxy/
Verifica che il servizio Kubernetes distribuisca il carico
Aggiorna questo URL più volte e nota che l'host cambia man mano che le richieste vengono bilanciate dal servizio tra i diversi pod. Confronta i valori Host con l'elenco dei pod riportato sopra per verificare che tutti i pod ricevano traffico.
Fai lo scale up delle istanze
La scalabilità delle app in Kubernetes è semplice. Il comando seguente eseguirà lo scale up del deployment fino a 6 istanze dell'applicazione.
$ kubectl scale deployment dotnet-deployment --replicas 6
deployment.apps/dotnet-deployment scaled
I nuovi pod e il loro stato attuale possono essere visualizzati con questo comando
kubectl get pod -w
Nota che l'aggiornamento della stessa finestra del browser mostra che il traffico è ora bilanciato tra tutti i nuovi pod.
8. Complimenti!
In questo lab, un'applicazione web di esempio .NET Core è stata convalidata in un ambiente di sviluppo e successivamente è stato eseguito il deployment in Kubernetes utilizzando GKE. L'app è stata poi modificata per visualizzare il nome host del container in cui era in esecuzione. Il deployment di Kubernetes è stato quindi aggiornato alla nuova versione e l'app è stata sottoposta a scale up per dimostrare come il carico viene distribuito tra le istanze aggiuntive.
Per scoprire di più su .NET e Kubernetes, consulta questi tutorial. Questi si basano su quanto appreso in questo lab introducendo Istio Service Mesh per pattern di routing e resilienza più sofisticati.
9. Esegui la pulizia
Per evitare costi imprevisti, utilizza i seguenti comandi per eliminare il cluster e l'immagine container creati in questo lab.
gcloud container clusters delete dotnet-cluster --zone ${DEFAULT_ZONE}
gcloud container images delete gcr.io/${PROJECT_ID}/aspnetapp:alpine