Apprendre à utiliser JetBrains Rider pour programmer des applications ASP.NET Core sur Docker

Ce tutoriel vise à vous apprendre à programmer des applications en ASP.Net Core sur Docker, avec l’EDI Rider de JetBrains.

Vous pouvez le télécharger sur le site officiel de JetBrains Rider.

Un espace de discussion vous est proposé pour recevoir vos avis et suggestions. Commentez Donner une note  l'article (5)

Article lu   fois.

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction

Les conteneurs existent depuis longtemps - je me souviens avoir utilisé une application PHP dans un conteneur Linux en 2004. Cependant, depuis la création de Docker en 2012-2014, il semble qu'ils aient décollé pour de bon. Et lorsque le .NET Core est apparu, nos applications Web C# ont tout à coup pu tout aussi bien fonctionner dans un environnement conteneurisé ! Dans cet article, nous examinerons plusieurs choses : que sont les conteneurs Docker ? Comment pouvons-nous conteneuriser une application ASP.NET Core ? Et comment utiliser un EDI tel que Rider pour créer et déboguer nos applications dans un conteneur Docker ?

II. Que sont les conteneurs Docker ?

Vous êtes probablement familiarisé avec le concept de machine virtuelle (VM), un ordinateur virtualisé qui exécute ses propres systèmes d'exploitation et applications. Les machines virtuelles fournissent un hébergement de densité supérieure, où plusieurs machines virtuelles peuvent partager la mémoire du processeur et les ressources de stockage de leur hôte. Elles offrent également une excellente isolation, fournie par le logiciel d'hyperviseur qui s'exécute sur la machine hôte. Chaque machine virtuelle ayant son propre système d'exploitation, une machine virtuelle Linux peut donc s'exécuter sur le même hôte qu'une machine virtuelle Windows.

Les machines virtuelles ont cependant leurs inconvénients. Il s’agit d’un ordinateur complet (virtuel). Par conséquent, une simple application de 5 Mo nécessite un système d’exploitation de plusieurs Go. Cette machine virtuelle doit être gérée et configurée. Et sa configuration peut être différente de celle des machines de développement, donnant lieu à la célèbre phrase selon laquelle les choses ne fonctionnent pas en production: « mais cela fonctionne sur ma machine! »

Les conteneurs sont différents : au lieu de virtualiser une machine entière, ils virtualisent un système d'exploitation. Ils partagent un système d'exploitation de base commun, sur lequel chaque conteneur peut ajouter ses propres entrées supplémentaires de logiciel, de configuration et de système de fichiers. Ces ajouts sont appelés « couches » et sont décrits dans un fichier Docker. Lorsque nous sommes prêts à exécuter notre application dans un conteneur, le fichier Dockerfile est exécuté et intégré dans une image de conteneur. Cette image peut ensuite être déployée et exécutée en tant que conteneur sur notre machine de développement, ainsi que dans la mise en scène ou la production, ce qui rend l'image très portable.

III. Prérequis et mise en route

Pour commencer à travailler avec Docker, ASP.NET Core et Rider, vous aurez besoin de :

  • Docker Desktop qui peut être téléchargé pour Windows ou Mac, et qui exécutera des conteneurs sur votre propre ordinateur ;
  • .NET Core, que vous devrez développer localement ;
  • Rider, l’IDE de JetBrains que vous utiliserez pour développer, construire et déboguer nos conteneurs.

Lors de l'exécution de Docker sous Windows, vous avez le choix d'exécuter des conteneurs Windows ou Linux. Pour cet article, nous utiliserons des conteneurs Linux. La commutation du système d'exploitation du conteneur peut être effectuée à partir de l'icône Docker dans la barre des tâches.

Docker exécute Docker Engine, processus qui génère et gère les conteneurs sur notre machine. Pour pouvoir gérer ultérieurement ce moteur Docker à partir de notre EDI, nous devrons activer un paramètre dans Docker: expose daemon on tcp://localhost:2375 without TLS – voir la figure 1 :

