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 classique 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 pour afficher le nom d'hôte du conteneur qui exécute 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 suivez exactement les instructions de cet atelier, 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 via la console Google Cloud et est préconfiguré avec de nombreux outils utiles, tels que gcloud et Docker. La procédure d'accès à Cloud Shell est présentée ci-dessous. 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 des 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, les commandes ci-dessous permettent d'identifier différents types de machines, zones et même GPU (accélérateurs).
- Répertoriez les types de machines à l'aide de la commande
gcloud compute machine-types list. - Répertoriez les GPU à l'aide de la commande
gcloud compute accelerator-types list. - Listez les zones de calcul à l'aide de la commande
gcloud compute zones list. - Obtenir de l'aide sur n'importe quelle commande gcloud
gcloud container clusters --help- Par exemple, vous trouverez ici des informations sur la création d'un cluster Kubernetes
gcloud container clusters create --help.
- Par exemple, vous trouverez ici des informations sur la création d'un cluster Kubernetes
Pour obtenir la liste complète des options de configuration pour GKE, consultez ce document.
Préparer la création du cluster Kubernetes
Dans Cloud Shell, vous devez définir des variables d'environnement et configurer le client gcloud. Pour ce faire, exécutez 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, vous devez 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-nodesest le nombre de nœuds à ajouter par zone. Vous pourrez le modifier 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 Workload Identity afin que les charges de travail GKE puissent accéder aux services Google Cloud.
Pendant la création du cluster, le message suivant s'affiche :
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éé, vous devez le configurer pour qu'il s'authentifie 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
Vous pouvez également valider un exemple d'application Web dans Cloud Shell. La commande d'exécution Docker 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 dans ce cas.
$ 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 affichée et validée dans un navigateur Web. La section suivante explique comment procéder dans Cloud Shell à l'aide de l'aperçu sur le 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 exécutés dans l'instance Cloud Shell.
Utiliser "Aperçu sur le Web" pour afficher des applications dans Cloud Shell
Dans Cloud Shell, cliquez sur le bouton "Aperçu sur le Web", puis sélectionnez Prévisualiser sur le port 8080 (ou sur le port défini pour l'aperçu sur le Web).

Une fenêtre de navigateur s'ouvre avec une adresse semblable à celle-ci :
https://8080-cs-754738286554-default.us-central1.cloudshell.dev/
Afficher l'exemple d'application .NET à l'aide de l'aperçu sur le Web
Vous pouvez maintenant afficher l'application exemple que vous avez lancée à l'étape précédente en démarrant l'aperçu sur le 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érifier que le nouveau déploiement est bien présent.
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
dotnet-deployment 3/3 3 3 80s
Examinez ensuite les ReplicaSets. Un ReplicaSet doit avoir été 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 y a trois instances. L'option -o wide est ajoutée pour que les nœuds sur lesquels ces instances sont exécutées soient affichés.
$ 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. Ainsi, 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 gérées par ce service.
La commande suivante doit afficher la ressource 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 du service, dotnet-service, en son adresse IP. Les requêtes envoyées au service seront équilibrées entre 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 qui ont été générées ci-dessus.
Vérifier l'application en cours d'exécution
À ce stade, l'application est active et prête à recevoir les 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 l'élément suivant à l'URL générée par l'aperçu sur le Web : /api/v1/namespaces/default/services/dotnet-service:8080/proxy/. 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 ! Vous avez déployé une application .NET Core sur Google Kubernetes Engine. Nous allons maintenant 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 exécutée. 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/
Mettre à 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 "Host" devrait être visible, comme illustré ici :

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 indiqué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
Ajouter un tag à l'image et la transférer 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 conteneurs et identifiez l'image souhaitée.
$ 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 valeur pour référencer l'image de conteneur 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 Deployment est indiquée comme mise à jour, tandis que la ressource Service est indiquée comme inchangée. Les pods mis à jour peuvent être affichés comme précédemment avec la commande kubectl get pod, mais cette fois, nous ajouterons l'option -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
Le résultat 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, il est accessible via 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 l'élément suivant à l'URL générée par l'aperçu sur le Web : /api/v1/namespaces/default/services/dotnet-service:8080/proxy/. Le résultat devrait ressembler à ceci :
https://8080-cs-473655782854-default.us-central1.cloudshell.dev/api/v1/namespaces/default/services/dotnet-service:8080/proxy/
Vérifier que le service Kubernetes répartit 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 d'hôte à 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 des applications dans Kubernetes. La commande suivante va 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é entre 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 fait l'objet d'un scaling à la hausse pour montrer comment la charge est répartie entre les 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 le maillage de services Istio pour des modèles de routage et de résilience plus sophistiqués.
9. Effectuer un nettoyage
Pour éviter des frais involontaires, 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