Docker avec .NET Core


Cet article est le premier d’une série de trois articles :

  • Docker avec .NET Core
  • Cas pratique: créer et déployer un conteneur Docker
  • Aller plus loin avec les conteneurs Docker

Qu’est-ce que Docker ?

Qui n’a pas entendu parler de Docker ? Docker est une mise en oeuvre pour Linux d’une technologie comparable aux machines virtuelles (VM), mais aux applications sans doute encore plus intéressantes.

Comparaison avec une machine virtuelle

Là où une VM se voit allouer une partie d’un système physique, un conteneur se voit partager une partie d’un système. Si une VM dispose de 8 Go de RAM, ces 8 Go lui sont réservés de manière relativement statique. A contrario, un conteneur Docker peut “voir” 8 Go de RAM alors que ces ressources ne lui sont pas dédiées. Je simplifie un peu, mais l’idée est là.

On parle aussi d’OS virtuel par opposition aux machines virtuelles. On appelle également l’OS contenu dans le conteneur le “Guest OS”.

L’avantage est qu’un conteneur est drastiquement plus léger qu’une VM. Un système qui peut exécuter un petit nombre de VM pourrait exécuter un très grand nombre de conteneurs. Et le démarrage de ces conteneurs est également beaucoup plus rapide que le démarrage d’une VM : c’est de l’ordre de quelques secondes (et même moins d’une seconde pour un conteneur “léger”). La technologie des conteneurs est complémentaire aux VM : une VM est davantage isolée qu’un conteneur, et ses ressources sont en quelques sortes garanties, dédiées.

Les VM sont intéressantes lorsque l’on a besoin de “machines”. Par exemple, on peut diviser un serveur physique en 3 VM : un frontal, un applicatif et un serveur de base de données; de façon que chaque serveur dispose de ressources dédiées, dimensionnées de façon très précise.

Les conteneurs sont plus des “espaces” isolés de l’OS, qui partagent ses ressources, exactement comme différents processus applicatifs d’un même système le font.

L’approche des conteneurs

Dans leur philosophie, les conteneurs se positionnent davantage au niveau des applications.

La question que l’on peut se poser est qu’est-ce qui définit une application ? Est-ce le (ou les) fichier exécutable ? Si l’on considère l’application comme l’ensemble de ses constituants pour pouvoir fonctionner, la réponse est plus difficile. Le plus souvent, une application est composée d’un fichier exécutable, de dépendances à d’autres fichiers dans son répertoire, d’une configuration typiquement dans un fichier également, mais aussi d’un ou plusieurs répertoires pour stocker diverses données. Lors du déploiement d’une application .NET, disons un service, il est courant de devoir suivre les étapes suivantes :

  • Copie des fichiers binaires.
  • Installation du service.
  • Configuration (divers chemins d’accès, etc.).
  • Création des répertoires de données de l’application, et gestion des droits d’accès, typiquement avec un compte de service dédié à l’application.

Avec un conteneur, le service serait encapsulé dans celui-ci, pré-configuré avec ses répertoires. Le déploiement, que ce soit le premier ou le centième, consisterait à charger l’image dans Docker et à la lancer. Seules les données applicatives et éventuellement la configuration liée à l’environnement devraient être conservées hors du conteneur. Mais tous les paramètres de configuration propres à l’application, ses fichiers, son cache local, etc., serait eux-mêmes encapsulés dans le conteneur. Les utilisateurs de ce conteneur n’ont plus à se soucier de certains paramètres très spécifiques au seul fonctionnement de l’application et indépendants d’autres applications. Les équipes d’exploitation gèrent la maintenance des conteneurs au lieu de gérer la maintenance des applications. L’exécution du conteneur sera la même que ce soit la première ou la centième fois, sans effet de bord lié à la durée de vie de l’application.

Prenons un autre exemple : imaginons un système applicatif composé de différentes petites applications. Habituellement, les développeurs conçoivent ces différentes applications, les testent, puis les livrent, typiquement sur un autre environnement. La livraison, qu’elle soit automatique ou manuelle, consiste à installer ou copier ces applications, les configurer et les exécuter de façon indépendante. Avec l’approche des conteneurs, les développeurs peuvent encapsuler les applications interdépendantes dans un seul conteneur, tester ce conteneur, puis le livrer avec la garantie que ce qu’ils ont testé est bien ce qui est exécuté sur l’environnement final.

Ces deux approches s’alignent parfaitement avec les architectures basées sur des microservices. Les puristes de Docker ont tendance à privilégier la première approche, à savoir un conteneur par application. Ce choix dépend entièrement de la stratégie de déploiement adoptée dans votre équipe: livrez-vous le plus souvent des mises à jour d’une seule application ou de plusieurs ? Même si les applications sont indépendantes, on peut choisir, par simplicité, de livrer l’ensemble dans un seul conteneur.

Docker a su s’imposer très rapidement et est devenu de fait presqu’un standard pour les conteneurs grâce à sa simplicité de mise en oeuvre. Son fonctionnement tire parti de fonctionnalités propres au noyau Linux, ce qui rend son utilisation encore peu commune sur Windows et a fortiori avec .NET.

Docker avec .NET

Comment fonctionne Docker ?

Docker est un système client / serveur. Le serveur est l’hôte Docker et le client communique avec lui. Le serveur est aussi appelé “Docker Host” ou “Docker Engine”. Nous l’appellons ici hôte Docker.

L’hôte Docker doit être installé sur un système de type Linux. Il peut s’agir d’une VM ou d’un système physique.
Le client Docker, qui permet de piloter l’hôte Docker, peut être installé sur Linux ou sur Windows. Ce client communique avec l’hôte via une API HTTP (type REST).

