Docker : pourquoi et comment ?
Docker, et la conteneurisation de manière générale, sont devenus pratiques courantes et résolvent bien des problèmes depuis l’avènement de ces technologies : déploiement, isolation des applicatifs, environnement homogène … Si votre application fonctionne sur votre machine avec des conteneurs, elle fonctionnera assurément sur vos environnements de déploiement.
La technologie des conteneurs se rapprochent de la virtualisation tout en étant beaucoup plus légère : un conteneur n’embarque pas forcément un système d’exploitation. De par cette caractéristique très importante, les conteneurs sont ainsi plus appropriés pour rendre portable les applications (d’une machine à une autre ou d’un Cloud à un autre) que les VM.
Ce chapitre traitera d’une des technologies les plus populaires pour la conteneurisation : Docker. Natif à Linux et récemment porté à Windows, Docker est aujourd’hui extrêmement répandu et aide les entreprises dans leurs déploiements. Docker permet également de construire des architectures d’entreprises plus complexe à base de conteneur et ceci grâce aux orchestrateurs. Ce chapitre traitera du plus connu et Open Source : Kubernetes. Avec tout ceci, les architectures microservices prennent de plus en plus d’ampleur dans le monde de l’entreprise. Enfin, ce chapitre traitera d’une méthode afin de concevoir votre architecture microservices.
Les bénéfices de Docker
Docker est une technologie issue du monde Open Source à la base. L’entreprise de la Silicon Valley exploite en effet plusieurs composants du noyau Linux afin de concevoir ses conteneurs (LXC, Libvirt …).
Lancée par le français Solomon Hykes, Docker permet de faciliter les déploiements d’application et la gestion du dimensionnement de l’infrastructure. Cette technologie s’appuie sur une brique d’API standard (LXC sur Linux et Windows Server Container sur Windows) afin de fournir une couche d’abstraction permettant aux conteneurs de s’exécuter sur n’importe quelle machine. Docker à l’avantage d’être bien plus léger d’une machine virtuelle. Le lancement d’un conteneur est également plus rapide ce qui en fait une solution privilégiée pour le déploiement de ses applications.
Docker ne fait pas qu’aider les entreprises dans leurs déploiements, il permet également d’accélérer les évolutions des écosystèmes Cloud, et ça Microsoft, Amazon ou encore Google l’ont bien compris. En effet, avec les conteneurs, il est très facile de déployer les services Cloud on-demand. Par exemple, il est judicieux de conteneuriser les bases de données (MySQL, SQL Server …) afin de les déployer très rapidement selon la demande des utilisateurs.
Cette technologie apporte une réponse concrète à certains scénarios de développement. Par exemple, vous souhaitez tester une nouvelle version d’un Framework sur votre applicatif ? Très simple, Docker est fait de plusieurs images Docker qui s’empilent pour créer votre image Docker, il suffit donc de remplacer la couche souhaitée par la nouvelle version afin d’essayer votre application. Avec une VM, cela devient compliqué de changer de version car il faut désinstaller, puis installer la nouvelle version. Le temps perdu peut être conséquent.
Il en va de même pour la mise en production des applicatifs. Avec Docker, si l’application fonctionne sur votre machine, vous serez certain qu’elle fonctionnera sur l’environnement de production. Docker permet d’encapsuler toutes les dépendances relatives au système d’information, et ceci tout en conservant des systèmes sous-jacents propres (votre machine de développement par exemple). Vous n’avez pas besoin d’installer les dépendances, tout est embarqué dans le conteneur.
Le grand avantage de Docker est donc sa portabilité. Il est tellement portable qu’il est possible de faire tourner des images Docker sur des objets connectés. A partir du moment où des terminaux embarquent des noyaux Linux (Raspbian par exemple sur des Raspberry), il est possible de faire fonctionner Docker sur des objets connectés. Docker propose également depuis longtemps des outils Open Source afin de manipuler des conteneurs et tester des architectures sur des objets connectés : LinuxKit.
Une limite persiste tout de même avec Docker : il n’est pas possible de faire fonctionner des images créées avec Linux sur Windows, et réciproquement. Cependant, les équipes de Docker travail activement avec les équipes de Microsoft pour pallier à ce manque (notamment avec LinuxKit car il faut un minimum de noyau Linux pour faire fonctionner ce genre d’image Docker).
La documentation de Docker explique très clairement comment installer Docker sur Linux et Windows. Sur Linux c’est une série de commande à lancer. Pour Windows, il faut installer Docker for Windows permettant ainsi de disposer de la technologie sur son poste. Une fois installé, on peut tester le CLI via la commande suivante :
> docker info
Cette commande donne accès à la version courante de Docker, mais également à toutes les images et les conteneurs du poste de travail. Afin de tester mieux l’installation, il suffit de lancer la commande suivante qui va lancer une image très simple de test :
> docker run hello-world
L’image n’étant pas sur votre machine (si c’est la première fois), Docker va télécharger l’image et lancer le conteneur. Un message apparaît alors dans la console montrant que l’installation s’est bien passée.
Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world d1725b59e92d: Pulling fs layer d1725b59e92d: Verifying Checksum d1725b59e92d: Download complete d1725b59e92d: Pull complete Digest: sha256:0add3ace90ecb4adbf7777e9aacf18357296e799f81cabc9fde470971e499788 Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears to be working correctly.
La commande suivante permet de vérifier les images qui ont été téléchargés sur votre machine
> docker image ls
Ensuite, cette commande permet de lister les conteneurs qui sont en cours de fonctionnement sur votre machine :
> docker container ls --all
Enfin, pour arrêter un container il suffit de lancer la commande suivante :
> docker stop NOM_DU_CONTENEUR
Un conteneur se construit à partir d’une image. Mais à partir de quoi se construit une image ? Docker se base des fichiers intitulé Dockerfile pour construire les images. Un Dockerfile contient une série d’instruction que Docker va exécuter afin de construire l’image finale qui va être stocké dans un registre d’image Docker. Le site Docker contient son propre registre public : https://hub.docker.com/.
Vous pouvez vous-même publier sur ce registre, beaucoup de grandes compagnies comme Microsoft ont déjà poussé des images Docker sur ce registre afin de que le grand public puisse les utiliser. Sinon, vous pouvez toujours installer un Hub privé sur vos serveurs, ou encore utiliser des services clé-en-main disponible sur Azure ou AWS pour constituer vos Hub privés.
Une image est constituée de couche successive d’autres images Docker. Ainsi, lors de la constitution de son image Docker, on va commencer par s’appuyer sur une autre image afin de bénéficier des commandes lancées précédemment dans cette image. Dans un fichier Dockerfile, la ligne ci-dessous permet de s’appuyer sur une image comportant déjà le Framework .NET Core en version 2.2 :
FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base
La commande FROM permet cette succession de couche d’image Docker. A partir de là, notre image est capable de lancer des projets .NET Core car l’image microsoft/dotnet:2.2-aspnetcore-runtime a déjà installé le runtime .NET Core.
Visual Studio 2017 intègre un template de projet avec un Dockerfile déjà intégré permettant de démarrer rapidement sur un projet ASP.NET Core avec Docker. Nous allons décortiquer le Dockerfile généré avec de mieux comprendre ce que fait Docker :
FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 FROM microsoft/dotnet:2.2-sdk AS build WORKDIR /src COPY ["DockerWeb/DockerWeb.csproj", "DockerWeb/"] RUN dotnet restore "DockerWeb/DockerWeb.csproj" COPY . . WORKDIR "/src/DockerWeb" RUN dotnet build "DockerWeb.csproj" -c Release -o /app FROM build AS publish RUN dotnet publish "DockerWeb.csproj" -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . ENTRYPOINT ["dotnet", "DockerWeb.dll"]
Les trois premières lignes permettent d’exposer le port 80 du conteneur vers l’extérieur. Cela veut dire que lorsque l’application .NET Core va fonctionner, son port 80 va automatiquement être exposer vers l’extérieur, garantissant les échanges entre le monde extérieur et l’application .NET Core fonctionnant à l’intérieur du conteneur.
Les lignes suivantes permettent de copier le .csproj du projet dans le conteneur Docker afin de restaurer les paquets Nuget.
FROM microsoft/dotnet:2.2-sdk AS build WORKDIR /src COPY ["DockerWeb.csproj", "DockerWeb/"] RUN dotnet restore "DockerWeb/DockerWeb.csproj"
Ensuite, les 3 lignes suivantes permettent de copier tous le code source afin de lancer la commande dotnet build à l’intérieur du conteneur.
WORKDIR "/src/DockerWeb" COPY . . RUN dotnet build "DockerWeb.csproj" -c Release -o /app
Enfin, Docker va lancer une publication via la commande dotnet publish, puis va lancer l’applicatif qui en résulte.
FROM build AS publish RUN dotnet publish "DockerWeb.csproj" -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . ENTRYPOINT ["dotnet", "DockerWeb.dll"]
Quelques remarques par rapport à ces dernières lignes :
- FROM build AS publish permet de n’utiliser que l’image avec le SDK .NET Core, c’est-à-dire les commandes de build. Ceci est une micro-optimisation car l’image est plus petite que si on prenait l’image avec tout le SDK .NET Core ;
- FROM base AS final permet d’utiliser uniquement l’image avec le runtime .NET Core pour lancer le projet.
Une fois lancé, on peut s’apercevoir que Docker lance les commandes inscrites dans le Dockerfile sous la forme d’étape qui s’enchaîne dans la console.
Si vous lancez une deuxième fois la commande, on peut s’apercevoir que Docker réutilise le résultat de la dernière build sur chaque couche déjà utilisé. C’est ici toute la puissance de Docker : ce système de cache permet de rapidement construire des images qui peuvent au final être conséquentes.
Au final, Docker indique que la build s’est bien passé. Nous n’avons pas donné de nom à notre image, Docker se charge de donné un identifiant unique pour pouvoir la retrouver.
Cette image est pour le moment sur votre ordinateur, et peut être lancé à tout moment via la commande suivante :
docker run f0568f9b129c
Nous pouvons constater que l’application se lance comme si on l’avait lancé sur le PC, mais ici le serveur fonctionne dans le conteneur Docker.
C’est ici toute la force de Docker, il permet d’encapsuler l’exécution d’une application, et ainsi la rendre portable de machine en machine. Notre application fonctionnant sur notre machine de développement, nous sommes certain que cette application fonctionnera sur un environnement différent supportant Docker (pré-production, production …).