Docker, pour qui, pour quoi ?
Docker est un outil de la mouvance DevOps, mouvement destiné à rapprocher les développeurs des opérationnels.
Du point de vue des développeurs, Docker permet de faire tourner n'importe quel langage, n'importe quelle base de données, dans n'importe quel système Linux. Le développeur gère lui-même les applications qu'il utilise, les librairies, le code... et ne se préoccupe pas du déploiement.
Du point de vue des opérationnels, Docker peut-être lancé sur n'importe quel système Linux, tournant sur n'importe quel cloud, ou type de machine (physique ou virtuel). Ils s'occupent du déploiement, du monitoring, de la configuration réseau...
Docker, la solution... mais à quel problème ?
Pour comprendre l'intérêt de Docker, il faut revenir à ses origines. Docker Inc., anciennement dotCloud, fournit un PaaS. Deux des principales caractéristiques d'un PaaS sont l'isolation, les applications déployées devant être isolées les unes par rapport aux autres ; et la rapidité de déploiement, en effet, si l'on prend l'exemple de l'auto-scaling, lorsqu'une application devient trop chargée, il est nécessaire de démarrer une nouvelle instance. Ainsi, plus cette instance est rapide à déployer, plus le PaaS pourra être réactif et pourra s'adapter à la charge.
Un autre aspect d'un PaaS est la capacité à faire tourner plusieurs types de langages, de bases de données, de serveurs... et ce sur plusieurs types de machines et systèmes d'exploitation. Ce problème est souvent nommé "The matrix Hell", que l'on pourrait traduire en bon français par "La matrice de la mort".
La solution à ce problème consiste donc à packager son application dans un conteneur, qui peut tourner indifféremment sur différents types de plates-formes, sans que l'on ait besoin de savoir ce qui se trouve à l'intérieur (nos applications, nos bases de données...).
Pour ce faire, les ingénieurs de chez dotCloud se sont basés sur certaines nouvelles fonctionnalités du noyau Linux, les namespaces et les Control groups (cgroups), notamment utilisés par LXC, ainsi que AUFS tout en les rendant plus simples à utiliser. Docker est donc la version Open Source (et repackagée) de la solution que dotCloud utilisait originellement pour son propre PaaS.
Un conteneur Linux ?
Je viens d'introduire la notion de conteneur. Pour récapituler, un conteneur est une unité de déploiement logiciel qui permet de faire tourner n'importe quoi (langage, application, base de données...) sur n'importe quel type de matériel (physique ou virtualisé) et de distribution Linux (d'autres types de distribution sont prévues pour les prochaines versions).
D'un point de vue haut niveau, un conteneur Linux est comme une machine virtuelle, mais très légère, garantissant l'isolation complète de son environnement (son propre système de fichier, mémoire, interfaces réseaux, CPU...).
D'un point de vue bas niveau, Docker est comme un chroot dopé aux stéroides. Tout comme les chroot, les conteneurs Linux garantissent l'isolation des processus.
Comment ça marche ?
Docker tire parti des dernières innovations dans le noyau Linux. (C'est pourquoi un noyau Linux récent est nécessaire pour faire tourner Docker).
namespaces
Un système d'exploitation gère des processus, des interfaces réseau, des utilisateurs. Vous pouvez créer ou supprimer des processus, gérer des tables de routage sur vos interfaces réseaux, mettre en place des droits en fonction de vos utilisateurs. Il n'en reste pas moins que toutes ces informations sont partagées et visibles par tous les utilisateurs du système.
Les namespaces vont permettre d'avoir différentes instances indépendantes de toutes ces fonctionnalités. Plusieurs interfaces réseaux avec chacune leur propre table de routage évoluant indépendamment les unes des autres ; des processus isolés et ne pouvant pas communiquer les uns avec les autres. Des utilisateurs partageant des espaces de droit isolés, permettant ainsi d'être root dans un conteneur sans pouvoir être root sur l'hôte.
namespace pid
Chaque namespace pid a sa propre numération pour les processus. Les namespaces pid forment une hiérarchie entre eux. Le kernel conserve l'information permettant de savoir quel namespace a été créé par quel autre namespace. Un namespace parent peut voir les processus de ses enfants mais les enfants ne peuvent ni voir les processus de leur parent, ni ceux de ses frères.
Depuis le namespace pid de plus haut niveau, il est donc possible de voir tous les processus tournant dans tous les namespaces enfants, mais avec des PIDs différents.
namespace mnt
Des processus qui tournent dans des namespaces mnt différents verront des points de montage différents et auront des dossiers racines différents. Cela permet de fournir à chaque conteneur son propre système de fichier, qui ne sera pas visible depuis les autres conteneurs.
namespace net
En séparant les processus dans des namespaces pid différents, et donc des conteneurs différents, il est fort possible de vouloir faire tourner deux processus qui souhaitent écouter sur le même port, deux serveurs Apache par exemple. Hors, un seul des processus va pouvoir écouter sur ce port. Le namespace net va donc permettre d'isoler tout ce qui concerne le réseau.
Ainsi chaque namespace net aura son propre ensemble d'interfaces réseaux, y compris l'interface de loopback lo. Chaque namespace net a également sa propre table de routage et ses propres règles iptables. Afin de pouvoir communiquer entre deux conteneurs, il est possible d'appairer deux interfaces dans deux namespaces différents. Il est également possible de faire communiquer l'interface d'un conteneur avec une interface de l'hôte.
namespace ipc
Les namespaces ipc traitent des communications inter-processus, qui fournissent les sémaphores), les files d'attente de messages et les segments de mémoire partagés). Les namespaces ipc vont donc isoler chaque communication dans son propre conteneur.
namespace user
Le namespace user (prochainement disponible dans LXC) va permettre d'isoler les utilisateurs dans leurs conteneurs, et ainsi de pouvoir être root sur plusieurs conteneurs différents sans pouvoir accéder aux autres conteneurs ou à l'hôte.
namespace uts
Finalement, le namespace uts est sûrement le plus simple, il permet tout simplement à chaque namespace d'avoir son propre hostname.
Pour résumer, les namespaces permettent de créer de vrais conteneurs isolés, que ce soit pour le réseau, les processus ou le système de fichiers.
cgroups
L'isolation des conteneurs mise en place, un problème reste à résoudre. Comment limiter la consommation mémoire, l'utilisation CPU, l'utilisation réseau... de chaque conteneur, afin de garantir à chacun une part du gateau ?
Les cgroups, ou Control groups, permettent de limiter (et de mesurer) l'usage de ces ressources pour un groupe de processus. Ils fonctionnent comme ulimit mais pour un groupe de processus et non pas un seul processus.
Que peut-on contrôler ?
Tout d'abord la mémoire. Les Control groups permettent de limiter la RAM ainsi que le swap utilisé par chaque groupe de processus. Ensuite, à chaque groupe de processus peut être allouée une part de CPU. Sur les systèmes multicoeurs, on peut également définir à quel ensemble de coeurs le groupe de processus peut avoir accès, et ainsi par exemple ne donner accès qu'à deux coeurs sur une machine qui en possède huit. Enfin, on peut également limiter le nombre d'accès disque pour chaque groupe de processus.
Le petit plus : l'accès aux métriques
Le gros bénefice des Control groups est la qualité des métriques. Il est possible de connaître pour chaque groupe de processus (et donc pour chaque conteneur), combien de mémoire il a consommé, combien de cycles CPU il a effectués, ou combien d'IOs (disque ou réseaux) il a effectués.
Pour revenir au cas d'utilisation original de Docker, le PaaS, il peut être intéressant dans cette situation d'avoir accès à toutes ces métriques afin de pouvoir proposer une facturation à l'usage la plus proche de l'usage réel du client.
AUFS
AUFS (Another Union File System) est un système de fichiers de type "Union". L'intérêt d'un tel système de fichiers est de pouvoir fusionner entre elles deux hiérarchies de répertoires. Un cas d'utilisation est de combiner un gros système de fichiers en lecture seule, qui contient une image bootable d'un système, avec un autre répertoire accessible cette fois-ci en écriture. C'est notamment ce qui est fait pour les Live CDs.
L'intérêt pour Docker est donc de pouvoir fusionner une image "base" partagée entre tous les conteneurs avec des modifications qui ont eu lieu sur une instance de conteneur. Cela permet de sauver énormément d'espace disque puisque le système de fichiers d'un conteneur ne contient alors plus que le différentiel par rapport à l'image "base".
En plus d'économiser de l'espace disque, cette solution est également beaucoup plus rapide. En effet, plus besoin de copier l'image entière d'un système, il suffit de créer une nouvelle "pseudo-image" fusionnée avec l'image "base", ce qui prend la plupart du temps moins d'une seconde.
De meilleures performances
Tous les systèmes d'exploitation utilisent des caches pour les accès disques, qui améliorent grandement les performances. AUFS permettant de partager des images de base, les conteneurs Docker accèdent donc souvent au même système de fichiers, et accèdent donc au cache plutôt que de réaliser des accès disques, beaucoup plus lents. Si chaque conteneur utilisait sa propre copie de l'image de base, il ne bénéficierait pas des mises en cache système.
D'autres systèmes de fichiers pour Docker
AUFS apporte d'autres avantages que ceux mentionnés mais a aussi son lot de problèmes. Le principal est sa disponibilité. AUFS n'étant pour l'instant disponible que sur Debian et Ubuntu, il a limité l'adoption de Docker sur d'autres distributions Linux. Heureusement, la version 0.7 de Docker a résolu ce problème en introduisant un système de plugins qui permet d'utiliser différents systèmes de fichiers selon ceux qui sont disponibles sur notre distribution, agrandissant ainsi le cercle des distributions compatibles Docker.
Docker
Docker est donc construit par dessus tous ses composants, et a pour simple vocation de faciliter leur utilisation pour les personnes qui ne sont pas administrateurs système ou spécialistes du noyau Linux. Docker facilite l'utilisation des conteneurs, tout en essayant d'abstraire au maximum les spécifités de chaque distribution afin de faciliter un vrai "write once, run everywhere".
Pour l'instant Docker ne peut tourner que sur des systèmes Unix possédant un noyau récent. L'objectif de Docker est de repackager les composants internes sous forme de plugins, afin de pouvoir, comme dans le cas d'AUFS, les substituer par d'autres technologies compatibles pour chaque OS. Et, pourquoi pas, voir bientôt tourner Docker dans FreeBSD, Solaris ou OS X.