1. Présentation
Microsoft .NET Core est une version Open Source et multiplate-forme de .NET qui peut s'exécuter de manière native dans des conteneurs. .NET Core est disponible sur GitHub et est géré par Microsoft et la communauté .NET. Cet atelier déploie une application .NET Core conteneurisée dans Google Kubernetes Engine (GKE).
Cet atelier suit un modèle de développement typique dans lequel les applications sont développées dans l'environnement local d'un développeur, puis déployées en production. Dans la première partie de l'atelier, un exemple d'application .NET Core est validé à l'aide d'un conteneur exécuté dans Cloud Shell. Une fois validée, l'application est déployée sur Kubernetes à l'aide de GKE. L'atelier inclut les étapes de création d'un cluster GKE.
Dans la deuxième partie de l'atelier, une modification mineure est apportée à l'application, qui affiche le nom d'hôte du conteneur exécutant cette instance d'application. L'application mise à jour est ensuite validée dans Cloud Shell, et le déploiement est mis à jour pour utiliser la nouvelle version. L'illustration suivante montre la séquence d'activités de cet atelier :

Coûts
Si vous effectuez cet atelier exactement comme indiqué, les coûts normaux des services suivants s'appliqueront :
2. Préparation
Prérequis
Pour réaliser cet atelier, vous devez disposer d'un compte et d'un projet Google Cloud. Pour obtenir des instructions plus détaillées sur la création d'un projet, consultez cet atelier de programmation.
Cet atelier utilise Docker exécuté dans Cloud Shell, qui est disponible dans la console Google Cloud et est préconfiguré avec de nombreux outils utiles, tels que gcloud et Docker. Vous trouverez ci-dessous la procédure pour accéder à Cloud Shell. Cliquez sur l'icône Cloud Shell en haut à droite pour l'afficher dans le volet inférieur de la fenêtre de la console.

Autres options de configuration pour le cluster GKE (facultatif)
Cet atelier nécessite un cluster Kubernetes. Dans la section suivante, un cluster GKE avec une configuration simple est créé. Cette section présente quelques commandes gcloud qui fournissent d'autres options de configuration à utiliser lors de la création d'un cluster Kubernetes à l'aide de GKE. Par exemple, à l'aide des commandes ci-dessous, il est possible d'identifier différents types de machines, zones et même GPU (accélérateurs).
- Lister les types de machines avec la commande
gcloud compute machine-types list - Listez les GPU avec la commande
gcloud compute accelerator-types list. - Listez les zones de calcul avec cette commande :
gcloud compute zones list - Obtenir de l'aide sur une commande gcloud
gcloud container clusters --help- Par exemple, vous trouverez des informations sur la création d'un cluster Kubernetes
gcloud container clusters create --help.
- Par exemple, vous trouverez des informations sur la création d'un cluster Kubernetes
Pour obtenir la liste complète des options de configuration de GKE, consultez ce document.
Préparer la création du cluster Kubernetes
Dans Cloud Shell, il est nécessaire de définir certaines variables d'environnement et de configurer le client gcloud. Pour ce faire, utilisez les commandes suivantes.
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}
Créer un cluster GKE
Comme cet atelier déploie l'application .NET Core sur Kubernetes, il est nécessaire de créer un cluster. Utilisez la commande suivante pour créer un cluster Kubernetes dans Google Cloud à l'aide de 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-nodescorrespond au nombre de nœuds à ajouter par zone. Vous pourrez l'adapter ultérieurement.--node-locationsest une liste de zones séparées par une virgule. Dans ce cas, la zone que vous identifiez dans la variable d'environnement ci-dessus etus-central1-bsont utilisées.- REMARQUE : Cette liste ne peut pas contenir de doublons.
--workload-poolétablit l'identité de charge de travail afin que les charges de travail GKE puissent accéder aux services Google Cloud.
Pendant la création du cluster, les éléments suivants s'affichent :
Creating cluster dotnet-cluster in us-central1-b... Cluster is being deployed...⠼
Configurer kubectl
La CLI kubectl est le principal moyen d'interagir avec un cluster Kubernetes. Pour l'utiliser avec le nouveau cluster qui vient d'être créé, il doit être configuré pour s'authentifier auprès du cluster. Pour ce faire, exécutez la commande suivante.
$ gcloud container clusters get-credentials dotnet-cluster --zone ${DEFAULT_ZONE}
Fetching cluster endpoint and auth data.
kubeconfig entry generated for dotnet-cluster.
Vous devriez maintenant pouvoir utiliser kubectl pour interagir avec le 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. Tester localement et confirmer la fonctionnalité souhaitée
Cet atelier utilise les images de conteneur suivantes du dépôt .NET officiel sur Docker Hub.
Exécuter le conteneur localement pour vérifier son fonctionnement
Dans Cloud Shell, vérifiez que Docker est opérationnel et que le conteneur .NET fonctionne comme prévu en exécutant la commande Docker suivante :
$ 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
Confirmer le fonctionnement de l'application Web
Un exemple d'application Web peut également être validé dans Cloud Shell. La commande Docker run ci-dessous crée un conteneur qui expose le port 80 et le mappe sur le port 8080 de localhost. N'oubliez pas que localhost se trouve dans 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
Comme il s'agit d'une application Web, elle doit être consultée et validée dans un navigateur Web. La section suivante explique comment procéder dans Cloud Shell à l'aide de l'aperçu Web.
4. Accéder aux services depuis Cloud Shell à l'aide de l'aperçu sur le Web
Cloud Shell propose la fonctionnalité Aperçu sur le Web, qui permet d'utiliser un navigateur pour interagir avec les processus en cours d'exécution dans l'instance Cloud Shell.
Utiliser "Aperçu sur le Web" pour afficher les applications dans Cloud Shell
Dans Cloud Shell, cliquez sur le bouton d'aperçu Web, puis sélectionnez Prévisualiser sur le port 8080 (ou le port utilisé par l'aperçu Web).

