Depuis son lancement en 2013, Docker est un projet qui fait beaucoup parler de lui, et pour de bonnes raisons !

Docker

Ceci est une révolution

En très très bref, Docker est un outil permettant de déployer facilement des applications sur un serveur Linux.

En un peu moins bref, Docker fournit un moyen d’encapsuler des applications dans des containers isolés, et de démarrer ces containers sur n’importe quel machine Linux, quelle que soit la distribution installée. En ce sens, Docker couvre les mêmes besoins qu’une machine virtuelle (VM), mais comme nous allons le voir, il y a tout de même des différences importantes entre containers et VMs.

Alors où est la révolution dans tout ça ? Après tout, n’importe quelle distribution Linux permet d’installer une nouvelle application en quelques clics, ou avec une simple ligne de commande. Mais que se passe-t-il si l’application qu’on souhaite installer n’est pas disponible dans la distribution, ou si seulement une vieille version est disponible ? Il faut alors récupérer un package par ses propres moyens, et c’est généralement là que les ennuis commencent:

  • Le package peut avoir des dépendances incompatibles avec les autres packages déjà installés, et c’est l’impasse…
  • Pire, il se peut qu’aucun package ne soit disponible, il faut alors télécharger le code source et passer des heures à essayer de compiler l’application soi-même (avec un fort risque de retomber sur les problèmes de dépendances)
  • En supposant que l’application démarre, elle n’a jamais été testée et validée sur sa propre machine, et donc des nouveaux bugs peuvent apparaitre. Même avec de la bonne volonté, l’auteur de l’application peut avoir beaucoup de mal à reproduire (et donc à corriger) le problème puisque son environnement de travail est différent.

Containers et union mount

Avec Docker, chaque application est exécutée à l’intérieur d’un container, qui contient l’ensemble des dépendances requises par cette application. Chaque container Docker possède son propre système de fichier, qui embarque l’intégralité d’une distribution Linux (par exemple une Ubuntu), avec les packages nécessaires pour faire tourner l’application. Il n’y a donc plus aucun problème de dépendances, car l’environnement d’exécution est complètement maitrisé et déterministe.

Mais alors si on souhaite lancer plusieurs dizaines (ou centaines) d’applications sur une même machine, l’espace disque ne risque-t-il pas d’exploser, avec toutes ces distributions Linux installées en même temps ? En fait non ! Docker utilise une astuce appelée union mount, qui consiste à découper un système de fichiers en plusieurs couches (layers), et à pouvoir partager ces couches entre plusieurs systèmes de fichiers. En pratique, tous les containers basés sur la même distribution Linux vont en fait partager les mêmes fichiers de base (les bibliothèques et binaires système, les fichiers de configuration, etc.), et seule la différence entre ces containers est stockée sur disque. C’est une des grandes différences entre container Docker et VM: il est littéralement possible de lancer des milliers de containers sur la même machine, tout en utilisant très peu d’espace disque si ces containers sont similaires.

Sandwich

Un autre aspect fondamental du fonctionnement de Docker est qu’un container n’est rien de plus qu’un process Linux (en réalité, un arbre de process) isolé. La conséquence est que démarrer un container revient en fait simplement à exécuter un process, ce qui est pratiquement aussi rapide que lancer ce même process en dehors d’un container. Au contraire d’une VM qui doit lancer un système d’exploitation complet au démarrage, ce qui peut prendre plusieurs dizaines de secondes, le démarrage d’un container Docker est donc quasiment instantané (tout dépend de l’application, évidemment) !

Docker fournit donc un moyen beaucoup plus léger qu’une VM pour encapsuler une application, mais les innovations ne s’arrêtent pas là !

Images, repositories et registry

Une grande idée de Docker est d’avoir repris les mêmes concepts que les systèmes de gestion de versions modernes, comme Git. Le principe est qu’un container Docker est toujours créé à partir d’une image Docker, qui est en quelque sorte un modèle (template) de container. Ces images sont conservées, et versionnées, dans un dépôt (repository) stocké en local sur la machine. Il est donc possible à tout instant de recréer un container Docker à partir d’une image d’une version précédente, de passer facilement d’une version à l’autre, ou même d’exécuter simultanément des containers basés sur des versions différentes.

Là où Docker rejoint encore Git, c’est qu’il est possible d’envoyer des images Docker sur un repository distant, appelé registry, ce qui permet facilement de partager et échanger des images entre plusieurs personnes. De même que Github permet de publier son code source sous Git sur Internet, Docker fournit le Docker Hub, qui est un registry public contenant des milliers d’images Docker prêtent à l’emploi. Il est par exemple possible de télécharger l’image officielle de Wordpress sur le Docker Hub, ce qui permet de lancer Worpdress (avec serveur Web et PHP déjà intégrés) sur sa machine en quelques secondes !

Encore plus fort: le concept de Dockerfile définit un standard simple pour créer une image Docker soi-même. Un Dockerfile n’est rien d’autre qu’un fichier texte, contenant quelques commandes à exécuter pour construire une image Docker à partir d’une autre image (généralement venant du Docker Hub).

Voici par exemple un Dockerfile minimaliste permettant de créer une image Docker Ubuntu contenant un serveur MySQL:

FROM ubuntu:14.04
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get -yq install mysql-server
EXPOSE 3306
CMD ["/usr/bin/mysqld_safe"]

Du container au navire

Le nom de “Docker” vient de l’analogie entre les containers Linux et le transport de marchandises. Grâces aux conteneurs (ceux de la vraie vie, cette fois), il est possible de transporter n’importe quelle marchandise sur n’importe quel bateau, train ou camion, n’importe où dans le monde. Comment ce miracle de la logistique est-il possible ? Tout simplement parce que les conteneurs ont des dimensions standard, ce qui permet de les transporter toujours de la même façon, quel que soit leur contenu. Ainsi, le fournisseur n’a pas à se soucier du mode de transport de ses marchandises, il lui suffit de tout mettre dans un conteneur, et le transporteur fera le reste. De la même façon, le transporteur n’a pas besoin de savoir ce que contiennent les conteneurs pour les transporter, du moment que le standard est respecté.

Cargo

Il se passe exactement la même chose avec les containers Docker, sauf qu’ici, le fournisseur est le développeur de l’application, et le transporteur est l’hébergeur. Il suffit au développeur d’encapsuler son application dans un container Docker, sans se soucier de la plateforme technique de l’hébergeur, et l’application peut être ainsi déployée de la même façon sur la propre machine du développeur, dans un data center privé, dans un cloud public, etc. De son côté, l’hébergeur peut manipuler des applications “enfermées” dans des containers Docker sans se soucier de leur contenu: différentes applications développées en Java, PHP, Python, Go, Node.js pourront donc être déployées et opérées exactement de la même façon sur un même serveur.