Image non disponible
Figure 1 : Paramètres généraux de Docker Desktop - Exposer un daemon sans TLS

Laissons .NET pour un instant et commençons notre premier conteneur, juste pour avoir une idée des bases. À partir de la ligne de commande, exécutez :

docker run hello-world

Cela permet de :

  • vérifier le cache sur notre ordinateur local pour voir si l'image « hello-world  » doit être téléchargée ou non ;
  • télécharger l’image si elle n’est pas mise en cache localement ;
  • commencer un nouveau conteneur avec un nom aléatoire, en exécutant ce qui est dans l'image «hello-world».

Nous pouvons voir le nom de ce conteneur, son identifiant et son statut en lançant :

docker container ls –all

Dans Rider, nous pouvons ouvrir la fenêtre de l'outil Docker à partir du menu : « View > Tool> Windows > Docker ». Si vous ne disposez pas de cette entrée, vous devrez peut-être installer le plug-in Docker à partir des paramètres de Rider. La première fois que vous utilisez l’intégration Docker dans Rider, vous devez la configurer pour la connexion à votre démon Docker local.

Dans les paramètres, dans « Build, Execution, Deployment > Docker », vous pouvez ajouter une nouvelle connexion à Docker. Tout ce que vous aurez besoin de configurer, c’est l’URL de l’API du moteur, c’est-à-dire l’hôte cp: // localhost:2375 que vous avez précédemment activé.

Image non disponible
Figure 2: Fenêtre de l'outil Docker dans Riderhello-world

Une fois connecté au moteur Docker, Rider affichera une liste de tous les conteneurs, ainsi que de toutes les images mises en cache localement. Vous pouvez voir le conteneur que vous venez d'exécuter, inspecter son journal et ses propriétés. Vous pouvez également voir l'image «hello-world».

Dans la fenêtre de l'outil Docker, vous pouvez utiliser le menu contextuel et la barre d'outils pour gérer les conteneurs et les images, à l'instar de certains outils de ligne de commande fournis par Docker.

Le conteneur «hello-world» est un conteneur très simple qui génère du texte. Il est construit à partir d'un fichier Dockerfile similaire à ceci :

 
Sélectionnez
FROM scratch
COPY hello /
CMD ["/hello"]

Ce fichier Dockerfile peut être construit comme une image de conteneur qui a une image de base, appelée «scratch», et comporte deux couches supplémentaires, la première copiant un fichier binaire nommé hello dans l’image de conteneur et la seconde couche exécutant ce fichier binaire. Génial ! Mais pas encore très utile. Un fichier Docker typique contient des couches supplémentaires. Pour les applications .NET Core, par exemple, vous réaliseriez une restauration NuGet, puis construiriez votre application, exécuteriez éventuellement des tests unitaires, puis démarreriez l'application. Alors… faisons cela.

Conseil : consultez la documentation sur Docker pour en savoir plus sur son fonctionnement.

IV. Dockérisation d'une application ASP.NET Core

Nous allons donc créer un conteneur Docker pour une application ASP.NET Core existante à partir de zéro. Après avoir ouvert une telle application dans Rider, nous pouvons ajouter un nouveau fichier à la racine de notre projet nommé Dockerfile (sans extension de fichier !). Une fois le ficher, copiez et collez-y le contenu suivant :

 
Sélectionnez
FROM microsoft/dotnet:2.2-sdk
WORKDIR /app

COPY . ./
RUN dotnet restore
RUN dotnet publish -c Release -o out --no-restore

ENTRYPOINT ["dotnet", "out/SampleWebApp.dll"]

