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 di sviluppo e poi distribuite in produzione. Nella prima parte del lab, viene convalidata un'app .NET di esempio utilizzando un container in esecuzione in Cloud Shell. Una volta convalidata, viene eseguito il deployment dell'app in Kubernetes mediante 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 su cui è in esecuzione l'istanza dell'app. L'applicazione aggiornata viene quindi convalidata in Cloud Shell e il deployment viene aggiornato per utilizzare la nuova versione. L'illustrazione seguente mostra la sequenza di attività in questo lab:
Costi
Se esegui questo lab esattamente come è scritto, verranno applicati i normali costi 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.
In questo lab viene utilizzato Docker in esecuzione in Cloud Shell, che è disponibile tramite la console Google Cloud ed è preconfigurato con molti strumenti utili, come gcloud e Docker. Di seguito è mostrato l'accesso 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 mediante GKE. Ad esempio, utilizzando i comandi riportati di seguito è possibile identificare diversi tipi di macchina, 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
- Ottieni assistenza per qualsiasi comando gcloud
gcloud container clusters --help
- Ad esempio, fornisci dettagli sulla creazione di un cluster Kubernetes
gcloud container clusters create --help
- Ad esempio, fornisci 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. Per farlo, usa i comandi seguenti.
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 questo 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 indicata sopra eus-central1-b
- NOTA: questo elenco non può contenere duplicati
--workload-pool
stabilisce l'identità dei carichi di lavoro in modo che i carichi di lavoro 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 di interagire con un cluster Kubernetes. Per utilizzarlo con il nuovo cluster appena creato, deve essere configurato per l'autenticazione a fronte del cluster. Per farlo, deve seguire 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 i test in locale e conferma la funzionalità desiderata
Questo lab utilizza le seguenti immagini container del repository .NET ufficiale nell'hub Docker.
Esegui il container in locale per verificare la funzionalità
In Cloud Shell, verifica che Docker sia attivo e 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
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 lo mappa alla 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 Anteprima web.
4. Accesso ai servizi da Cloud Shell utilizzando "Anteprima web"
Cloud Shell offre 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 dell'anteprima web e scegli "Anteprima sulla porta 8080" (o qualsiasi porta sia impostata per l'utilizzo da parte dell'Anteprima web).
Si aprirà una finestra del browser con un indirizzo simile al seguente:
https://8080-cs-754738286554-default.us-central1.cloudshell.dev/?authuser=0
Visualizza l'applicazione di esempio .NET utilizzando Anteprima web
Ora è possibile visualizzare l'app di esempio avviata nell'ultimo passaggio 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 applica
Il passaggio successivo richiede un file YAML che descrive due risorse Kubernetes: un deployment e un Service. Crea un file denominato dotnet-app.yaml
in Cloud Shell e aggiungici i contenuti seguenti.
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 sono state create le risorse desiderate.
Esplora le risorse risultanti
Possiamo utilizzare l'interfaccia a riga di comando kubectl
per esaminare le risorse create sopra. Innanzitutto, diamo un'occhiata alle risorse del deployment e verifichiamo che il nuovo deployment sia già 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 di cui sopra.
$ 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 seguente dovrebbe mostrare che sono presenti tre istanze. Viene aggiunta l'opzione -o wide
in modo che i nodi in cui sono in esecuzione queste istanze vengano mostrati.
$ 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 del servizio
Una risorsa Servizio in Kubernetes è un bilanciatore del carico. Gli endpoint sono determinati da 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 da quel 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
...
Usa il comando seguente per visualizzare ulteriori dettagli sul servizio.
$ 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 relativo indirizzo IP. Le richieste inviate al servizio verranno bilanciate in tutte le istanze (pod). Il valore Endpoints
riportato sopra mostra gli IP dei pod attualmente disponibili per questo servizio. Confronta queste informazioni con gli IP dei pod di cui è stato eseguito l'output sopra.
Verifica l'app in esecuzione
A questo punto l'applicazione è attiva e pronta per le richieste degli utenti. Per accedervi, utilizza un proxy. Il comando seguente 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 Web Preview in Cloud Shell per accedere all'applicazione web.
Aggiungi il seguente 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/
Complimenti per aver eseguito il deployment di un'app .NET Core in Google Kubernetes Engine. Ora apporteremo una modifica all'app ed eseguire di nuovo 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 confermare che il bilanciamento del carico funziona e che i pod disponibili rispondono 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 eseguine 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 localmente
$ 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 tramite 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 vedere che l'ID contenitore corrisponde 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 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
Poi, tagga l'immagine ed eseguine il push in Google Container Registry. Utilizzando l'ID IMAGE sopra, apparirà come questo
docker tag 95b4267bb6d0 gcr.io/${PROJECT_ID}/aspnetapp:alpine
docker push gcr.io/${PROJECT_ID}/aspnetapp:alpine
7. Esegui di nuovo 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 che è stata creata e sottoposta a push a gcr.io qui sopra.
image: gcr.io/PROJECT_ID/aspnetapp:alpine
Non dimenticare di modificarlo per utilizzare PROJECT_ID
. Al termine, 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
Tieni presente che la risorsa Deployment viene visualizzata come aggiornata, mentre la risorsa Service non è stata modificata. I pod aggiornati possono essere visualizzati come prima con il comando kubectl get pod
, ma questa volta aggiungeremo il valore -w
, che osserva 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 riportato sopra mostra l'aggiornamento in sequenza nel momento in cui si verifica. Per prima cosa vengono avviati i nuovi container e, quando sono in esecuzione, quelli precedenti vengono terminati.
Verifica l'app in esecuzione
A questo punto l'applicazione è aggiornata e pronta per le richieste dell'utente. Come prima, è possibile accedervi utilizzando un proxy.
$ kubectl proxy --port 8080
Starting to serve on 127.0.0.1:8080
Ora utilizza Web Preview in Cloud Shell per accedere all'applicazione web.
Aggiungi il seguente 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/
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 in diversi pod dal servizio. Confronta i valori Host con l'elenco dei pod in alto per vedere che tutti i pod ricevono traffico.
Fai lo scale up delle istanze
La scalabilità delle app in Kubernetes è facile. Il comando seguente consentirà di scalare il deployment fino a un massimo di 6 istanze dell'applicazione.
$ kubectl scale deployment dotnet-deployment --replicas 6
deployment.apps/dotnet-deployment scaled
Con questo comando è possibile visualizzare i nuovi pod e il loro stato attuale
kubectl get pod -w
Nota che l'aggiornamento della stessa finestra del browser mostra che il traffico viene ora bilanciato in tutti i nuovi pod.
8. Complimenti!
In questo lab, un'applicazione web di esempio .NET Core è stata convalidata in un ambiente di sviluppo e di cui è stato eseguito il deployment in Kubernetes utilizzando GKE. L'app è stata poi modificata in modo da visualizzare il nome host del container in cui era in esecuzione. Il deployment di Kubernetes è stato quindi aggiornato alla nuova versione ed è stato fatto lo scale up dell'app per dimostrare come il carico viene distribuito tra le istanze aggiuntive.
Per saperne di più su .NET e Kubernetes, considera questi tutorial. 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 indesiderati, utilizza i comandi seguenti per eliminare il cluster e l'immagine container che sono state create in questo lab.
gcloud container clusters delete dotnet-cluster --zone ${DEFAULT_ZONE}
gcloud container images delete gcr.io/${PROJECT_ID}/aspnetapp:alpine