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. Questo lab esegue 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 in un ambiente locale per sviluppatori e poi implementate 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 poi eseguita il deployment 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 descritto, 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 illustrato 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 utilizzando GKE. Ad esempio, utilizzando i comandi riportati di seguito è possibile identificare diversi tipi di macchine, zone e persino GPU (acceleratori).
- Elenca i tipi di macchina 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 - Ricevere assistenza per qualsiasi comando gcloud
gcloud container clusters --help- Ad esempio, vengono forniti dettagli sulla creazione di un cluster Kubernetes
gcloud container clusters create --help
- Ad esempio, vengono forniti 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, vengono utilizzati 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 separate da virgole. In questo caso vengono utilizzate la zona identificata nella variabile di ambiente sopra eus-central1-b- NOTA: questo elenco non può contenere duplicati
--workload-poolstabilisce l'identità del workload in modo che i workload GKE possano accedere ai servizi Google Cloud
Durante la creazione del cluster viene visualizzato il seguente messaggio
Creating cluster dotnet-cluster in us-central1-b... Cluster is being deployed...⠼
Configura kubectl
La CLI 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. A tale scopo, utilizza il comando seguente.
$ 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 localmente e conferma la funzionalità desiderata
Questo lab utilizza le seguenti immagini container dal repository .NET ufficiale su Docker Hub.
Esegui il container localmente per verificare la funzionalità
In Cloud Shell, verifica che Docker sia in esecuzione 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
È possibile convalidare anche un'applicazione web di esempio in Cloud Shell. Il comando Docker run riportato di seguito crea un nuovo container che espone la porta 80 e la mappa sulla porta localhost 8080. Ricorda che localhost in questo caso 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. Accedere 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.
Utilizzare "Anteprima web" per visualizzare le app in Cloud Shell
In Cloud Shell, fai clic sul pulsante di anteprima web e scegli "Anteprima sulla porta 8080" (o qualsiasi porta sia impostata per l'utilizzo dell'anteprima web).

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

5. Eseguire 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
Nota 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
Successivamente, dai un'occhiata ai ReplicaSet. Il deployment precedente dovrebbe aver creato un ReplicaSet.
$ kubectl get replicaset
NAME DESIRED CURRENT READY AGE
dotnet-deployment-5c9d4cc4b9 3 3 3 111s
Infine, dai un'occhiata ai pod. Il deployment ha indicato che dovrebbero esserci tre istanze. Il comando riportato di seguito 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 queste 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>
Rivedi la risorsa Servizio
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 riportata sopra, i pod risultanti sono immediatamente disponibili per le richieste gestite da quel servizio.
Il seguente comando dovrebbe mostrare la risorsa di servizio.
$ 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>
Nota che il servizio è di tipo ClusterIP. Ciò significa che qualsiasi pod all'interno del cluster può risolvere il nome del servizio, dotnet-service, nel suo indirizzo IP. Le richieste inviate al servizio verranno bilanciate del carico su tutte le istanze (pod). Il valore Endpoints riportato sopra mostra gli IP dei pod attualmente disponibili per questo servizio. Confronta questi valori con gli IP dei pod visualizzati sopra.
Verificare 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 trasmette 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 al seguente:
https://8080-cs-473655782854-default.us-central1.cloudshell.dev/api/v1/namespaces/default/services/dotnet-service:8080/proxy/
Congratulazioni per il deployment di un'app .NET Core su Google Kubernetes Engine. Ora apportiamo una modifica all'app ed eseguiamo di nuovo il deployment.
6. Modificare 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 e testala 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 in precedenza, è 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 contenitore 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 esegui il push in modo che sia disponibile per Kubernetes.
L'immagine deve essere taggata e inviata per consentire a Kubernetes di eseguirne il pull. Inizia elencando le immagini container e identifica l'immagine che ti interessa.
$ 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 codice sarà simile a questo
docker tag 95b4267bb6d0 gcr.io/${PROJECT_ID}/aspnetapp:alpine
docker push gcr.io/${PROJECT_ID}/aspnetapp:alpine
7. Esegui nuovamente 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 valore deve essere modificato in modo che faccia riferimento all'immagine container creata e inviata a gcr.io sopra.
image: gcr.io/PROJECT_ID/aspnetapp:alpine
Non dimenticare di modificarlo per utilizzare il tuo PROJECT_ID. Al termine, dovrebbe avere un aspetto 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 viene visualizzata come aggiornata e la risorsa Service viene visualizzata come invariata. I pod aggiornati possono essere visualizzati come prima con il comando kubectl get pod, ma questa volta aggiungeremo -w, che monitorerà tutte le modifiche man mano che vengono apportate.
$ 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 precedente mostra l'aggiornamento in sequenza man mano che viene eseguito. Innanzitutto, vengono avviati i nuovi container e, quando sono in esecuzione, vengono terminati quelli precedenti.
Verificare l'app in esecuzione
A questo punto l'applicazione è aggiornata e pronta per le richieste degli utenti. Come in precedenza, è possibile accedervi tramite 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 al seguente:
https://8080-cs-473655782854-default.us-central1.cloudshell.dev/api/v1/namespaces/default/services/dotnet-service:8080/proxy/
Conferma che il servizio Kubernetes distribuisce il carico
Aggiorna questo URL più volte e nota che l'host cambia man mano che le richieste vengono bilanciate tra i diversi pod dal servizio. Confronta i valori di Host con l'elenco dei pod riportato sopra per verificare che tutti i pod ricevano traffico.
Aumentare le istanze
Scalare le app in Kubernetes è facile. Il seguente comando aumenterà 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 viene 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 Kubernetes è stato quindi aggiornato alla nuova versione e l'app è stata scalata per dimostrare come il carico viene distribuito su istanze aggiuntive.
Per saperne 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 non previsti, 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