Ce Dockerfile utilisera l’image « microsoft / dotnet: 2.2-sdk » (https://hub.docker.com/r/microsoft/dotnet/) comme base.

Examinons chaque ligne séparément :

  • WORKDIR / app crée et définit le répertoire de travail actuel de notre conteneur sur « / app » ;
  • COPY . ./ Copie les fichiers du dossier actuel de notre machine dans le répertoire de travail actuel de notre conteneur ;
  • RUN dotnet restore exécute une commande de restauration de package à l'intérieur du conteneur pour s'assurer que toutes les dépendances de NuGet sont restaurées.
  • RUN dotnet publish -c Release -o out --no-restore construit et publie notre application ASP.NET Core dans le dossier « out ». Puisque nous sommes déjà dans le dossier « / app », cela signifie que notre application publiée ira dans « / app / out ». Nous ajoutons également l'indicateur --no-restore ici, car nous avons déjà restauré les packages NuGet à l'étape précédente.
  • ENTRYPOINT ["dotnet", "out / SampleWebApp.dll"] exécute notre application. Notez que « SampleWebApp.dll » correspond au nom de l’artefact de point d’entrée de votre application.
Image non disponible
Figure 3 : Exécuter le conteneur à partir de Dockerfile à l'aide de JetBrains Rider

Beaucoup de choses vont se passer maintenant. L'image de base « microsoft / dotnet: 2.2-sdk » est téléchargée et chaque étape de notre fichier Dockerfile est également exécutée. Et à la fin, notre conteneur fonctionne et dit qu'il écoute sur le port 80 - ce qui signifie que vous devriez pouvoir y accéder !

Image non disponible
Figure 4 : Rider affiche le journal du conteneur dans la fenêtre de l'outil Docker

Nous n'y sommes pas encore. Un conteneur Docker n'expose aucun port automatiquement au monde extérieur. Le port 80 mentionné ici signifie que si nous nous connectons au conteneur, nous pouvons accéder au port 80, mais pas à partir de notre propre machine de développement ou de n’importe où ailleurs. À partir de l'onglet Liaisons de port, nous pouvons ajouter une nouvelle liaison de port. L’idée est que nous exposerons le port 80 du conteneur, par exemple sur le port 8080 de notre machine locale:

Image non disponible
Figure 5 : exposition d'un port de conteneur sur l’hôte

Une fois que nous avons enregistré, Rider met à jour le conteneur et nous pouvons maintenant accéder à http:// localhost:8080 et voir notre application ASP.NET Core en action - servie depuis notre conteneur Docker. Nous pourrions publier cette image de conteneur dans un référentiel, puis exécuter l'application sur une machine de production ou ailleurs.

Il serait préférable de toujours exposer le port 80 dans le conteneur lorsque nous l’exécutons sur notre machine pour ne pas avoir à le configurer à chaque fois. Depuis le menu « Run > Edit Configurations… », nous pouvons configurer les arguments transmis au moteur Docker chaque fois que nous exécutons notre conteneur à partir de Rider.

Image non disponible
Figure 6 : Exécuter les paramètres de configuration

Faisons quelques changements ici :

  • le nom du conteneur peut être défini sur le nom de notre application. Par défaut, Docker démarre notre application dans un nouveau conteneur avec un nom aléatoire, et pour conserver un aperçu de la liste des conteneurs en cours d'exécution et arrêtés, le nom de notre application ne fait jamais de mal ;
  • Bind ports est l’emplacement où nous pouvons à nouveau configurer le port 80 de notre conteneur pour qu’il soit mappé sur le port hôte 8080.

Après avoir sauvegardé, nous pouvons maintenant utiliser cette configuration d’exécution pour démarrer notre conteneur ultérieurement.

Remarque : lors de la tentative d'exécution d'un fichier Docker généré avec Visual Studio, il est fort probable que l'exécution du conteneur risque de ne pas fonctionner immédiatement, généralement avec une erreur similaire à

Failed to deploy '<unknown> Dockerfile: SampleWebApp / Dockerfile': COPY failed: stat /var/lib/docker/tmp/docker-builder109565895/SampleWebApp/SampleWebApp.csproj:no such file or directory.

La configuration d'exécution dans Rider tente toujours de générer le conteneur à partir du répertoire du projet, alors que le fichier Dockerfile généré par Visual Studio est le conteneur à construire à partir de notre dossier de solutions. Nous pouvons résoudre ce problème en définissant le dossier de contexte dans notre configuration d'exécution sur « . » (Un seul point).

V. Conteneurs Docker à plusieurs étages

Bien que notre conteneur fonctionne, il est actuellement trop gros. Sa base contient l'intégralité du Kit de développement .NET Core SDK, où notre application fonctionnerait correctement avec juste l'exécution. Et nous copions également toutes les sources de notre projet dans notre image de conteneur, où seul l’assemblage suffit pour pouvoir l’exécuter.

Idéalement, la partie de génération .NET Core de notre image de conteneur serait distincte de la partie d'exécution. Nous pouvons utiliser des conteneurs multi-étapes pour cela ! Remplaçons notre fichier Docker par ce qui suit :

 
Sélectionnez
FROM microsoft/dotnet:2.2-sdk AS build
WORKDIR /app

# Restoring packages can be done from just csproj
COPY *.csproj ./
RUN dotnet restore

# Copy everything else to build/publish
COPY . ./
RUN dotnet publish -c Release -o out --no-restore

# Build runtime image containing build/publish output
FROM microsoft/dotnet:2.2-aspnetcore-runtime
WORKDIR /app
COPY --from=build /app/out .
ENTRYPOINT ["dotnet", "SampleWebApp.dll"]

Ce fichier Dockerfile est maintenant multi-étapes (notez les deux FROM ici). La première étape utilise comme base l’image « microsoft / dotnet: 2.2-sdk » complète, qui contient l’ensemble du SDK .NET Core complet, ainsi que tous les outils de construction. Notez l'AS où nous appelons cette étape comme « build ». La deuxième étape utilise comme base une image plus fine « microsoft / dotnet: 2.2-aspnetcore-runtime », contenant tout le nécessaire pour exécuter une application ASP.NET Core, mais pas les outils de construction, etc.

Voici la procédure complète pour la première étape :

  • WORKDIR / app crée et définit le répertoire de travail actuel dans notre conteneur de construction sur « / app » ;
  • COPY * .csproj ./ copie uniquement le fichier .csproj dans notre conteneur de génération, car ce dernier est tout ce dont nous avons besoin pour restaurer le paquet NuGet ;
  • RUN dotnet restore exécute une commande de restauration de package à l'intérieur du conteneur pour s'assurer que toutes les dépendances de NuGet sont restaurées ;
  • COPIE . ./ copie les fichiers du dossier actuel de notre machine dans le répertoire de travail actuel de notre conteneur. Cela est fait, car nous avons besoin de tout le code source pour effectuer la construction ;
  • lancez dotnet publish -c Release -o out --no-restore. Construit et publie notre application ASP.NET Core dans le dossier « out ». Puisque nous sommes déjà dans le dossier « / app », cela signifie que notre application publiée ira dans « / app / out ». Nous ajoutons également l'indicateur --no-restore ici, car nous avons déjà restauré les packages NuGet à l'étape précédente.

Et voici ce qui se passe dans la deuxième étape:

  • WORKDIR / app -crée et définit le répertoire de travail actuel de notre conteneur d'exécution sur « / app » ;
  • COPY --from = build / app / out copie le dossier « / app / out » de notre première étape (nommée build) dans le répertoire de travail actuel de notre conteneur. Cela est fait, car nous avons besoin de tout le code source pour effectuer la construction.
  • ENTRYPOINT ["dotnet", "SampleWebApp.dll"] - Exécute notre application. Notez que «SampleWebApp.dll» correspond au nom de l’artefact de point d’entrée de votre application.

Nous pouvons construire ce conteneur et nous verrons que tout continue à bien fonctionner. La taille de l'image de conteneur résultante sera toutefois beaucoup plus petite, car elle ne contient que les artefacts d'exécution et publiés.

Il y a une chose supplémentaire à noter ici : dans le fichier Dockerfile, l'étape de restauration utilise uniquement le fichier .csproj, tandis que l'étape de génération utilise toutes les sources. Avec .NET Core, le fichier .csproj ne change généralement que lorsque nous le modifions manuellement ou que nous ajoutons des dépendances supplémentaires. Sauf modification de notre fichier .csproj, Docker peut mettre en cache toutes les étapes antérieures aux étapes de copie et de construction, ce qui accélérera la création globale de l'image du conteneur.

VI. Débogage du noyau ASP.NET dans Docker

Pour le moment, nous avons créé un fichier Dockerfile, exposé un port depuis l'intérieur de notre conteneur, configuré une image en plusieurs étapes et nous sommes en mesure de le créer et de l'exécuter sur notre machine. Il manque encore une chose : comment déboguer l’application que nous construisons ?

Puisque nous avons déjà une configuration d'exécution pour notre conteneur, tout ce que nous avons à faire est d'utiliser la commande Run | Debugermenu au lieu de simplement Exécuter. Cela lancera notre conteneur et attachera le débogueur à notre application en cours d'exécution à l'intérieur de celui-ci. Tout ce qui reste à faire est de définir un point d'arrêt n'importe où dans notre base de code. Une fois que nous aurons accédé à notre application, nous pourrons le déboguer.

Image non disponible
Figure 7 : Débogage d'une application ASP.NET Core s'exécutant dans Docker à l'aide de Rider

Lorsqu'un point d'arrêt est atteint, nous pouvons parcourir notre code, inspecter les variables, consulter la pile d'appels - tout ce que nous ferions lors du débogage d'une application s'exécutant sur notre propre ordinateur. Sur la figure 7, nous pouvons voir que le système d'exploitation actuel est répertorié sous Unix, alors que nous utilisons Rider sous Windows. Cela fonctionne sur ma machine ? Cela fonctionne sur toutes les machines !

Et après ?

Dans cet article, nous avons simplement abordé les bases des conteneurs et de Docker. Bien que nous puissions maintenant utiliser Rider pour développer des applications ASP.NET Core sur Docker, il reste encore beaucoup à apprendre !

Docker Compose est un outil qui permet de définir plusieurs conteneurs et leur interaction. Par exemple, nous pouvons définir un conteneur qui exécute notre application, un autre qui exécute SQL Server pour Linux, puis les faire communiquer. Cette approche nous permet de créer un environnement de développement identique à l'environnement de production, à la différence que tous les conteneurs s'exécutent sur notre moteur Docker local au lieu d'un cluster de production.

L’information fournie dans l'orientation Mise en route / Docker de Docker est bien plus détaillée : elle couvre les commandes supplémentaires de Docker, la création de services, les nuées où les conteneurs peuvent être déployés de manière plus résiliente, etc.

Et puis, il y a Kubernetes, un orchestrateur de conteneurs qui permet de déployer des conteneurs sur un cluster, de gérer les basculements, les limites de ressources, etc.

Essayez Rider. C'est un EDI NET complet qui peut être utilisé sous Windows, Mac OS X et Linux, et s'intègre parfaitement aux conteneurs de ciblage de développement. Une version d’essai gratuite est disponible sur https://www.jetbrains.com/rider.

VII. A propos de l’auteur

Image non disponible

Maarten Balliauw aime créer des applications Web et Cloud. Ses principaux centres d’intérêts sont les technologies Web .NET, C#, Microsoft Azure et les performances des applications. Il est Developer Advocate chez JetBrains, ASP Insider et ancien MVP de Microsoft. Maarten intervient fréquemment lors d’événements nationaux et internationaux et organise des événements pour les groupes d'utilisateurs Azure en Belgique. Pendant ses temps libres, il brasse sa propre bière.

VIII. Remerciements Developpez.com

Developpez.com remercie JetBrains et Maarten Biallauw pour l’autorisation de publication de ce tutoriel, initialement publié sur codemag.com. Tous les remerciements aussi à Guillaume SIGUI pour la mise au gabarit et Malick pour la relecture orthographique.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2019 JetBrains. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.