Une fenêtre de navigateur s'ouvre avec une adresse semblable à celle-ci :
https://8080-cs-754738286554-default.us-central1.cloudshell.dev/?authuser=0
Afficher l'exemple d'application .NET à l'aide de l'aperçu sur le Web
L'application exemple lancée à la dernière étape peut désormais être consultée en démarrant l'aperçu Web et en chargeant l'URL fournie. Voici un exemple :

5. Déployer sur Kubernetes
Créer le fichier YAML et l'appliquer
L'étape suivante nécessite un fichier YAML décrivant deux ressources Kubernetes : un déploiement et un service. Créez un fichier nommé dotnet-app.yaml dans Cloud Shell et ajoutez-y le contenu suivant.
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
Utilisez maintenant kubectl pour appliquer ce fichier à Kubernetes.
$ kubectl apply -f dotnet-app.yaml
deployment.apps/dotnet-deployment created
service/dotnet-service created
Notez les messages indiquant que les ressources souhaitées ont été créées.
Explorer les ressources obtenues
Nous pouvons utiliser la CLI kubectl pour examiner les ressources créées ci-dessus. Commençons par examiner les ressources de déploiement et vérifions que le nouveau déploiement est bien là.
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
dotnet-deployment 3/3 3 3 80s
Ensuite, examinez les ReplicaSets. Un ReplicaSet doit être créé par le déploiement ci-dessus.
$ kubectl get replicaset
NAME DESIRED CURRENT READY AGE
dotnet-deployment-5c9d4cc4b9 3 3 3 111s
Enfin, examinez les pods. Le déploiement indiquait qu'il devait y avoir trois instances. La commande ci-dessous devrait indiquer qu'il existe trois instances. L'option -o wide est ajoutée pour que les nœuds sur lesquels ces instances s'exécutent s'affichent.
$ 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>
Examiner la ressource Service
Une ressource Service dans Kubernetes est un équilibreur de charge. Les points de terminaison sont déterminés par les étiquettes des pods. De cette façon, dès que de nouveaux pods sont ajoutés au déploiement via l'opération kubectl scale deployment ci-dessus, les pods résultants sont immédiatement disponibles pour les requêtes traitées par ce service.
La commande suivante doit afficher la ressource de service.
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dotnet-service ClusterIP 10.20.9.124 <none> 8080/TCP 2m50s
...
Vous pouvez afficher plus de détails sur le service à l'aide de la commande suivante.
$ 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>
Notez que le service est de type ClusterIP. Cela signifie que n'importe quel pod du cluster peut résoudre le nom de service dotnet-service en son adresse IP. Les requêtes envoyées au service seront équilibrées sur toutes les instances (pods). La valeur Endpoints ci-dessus indique les adresses IP des pods actuellement disponibles pour ce service. Comparez-les aux adresses IP des pods indiquées ci-dessus.
Vérifier l'application en cours d'exécution
À ce stade, l'application est en ligne et prête à répondre aux requêtes des utilisateurs. Pour y accéder, utilisez un proxy. La commande suivante crée un proxy local qui accepte les requêtes sur le port 8080 et les transmet au cluster Kubernetes.
$ kubectl proxy --port 8080
Starting to serve on 127.0.0.1:8080
Utilisez maintenant l'aperçu sur le Web dans Cloud Shell pour accéder à l'application Web.
Ajoutez /api/v1/namespaces/default/services/dotnet-service:8080/proxy/ à l'URL générée par l'aperçu sur le Web. Le résultat devrait ressembler à ceci :
https://8080-cs-473655782854-default.us-central1.cloudshell.dev/api/v1/namespaces/default/services/dotnet-service:8080/proxy/
Félicitations pour le déploiement d'une application .NET Core sur Google Kubernetes Engine. Nous allons ensuite modifier l'application et la redéployer.
6. Modifier l'application
Dans cette section, l'application sera modifiée pour afficher l'hôte sur lequel l'instance est en cours d'exécution. Cela permettra de confirmer que l'équilibrage de charge fonctionne et que les pods disponibles répondent comme prévu.
Obtenir le code source
git clone https://github.com/dotnet/dotnet-docker.git
cd dotnet-docker/samples/aspnetapp/
Mettez à jour l'application pour inclure le nom d'hôte.
vi aspnetapp/Pages/Index.cshtml
<tr>
<td>Host</td>
<td>@Environment.MachineName</td>
</tr>
Créer une image de conteneur et la tester localement
Créez la nouvelle image de conteneur avec le code mis à jour.
docker build --pull -t aspnetapp:alpine -f Dockerfile.alpine-x64 .
Comme précédemment, testez la nouvelle application en local.
$ 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
Comme précédemment, l'application est accessible à l'aide de l'aperçu sur le Web. Cette fois, le paramètre "Hôte" devrait être visible, comme indiqué ci-dessous :

