Blog Agaetis

Docker, les conteneurs et l’Open Container Initiative (OCI)

02/02/2021| François Travais read_time 4 min.

Plus besoin de présenter Docker, depuis sa création en 2013, il a très vite été adopté et a démocratisé les conteneurs. Incontournable à ses débuts, ce n’est plus le cas; les standards de l’OCI (entre autres) ont permis l’émergence d’alternatives stables offrant d’autres fonctionnalités. Ces alternatives sont tellement solides que Kubernetes a même décidé de déprécier Docker en tant que runtime CRI (Container Runtime Interface) à partir de la version 1.20.

Cet article est le premier d’une série traitant de Docker et de ses alternatives, ainsi que des technologies de conteneurs en général.

Avant d’aller plus loin, commençons par les fondamentaux. Nous regarderons ensuite de plus près Docker et ses usages, puis l’OCI et ses standards.

Qu’est-ce qu’un conteneur ?

Le concept d’un conteneur applicatif est de packager une application et toutes ses dépendances de façon à l’isoler de l’environnement extérieur (l’OS, les autres applications…). Une méthode serait d’isoler cette application dans une machine virtuelle dédiée, mais cette solution est bien trop lourde pour une bonne partie des applications actuelles.

La méthode la plus communément utilisée, notamment par Docker, est d’utiliser les fonctionnalités du kernel Linux de la machine hôte afin d’encapsuler l’application et ses dépendances dans un conteneur pour aboutir à une solution plus légère et performante qu’une machine virtuelle classique. Le conteneur ne contient donc rien d’autre que l’application, ses dépendances et une arborescence de fichiers classiques d’OS au besoin.

Afin d’isoler un maximum le conteneur du reste du système, les conteneurs utilisent une combinaison des fonctionnalités suivantes du kernel Linux.

Les namespaces de différents types, dont :

– Le namespace de PID : le conteneur ne voit pas les processus de la machine hôte. Les processus du conteneur sont en revanche bien visibles de la machine hôte.

– Le namespace réseau : le conteneur peut utiliser tous les ports qu’il souhaite sans entrer en conflit avec les vrais ports de la machine hôte.

– Le namespace de mount : il est possible de monter le système de fichiers et devices que l’on souhaite. La logique est plutôt de réduire les mount au maximum.

Les cgroups permettent de restreindre les ressources CPU et RAM. Si le conteneur essaie d’utiliser plus de CPU qu’il n’en a le droit, il sera simplement limité. S’il essaie d’utiliser plus de RAM, il se fera tuer (OOM Killed).

Les Linux Security Modules (LSM) qui permettent de limiter les appels systèmes. Seccomp, AppArmor et SELinux sont des exemples d’implémentation de ce framework.

La combinaison de ces fonctionnalités permet la création de conteneurs légers et isolés du reste de la machine. Les principaux bénéfices de cette approche sont :

– La reproductibilité et la portabilité : l’application fonctionnera de la même façon quelque soit la machine hôte puisqu’elle embarque ses dépendances et est isolée de l’environnement extérieur ;

– La facilité d’utilisation et de partage : il est très simple de télécharger et de lancer un conteneur ;

– La facilité d’administration : le conteneur peut être facilement démarré sur une machine ou une autre et déplacé au besoin.

Maintenant que le concept de conteneur applicatif est un peu plus concret, regardons de plus près Docker et ses usages.

Docker et ses usages

Docker est un outil aux multiples usages, il permet entre autres de télécharger des images, de lancer des conteneurs, d’en construire et de redémarrer les conteneurs qui s’arrêteraient inopinément…

Il est également utilisé par des personnes aux rôles diverses, que ce soit le développeur pour tester rapidement une application ou construire localement des conteneurs, l’expert DevOps qui sera en charge de construire les images et de les distribuer dans un pipeline de CI/CD ou encore l’administrateur qui maintiendra le conteneur en production.

Docker est également utilisé par des orchestrateurs de conteneurs tels que Kubernetes.

Face à tous ces usages et profils d’utilisateurs, nous pouvons découper les outils de conteneurs comme ceci :

– Les container engines : les boîtes à outils permettant de faire un maximum d’actions sur les conteneurs (build, push, run…). Docker étant en tête de liste évidemment.

– Les builders : Docker et ses alternatives sur la partie du build d’image uniquement. Ces outils sont particulièrement utiles dans ces pipelines de CI

– Les container runtime pour Kubernetes (CRI) : les outils permettant à Kubernetes de lancer et maintenir les conteneurs en marche

Nous consacrerons également un article sur les container runtimes : se sont les outils de plus bas niveau permettant de lancer les conteneurs. Docker n’en est pas un mais en utilise un interne.

Docker permet donc de faire à peu près tout ce que l’on souhaite avec les conteneurs. Les autres outils s’appuient sur les standards basés sur Docker pour venir enrichir le panel d’outils disponibles.

L’OCI et ses standards

Docker et les acteurs majeurs de l’industrie du conteneur fondent l’Open Container Initiative en juin 2015 dans le but de développer des standards. Cette structure est sous la houlette de la Linux Foundation. Docker donne son runtime de conteneur, runc, ainsi que les bases des standards actuels.

Aujourd’hui l’OCI porte deux spécifications : la spécification de runtime (runtime-spec) et la spécification d’image (image-spec).

La spécification d’image définit ce qu’est une OCI image. Elle est composée :

– D’un système de fichiers en couche : chaque couche représente un changement par rapport à la couche du dessous (ajouts, suppression ou modification de fichiers)

– D’un manifeste d’image : il contient la liste des couches de filesystem successives spécifique à une plateforme (architecture CPU, OS)

– D’un index de manifestes : il contient la listes des manifestes d’image pour chaque plateforme supportée par l’image

– D’une configuration d’image : elle contient les paramètres d’exécution comme les variables d’environnement, les ports exposés, la commande par défaut…

Source : Open Container Initiative

Ces OCI Images sont construites par ce que nous appelons un builder (cf. Docker et ses usages), comme Docker, Buildah ou Kaniko.

Une OCI Image peut ensuite être extraite en “filesystem bundle”, qui sera ensuite lancée par un outil implémentant l’OCI Runtime specification, plus communément appelé OCI runtime ou container runtime.

Une autre spécification majeure existe, il s’agit de la Container Runtime Interface de Kubernetes. Celle-ci sera abordée dans notre prochain article consacré aux implémentations de cette spécification.

Conclusion

Les conteneurs sont maintenant très utilisés, en particulier grâce à Docker. Celui-ci permet de faire beaucoup de choses avec les conteneurs et s’adresse à de nombreux utilisateurs différents. Tous ces usages peuvent être décorrélés et exécutés par d’autres outils grâce aux standards de l’industrie comme ceux de l’OCI, comme le pipe du shell permet de connecter cat et sed par exemple.

Les prochains articles de la série traiteront des container runtimes, des container engines, des builders et des implémentations du CRI de Kubernetes. En attendant le prochain épisode, n’hésitez pas à aller consulter notre article sur comment adopter une approche Kubernetes First

Pour aller plus loin, voici quelques articles qui pourraient vous intéresser :

Un sujet vous intéresse ? Une question ? Contactez-nous

Nos adresses

Clermont-Ferrand
9, allée Evariste Galois
63170 Aubière
Tél. 04 73 35 47 51
Paris
21, rue de la banque
75002 Paris
Tél. 01 44 63 53 13
Lyon
52, Quai Rambaud
69002 Lyon