Qu’est-ce qu’un conteneur ?

Le conteneur est ce qui est exécuté par l’hôte Docker. Il a été créé à partir d’une image. L’image est donc une sorte de code source du conteneur.

La commande docker run hello-world exécutée plus bas dans cet article consistera à télécharger une image nommée “hello-world” et à créer et charger son conteneur dans l’hôte Docker.

L’image Docker est le format d’échange de Docker.

Une image peut être publique ou privée. Elle est publique si elle est partagée sur le Docker Hub (telle l’image “hello-world”). N’importe qui peut partager une image sur le Docker Hub en créant un compte sur cette plateforme. C’est un peu le GitHub des images Docker. Dans la terminologie Docker, le Docker Hub est un registre d’images. Le Docker Hub est automatiquement connu par l’hôte Docker, ce qui explique qu’il ait été si simple de télécharger et charger l’image “hello-world” à partir d’une simple commande. Il est possible de créer son propre registre privé pour y stocker ses images privées. Ce ne sera pas le sujet de cet article.

Le conteneur est simplement une image exécutée. Celui-ci peut contenir un ou plusieurs processus selon ce qui a été configuré dans l’image. Il est possible de lancer plusieurs conteneurs à partir d’une même image, en même temps. L’image est un peu notre application, et le conteneur est l’instance exécutée de l’application.

Docker avec Windows

Vous l’avez deviné : si vous souhaitez héberger un hôte Docker sur un système Windows, vous devrez le faire dans une VM Linux. Il n’y a aucune raison pour que cela ne fonctionne pas bien : le client peut rester sur Windows, et l’hôte est installé dans une VM Linux. Le coût sera celui de la VM. Une VM minimaliste est proposée par Docker (Docker Toolbox).

Que l’hôte Docker soit installé sur Windows (dans une VM) ou sur Linux, il reste à définir si les conteneurs seront des conteneurs Linux ou Windows. Le support des conteneurs Windows arrive avec Windows Server 2016. Une alternative est de configurer une VM Windows dans l’image basée sur Linux. Cependant, ce n’est pas cette solution dont nous parlerons dans cet article.

Docker avec .NET

Il existe une alternative depuis que .NET est passé open source et que son support est annoncé sur Linux : déployer des applications .NET pour Linux en ciblant .NET Core. C’est de cette solution dont nous parlerons dans le reste de cet article. Nous allons tirer parti du nouveau framework .NET Core.

.NET Core

Décrire .NET Core mériterait à lui seul un article. Je vous invite à lire cette introduction à son sujet.

En un mot, .NET Core est un nouveau framework .NET, open source et supporté sur Windows comme sur Linux et Mac OS X. La nouveauté principale est qu’il peut être déployé avec l’application au lieu d’être installé au niveau machine. De ce fait, plusieurs applications d’une même machine peuvent cibler une version différente de .NET Core. Cela vient au prix d’un package applicatif plus volumineux mais très inférieur au framework standard car le framework est éclaté en packages Nuget (à peu près un par DLL du framework standard). L’objectif de .NET Core est de permettre le déploiement d’un même code vers différentes plateformes. De ce fait, le déploiement change légèrement.

Grâce à ce nouveau mode de déploiement, il devient facile de déployer une même application .NET (Core) que ce soit sur Windows, Linux ou OS X, puisqu’on inclut le runtime. Celui-ci n’a pas besoin d’être installé sur la machine pour exécuter l’application.

Nous sommes encore dans les premiers jours de ce nouveau runtime. Ses performances sont pour le moment inférieures au runtime .NET standard. Ne vous étonnez pas si le temps de démarrage de l’application est plus long. Les performances s’amélioreront au fil des mises à jour du nouveau framework. Actuellement, .NET Core est conçu avant tout pour des projets ASP.NET 5. Tout le framework .NET n’est pas porté vers .NET Core.

Pré-requis et préparation pour utiliser Docker

En supposant que vous installez l’hôte Docker sur un système Windows, suivez la première étape du guide Docker pour Windows.

Sachez que si vous souhaitez installer Docker sur Mac OS X, il suffit de suivre la procédure similaire pour Mac. La gestion de Docker sera la même qu’il s’agisse de mac ou de Windows puisque concrètement nous utiliserons toujours le client Docker.

Cela consiste à télécharger la version de Docker Toolbox pour votre plateforme (un package de moins de 200 Mo sur Windows) et à exécuter l’installeur. Sur Windows, celui-ci va installer les éléments suivants :

  • Le client Docker pour Windows,
  • VirtualBox avec une VM Linux contenant l’hôte Docker,
  • des utilitaires annexes (dont Git pour Windows).

Si votre système contient déjà un élément, il est conseillé soit de le désinstaller pour conserver les choix par défaut de l’installeur, soit de modifier les options d’installation.

À la fin de l’installation, lancez le “Docker Quickstart Terminal”.

Lors du premier lancement, Docker configure le système en exécutant notamment la VM de l’hôte Docker. Ensuite, même si vous quittez le terminal (via la commande exit ou en fermant la fenêtre), la VM de l’hôte Docker n’est pas arrêtée. L’ouverture du terminal sera donc immédiate (jusqu’à ce que vous redémarriez la machine physique).

Pour tester la bonne installation, exécutez la commande suivante dans le terminal :

Celle-ci ne fera qu’afficher dans la console un texte indiquant que Docker est fonctionnel, en vous détaillant le processus effectué par cette commande.

Nous sommes arrivé à la fin du premier article de cette série. Le prochain article vous montrera comment créer et déployer un conteneur avec une application .NET Core.

 

 

Commentez cet article