Ouvrez un nouvel onglet dans Cloud Shell et exécutez docker ps pour vérifier que l'ID du conteneur correspond à la valeur de l'hôte affichée ci-dessus.
$ 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
Ajoutez un tag et transférez l'image pour qu'elle soit disponible pour Kubernetes.
L'image doit être taguée et transférée pour que Kubernetes puisse l'extraire. Commencez par lister les images de conteneur et identifiez celle de votre choix.
$ docker image list
REPOSITORY TAG IMAGE ID CREATED SIZE
aspnetapp alpine 95b4267bb6d0 6 days ago 110MB
Ensuite, ajoutez un tag à cette image et transférez-la vers Google Container Registry. En utilisant l'ID d'IMAGE ci-dessus, cela ressemblera à ceci :
docker tag 95b4267bb6d0 gcr.io/${PROJECT_ID}/aspnetapp:alpine
docker push gcr.io/${PROJECT_ID}/aspnetapp:alpine
7. Redéployer l'application mise à jour
Modifier le fichier YAML
Revenez au répertoire dans lequel le fichier dotnet-app.yaml est enregistré. Recherchez la ligne suivante dans le fichier YAML :
image: mcr.microsoft.com/dotnet/core/samples:aspnetapp
Vous devez modifier cette ligne pour faire référence à l'image de conteneur qui a été créée et transférée vers gcr.io ci-dessus.
image: gcr.io/PROJECT_ID/aspnetapp:alpine
N'oubliez pas de le modifier pour utiliser votre PROJECT_ID. Une fois que vous avez terminé, le résultat devrait ressembler à ceci :
image: gcr.io/myproject/aspnetapp:alpine
Appliquer le fichier YAML mis à jour
$ kubectl apply -f dotnet-app.yaml
deployment.apps/dotnet-deployment configured
service/dotnet-service unchanged
Notez que la ressource de déploiement est mise à jour, tandis que la ressource de service reste inchangée. Vous pouvez voir les pods mis à jour comme avant avec la commande kubectl get pod, mais cette fois, nous allons ajouter -w, qui surveillera toutes les modifications au fur et à mesure qu'elles se produisent.
$ 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
La sortie ci-dessus montre la mise à jour progressive en cours. Tout d'abord, de nouveaux conteneurs sont démarrés. Une fois qu'ils sont en cours d'exécution, les anciens conteneurs sont arrêtés.
Vérifier l'application en cours d'exécution
À ce stade, l'application est mise à jour et prête à répondre aux requêtes des utilisateurs. Comme précédemment, vous pouvez y accéder à l'aide d'un proxy.
$ kubectl proxy --port 8080
Starting to serve on 127.0.0.1:8080
Utilisez maintenant l'aperçu sur le Web dans Cloud Shell pour accéder à l'application Web.
Ajoutez /api/v1/namespaces/default/services/dotnet-service:8080/proxy/ à l'URL générée par l'aperçu sur le Web. Le résultat devrait ressembler à ceci :
https://8080-cs-473655782854-default.us-central1.cloudshell.dev/api/v1/namespaces/default/services/dotnet-service:8080/proxy/
Confirmer que le service Kubernetes distribue la charge
Actualisez cette URL plusieurs fois et notez que l'hôte change à mesure que les requêtes sont équilibrées entre différents pods par le service. Comparez les valeurs "Host" à la liste des pods ci-dessus pour vérifier que tous les pods reçoivent du trafic.
Effectuer un scaling à la hausse des instances
Il est facile de faire évoluer les applications dans Kubernetes. La commande suivante permet de faire passer le déploiement à six instances de l'application.
$ kubectl scale deployment dotnet-deployment --replicas 6
deployment.apps/dotnet-deployment scaled
Vous pouvez afficher les nouveaux pods et leur état actuel à l'aide de cette commande.
kubectl get pod -w
Notez que l'actualisation de la même fenêtre de navigateur montre que le trafic est désormais équilibré sur tous les nouveaux pods.
8. Félicitations !
Dans cet atelier, un exemple d'application Web .NET Core a été validé dans un environnement de développement, puis déployé sur Kubernetes à l'aide de GKE. L'application a ensuite été modifiée pour afficher le nom d'hôte du conteneur dans lequel elle s'exécutait. Le déploiement Kubernetes a ensuite été mis à jour vers la nouvelle version, et l'application a été mise à l'échelle pour montrer comment la charge est répartie sur des instances supplémentaires.
Pour en savoir plus sur .NET et Kubernetes, consultez ces tutoriels. Ils s'appuient sur ce que vous avez appris dans cet atelier en introduisant Istio Service Mesh pour des modèles de routage et de résilience plus sophistiqués.
9. Effectuer un nettoyage
Pour éviter des coûts imprévus, utilisez les commandes suivantes pour supprimer le cluster et l'image de conteneur créés dans cet atelier.
gcloud container clusters delete dotnet-cluster --zone ${DEFAULT_ZONE}
gcloud container images delete gcr.io/${PROJECT_ID}/aspnetapp:alpine