Un aperçu des composants des moteurs de conteneurs
Sommaire
Histoire des moteurs de conteneurs
Mes connaissances sur l’histoire de la conteneurisation sont assez réduites, car je m’y suis mis assez tardivement. J’ai commencé à me renseigner sur LXC lors de la sortie de la version 2, autour de 2017, alors que Docker venait d’abandonner le support de LXC et était déjà passé sur runc. En m’intéressant à LXC j’ai découvert le travail impressionnant de Stéphane Graber et Christian Brauner sur l’implémentation des espaces de noms du noyau Linux. C’est, selon moi, le travail auquel on doit la paternité de la conteneurisation, bien que FreeBSD avait déjà l’appel système, très ressemblant à ce que font les espaces de noms sous Linux, jail dans les années 2000 (Linux introduira un appel système équivalent, unshare, qu’en 2006, avec la sortie de la version 2.6.16).
Prenons la création de Docker comme point de référence historique.
Avant Docker, il y avait LXC, qui était capable d’isoler des processus et qui disposait d’une interface en ligne de commande. LXC couvrait 100% des capacités d’isolation fournies par les espaces de noms de Linux, ce qui en fit un candidat idéal pour l’intégrer comme composant des premières versions de Docker. Ce dernier a apporté ce qu’il manquait à LXC ; la gestion du réseau, le redémarrage automatique, la gestion de la sortie standard, tout en simplifiant l’utilisation de points de montage, la construction d’une hiérarchie de fichiers racine, et bien d’autres améliorations qui ont largement contribuées à rendre plus accessible la conteneurisation.
Autour de 2015, pendant que Docker devenait populaire, et probablement à cause de l’apparition de moteurs de containers qui répondaient à un besoin que ne couvrait pas Docker, un effort de normalisation est apparu, dirigé par Microsoft, Red Hat, Docker (la société), Huawei et probablement d’autres sociétés qui ne disaient pas leur nom à l’époque, ainsi que quelques individus qui n’affichaient aucune affiliation. La spécification Open Container Format (abrégé “OCF”) est née. Celle-ci engendrera le moteur d’éxécution de conteneurs runc et l’organisation Open Container Initiative (abrégé “OCI”). Quelques mois plus tard, l’OCF sera ré-organisée en l’ensemble des spécifications qu’on connait aujourd’hui : la Runtime Specification et la Image Format Specification.
Parallèlement, la version 1 de Kubernetes est publiée par Google, et on voit naître le moteur de conteneurs rkt, qui vient directement concurrencer Docker.
À partir de 2015, au cours deux ou trois ans, Docker se modularise de plus en plus. Les projets containerd, libnetwork et buildkit naissent de la base de code de Docker, et le dépôt Git de Docker est séparé en deux : un pour l’interface en ligne de commande et un autre pour le démon. Enfin, le projet Moby est créé par Docker (la société), concluant la modularisation de Docker en tout ce qui pouvait être ré-utilisé par d’autres moteurs de conteneurs. Puisque j’ai attribué la paternité de la conteneurisation à LXC, je dois attribuer la paternité de la conteneurisation moderne à Docker.
Une des fonctionnalités fortement demandée par de gros exploitants de conteneurs était leur orchestration. C’est précisément à ce besoin que répondait Kubernetes, qui introduisit plusieurs concepts qui rendirent plus manipulables les applications multi-processus, tout en permettant l’évolutivité de la conteneurisation. Dès ses premières versions, Kubernetes est capable d’utiliser les moteurs de conteneurs Docker et rkt. Rapidement, le projet CRI-O est lancé, avec pour objectif de fournir un moteur de conteneurs plus petit et taillé pour le besoin de Kubernetes. Un peu plus tard, le support pour containerd est ajouté.
Aujourd’hui, grâce à tous les efforts de modularisation de la conteneurisation, nous disposons de normes et d’outils ré-utilisables qui permettent une inter-opérabilité exemplaire.
L’état actuel de l’écosystème
Les normes
La Runtime Specification normalise l’environnement d’exécution, la configuration et le cycle de vie d’un conteneur. Cette norme est accompagnée d’une implémentation de référence : runc.
La Image Format Specification normalise le format des images à partir desquelles sont créés les conteneurs.
La Container Network Interface Specification normalise la configuration, la modularité et l’inter-opérabilité de l’environnement réseau des conteneurs.
La Distribution Specification normalise la façon dont sont distribuées les images. Le projet à l’origine de cette spécification est Distribution, un projet donné “à la communauté” par Docker (la société).
Les moteurs d’exécution de conteneurs
Les moteurs d’exécution de conteneurs sont chargés de gérer la configuration et le cycle de vie d’un conteneur. En voici une liste non exhaustive :
Un moteur d’exécution de conteneurs écrit en Go qui a pour objectif d’être l’implémentation de référence de la Runtime Specification.
Un moteur d’exécution de conteneurs écrit en Go et basé sur runc. Il apporte les fonctionnalités supplémentaires suivantes :
Gestion des images (téléchargement, téléversement et stockage).
Création automatique des hiérarchies de fichiers racine à partir d’images.
Manipulations avancées des conteneurs (gel/dégel et transfert entre hôtes).
Extensible par greffons via une API gRPC.
Un moteur d’exécution de conteneurs écrit en C conforme à la Runtime Specification.
Un moteur d’exécution de conteneurs écrit en Rust conforme à la Runtime Specification.
Un moteur d’exécution de conteneurs basé sur runc, qui a pour objectif de répondre spécifiquement aus besoins de Kubernetes. Il sert d’interface, via la CRI, entre les projets conformes aux normes de l’OCI et Kubernetes.
Les moteurs de conteneurs
Les moteurs de conteneurs sont chargés de paramétrer l’environnement et créer, configurer et exécuter des conteneurs.
Le moteur de conteneur à l’origine de tout cet écosystème.
Un moteur de conteneurs mort en 2020 après l’annonce de l’abandon du projet CoreOS par Red Hat. Ce projet avait lancé la App Container Specification, une norme très proche de la Runtime Specification et la Image Format Specification.
Une alternative à Docker qui n’emploie pas de démon central. Souhaite être utilisé en lieu et place de l’interface en ligne de commande de Docker de façon compatible. Ses principes sont très proches de ceux de Docker. Une différence notable est le fait que Podman rassemble plusieurs containers qui doivent partager certains espaces de noms dans des “pods”, un concept similaire aux “pods” de Kubernetes.
Les constructeurs d’images
Comme leur nom l’indique, les constructeurs d’image servent à construire des images.
Le constructeur d’images utilisé par Docker. Compatible avec la Image Format Specification. C’est aussi l’implémentation de référence du format Dockerfile.
Le constructeur d’images utilisé par Podman. Compatible avec la Image Format Specification.
Les auxilliaires
Dans cette catégorie, j’ai placé des projets qui ne servent pas à grand-chose à eux seuls. Ce sont surtout de bibliothèques logicielles.
Une librairie en Go capable de manipuler des images ; téléchargement, téléversement, lecture et écriture.
Une librairie en Go capable de gérer des couches de hiérarchies de fichiers d’images et de conteneurs, et d’exposer, ainsi, la hiérarchie de fichiers d’une image ou d’un conteneur.
Un dépôt d’images. Il est à la base de la plupart des dépôt publiques d’images : Docker Hub, GitHub Container Registry et GitLab Container Registry entre autres.
Une librairie en Rust capable de gérer la couche réseau pour des conteneurs. Utilisée par Podman.
Un serveur DNS faisant autorité en Rust destiné à répondre à des requêtes DNS provenant de conteneurs.
Une librairie en Go capable de gérer la couche réseau pour des conteneurs. Elle fait partie du projet Moby.
Les outils
Un logiciel capable de modifier des images.
Un logiciel capable de convertir, télécharger et téléverser des images.
Les inclassables
Un orchestrateur de conteneurs. On pourrait dire que Kubernetes est à la fois un moteur de conteneurs et un moteur de moteurs de conteneurs. Techniquement, kubelet, un de ses composants, est un pur moteur de conteneurs.
Une alternative plus légère à Kubernetes qui vise l’embarqué.
Une alternative plus légère à Kubernetes qui vise à simplifier la création d’une grappe de serveurs.
Le squelette de Docker. Inutilisable en l’état, mais peut servir de base à d’autres moteurs de conteneurs.