📘 Anatomie et usage des images de conteneurs
Pour comprendre comment créer des conteneurs, il utile de comprendre les bases du fonctionnement et de l’usage des images de conteneurs, que ce soit comment elles sont constituées, où elles sont stockées, et comment faire référence à une image stockée quelque part.
1. Ingrédients d’une image de conteneur
Une image de conteneur prend la forme d’un ensemble de données comprenant :
-
Une arborescence de répertoires et de fichiers, que l’on peut généralement diviser en :
-
Les fichiers nécessaires au fonctionnement du système d’exploitation restreint (notamment l’ensemble des utilitaires requis)
-
Le logiciel conteneurisé, et tous les fichiers nécessaires à son fonctionnement
-
-
Des métadonnées au sujet de l’image : origine, date de création, version, environnement d’accueil requis, format de stockage, etc.
-
En particulier, une métadonnée très importante est le point d’entrée de l’image, qui prend la forme d’une commande qui sera exécutée lors du démarrage d’un conteneur créé à partir de l’image. C’est ce point d’entrée qui indique comment le logiciel conteneurisé sera lancé (à savoir, quel exécutable lancer et avec avec quels arguments).
-
Une caractéristique clé d’une image de conteneur est l’immutabilité, c’est à dire qu’il n’est pas possible de modifier une image de conteneur existante. Cela permet de garantir qu’un déploiement réalisé avec une image de conteneur sera similaire d’un contexte à un autre.
| Il naturellement tout de même possible de créer une nouvelle image de conteneur à partir d’une copie d’une image de conteneur existante. |
Enfin, au sein d’un stockage donné, une image de conteneur est toujours qualifiée à l’aide d’un nom et une étiquette. Nous détaillons ces notions dans la Section 3.
2. Déroulement d’une création de conteneur
Avant de plonger dans les détails, prenons de la hauteur et voyons comment se déroule de manière générale la création d’un conteneur à partir d’une image de conteneur.
| Dans ce qui suit ci-dessous, nous allons évoquer différentes notions qui serons traitées plus tard dans ce chapitre et dans le cours − c’est normal ! |
-
Si l’image de conteneur requis provient d’un registre distant, et s’il n’est pas encore présent dans le stockage local d’images de l’environnement d’accueil, alors un téléchargement est fait du distant vers le local.
-
Un nouveau conteneur est instancié en effectuant une simple copie à l’écriture de l’image de conteneur (voir encart "Copie à l’écriture" si dessous).
-
Le nouveau conteneur est configuré pour lui donner le comportement souhaité dans le contexte de son exécution. Cette configuration inclut notamment les éléments suivants :
-
On peut confier au conteneur un ensemble de variables d’environnement. Pour rappel, une variable d’environnement est une paire
CLÉ=valeurqui, une fois définie, est accessible depuis tout logiciel s’exécutant dans un environnement, y compris si cet environnement est un conteneur. Il est courant d’associer des variables d’environnement à un conteneur de manière à paramétrer le logiciel qu’il contient. -
On indique l’ensemble des publications de ports souhaitées (sera vu dans les chapitres sur la mise en réseau des conteneurs).
-
On indique l’ensemble des montages souhaités (sera vu dans le chapitre sur la gestion des données des conteneurs).
-
-
Le point d’entrée du conteneur (soit celui spécifié dans l’image de conteneur, soit celui choisi à l’instantiation du conteneur) est exécuté. Tant que cette exécution n’est pas terminée, alors le conteneur est considéré comme étant en exécution.
|
Dans la pratique courante, étant donné que recréer un conteneur à partir d’une image de conteneur est très peu coûteux, on considère généralement un conteneur comme quelque chose de jetable que l’on n’hésite pas à détruire et recréer à loisir. Par exemple, pour mettre à jour un logiciel exécuté au sein d’un conteneur, on préférera toujours supprimer le conteneur et le recréer à l’aide d’une image de conteneur plus récente, plutôt que d’aller réinstaller le logiciel au sein du conteneur existant. Une conséquence importante de cela est qu'il faut toujours utiliser un montage pour stocker les données importantes d’un conteneur au sein de l’environnement d’accueil (sera vu dans le chapitre sur la gestion des données des conteneurs), et ne éviter de stocker de telles données directement dans le conteneur lui-même, qui pourra être supprimé à tout moment. |
3. Référencer une image
Pour qu’elle soit bien accessible, une image de conteneur peut être qualifiée au sein de l’emplacement où elle se trouve (stockage local ou registre de conteneur). Ce qualificatif est toujours composé de deux parties :
-
Un nom, qui décrit l’image, et qui généralement informe sur le logiciel contenu au sein de l’image. Il est fréquent dans certains registres de conteneurs que ce nom soit écrit sous la forme d’un chemin découpé à l’aide de de barres obliques (
/). On peut par exemple avoir un nom sous la forme<groupe>/<nom au sein du groupe>dans un registre de conteneur qui permet de faire des groupes d’images. -
Une étiquette, qui donne des informations utiles au sujet de l’image : version du logiciel qui s’y trouve, type d’environnement inclus dans l’image (par exemple l’image de base utilisée), information sur la taille de l’image (légère ou non), à quoi l’image est destinée (par exemple débogage, recette ou production) etc.
En pratique, on écrit toujours la qualificatif d’une image avec la syntaxe suivante :
<nom de l'image>:<étiquette>
| Il n’existe pas de règles strictes pour bien nommer et étiqueter une image, et les pratiques sont très variées d’un projet à un autre. Il est cependant communément admis que bien étiqueter une image est très utile pour bien les distinguer entre elles ! |
Il faut être vigilant sur quelques aspects :
-
Au sein d’un même emplacement donné (stockage local ou registre de conteneur) :
-
Un qualificatif
<nom de l’image>:<étiquette>correspond toujours à une seule image de conteneur. Ainsi, si on attribue à une image un qualificatif déjà utilisé par une autre image, le qualificatif est automatiquement retiré de cette dernière. -
Une même image peut être connue sous plusieurs plusieurs qualificatifs différents (le plus souvent souvent avec le même nom mais avec des étiquettes différentes).
-
-
Un qualificatif est toujours spécifique à un emplacement donné (stockage local ou registre de conteneur). Ainsi, une même image stockée simultanément dans différents emplacements peut posséder un qualificatif différent par emplacement.
| Il est par exemple possible de créer une image dans un registre local et de lui donner un qualificatif, puis d’envoyer cette image dans un registre de conteneurs distant en lui donnant une autre qualificatif. |
À partir d’un qualificatif, on peut écrire une référence d’image sous la forme d’un chemin d’image, qui prend la forme suivante :
<emplacement>/<qualificatif>
où :
-
<emplacement>désigne l’endroit où récupérer l’image, qui peut être soit le stockage local, que l’on désigne toujours parlocalhost, soit un registre de conteneurs, désigné par son nom d’hôte. -
<qualificatif>désigne le qualificatif tel que défini plus haut.
Prenons le chemin d’image suivant :
docker.io/nginx:1.25
Nous avons ici :
-
docker.iocomme emplacement, donc le Docker Hub, comme registre de conteneur. -
nginx:1.25comme qualificatif d’image, avec :-
nginxcomme nom d’image, ce qui sur le Docker Hub correspond à l’image officielle pour le serveur web nginx. On peut voir des informations sur cette image en se rendant sur sa page de description sur le catalogue du Docker Hub. -
1.25comme étiquette, qui fait ici fait référence à la version de nginx embarquée dans l’image.
-
Prenons la référence d’image suivante, que l’on écrit pour désigner une image Simplestblog que nous avons au préalable publiée :
docker-registry.univ-nantes.fr/gl/developpement_exploitation/resources/simplestblog:latest
Ici on a :
-
docker-registry.univ-nantes.frcomme emplacement, donc le registre de conteneurs l’instance Gitlab de Nantes Université. -
gl/developpement_exploitation/resources/simplestblog:latestcomme qualificatif d’image, avec :-
gl/developpement_exploitation/resources/simplestblogcomme nom d’image, ce qui ici correspond à l’emplacement du projet Simplestblog au sein de l’instance Gitlab. -
latestcomme étiquette, donc vraisemblablement la version la plus récente de l’image.
-
| Une fois une image distante récupérée sur un stockage local, l’image (désormais locale) mémorise tout de même son emplacement d’origine, ce qui permet à l’image de posséder, même localement, le même chemin que l’image distante. |
4. Registres de conteneurs
Il est possible de créer un conteneur à partir d’une image de conteneur située sur un registre de conteneurs, qui est un stockage d’images distant accessible par le réseau. On peut voir un registre de conteneurs comme une sorte de "magasin d’applications" qui ne contient que des images de conteneurs. Un registre de conteneurs peut être privé, par exemple au sein d’une entreprise, ou encore peut être public et mettre à disposition des images à tous directement sur Internet. Lorsqu’on utilise une image provenant d’un registre de conteneurs, cette image est toujours automatiquement copiée au sein du stockage local d’images, avant d’être ensuite utilisée pour créer un nouveau conteneur.
| Bien qu’on parle de registre de conteneurs, un registre contient en fait des images de conteneurs ! |
| La très grande majorité des images de conteneurs disponibles en ligne sont dédiées à des environnements d’accueil de type Linux. Une raison est que, aujourd’hui, Linux est utilisé massivement comme environnement d’accueil pour faire fonctionner des serveurs. Or, la conteneurisation vise en grande partie à faciliter la de déploiement de logiciels sur des serveurs. Par conséquent, il est aujourd’hui encore rare d’utiliser la conteneurisation pour des logiciels qui s’exécutent dans un environnement Windows, bien moins utilisé comme environnement serveur. |
Concernant les registres de conteneurs publics, Docker Hub est de très loin le registre de conteneurs public le plus important au monde à l’heure actuelle. C’est un registre hébergé par l’entreprise américaine Docker, Inc. qui développe le moteur de conteneurisation Docker. C’est historiquement le premier véritable registre de conteneurs public, et par conséquent c’est aujourd’hui de très loin le plus visible, le plus complet, et le plus utilisé. Tout le monde peut gratuitement utiliser des images hébergées publiquement sur le registre, en revanche il est préférable de passer par une offre payante pour stocker ses propres images sur le registre.
Bien que le site web du Docker Hub soit visitable à l’adresse https://hub.docker.com/, le registre de conteneurs lui-même est disponible avec le nom d’hôte docker.io.
C’est ce nom d’hôte qu’il faut utiliser lorsqu’on encode une référence à une image située sur le Docker Hub !
|
On trouve également des registres de conteneurs importants adossés à deux parmi les principales solutions de forges logicielles commerciales :
-
La plateforme GitHub fournit un registre de conteneurs gratuit qui permet de stocker des images de conteneurs directement au sein d’un projet de développement hébergé sur Github. Le nom d’hôte du regist de conteneurs est
ghcr.io. -
La plateforme Gitlab fournit elle-aussi un registre de conteneurs gratuit qui permet de stocker des images de conteneurs directement au sein d’un projet de développement hébergé sur Gitlab. Le nom d’hôte du registre de conteneurs est
registry.gitlab.com.
Enfin, dans le cadre de ce cours, nous allons faire usage d’un registre de conteneurs "privé" adossé à l’instance Gitlab de Nantes Université.
Le nom d’hôte de ce registre de conteneurs est docker-registry.univ-nantes.fr.
|
Vous êtes peut-être confus sur l’existence d’un Gitlab à l’adresse https://gitlab.com/ et d’un Gitlab "Nantes Université" à l’adresse https://gitlab.univ-nantes.fr/ ? Pas de panique, quelques explications. Gitlab Inc. est le nom d’une entreprise qui possède deux activités principales :
Et pour ce qui est l’instance Gitlab de Nantes Université, accessible à l’adresse https://gitlab.univ-nantes.fr/, c’est simplement une installation du logiciel Gitlab qui a été effectuée par le service informatique de l’université, à destination des personnels et étudiants. |
5. Quelles images utiliser comme base ?
Lorsqu’on souhaite déployer un logiciel qui n’est pas déjà disponible sous la forme d’une image, il est important de prendre le temps de réfléchir à l’image de base à utiliser pour un conteneur. Cette image de base va non seulement définir la première couche du mini-environnement dans lequel notre logiciel va s’exécuter, mais va également nous fournir des outils qui peuvent servir à construire notre logiciel, et également des outils utiles pour maintenir et déboguer notre conteneur.
Il est par exemple intéressant de se poser les questions suivants :
-
Ai-je besoin de construire le logiciel ?
-
Est-ce que mon logiciel a besoin d’un environnement Linux complet, avec tous les logiciels système habituels (ligne de commande, outils d’interface réseau, etc) ?
-
Comment installer efficacement les dépendances de mon logiciel ? Faut-il mieux obtenir les dépendances en amont et les dépose dans le conteneur lors de sa création ? Ou bien est-ce qu’on préférera utiliser un installeur de paquets existant (comme
apt,apkoudnf) fourni au préalable par le système d’exploitation de l’image ? -
Quels outils seraient utiles dans le conteneur s’il est nécessaire de déboguer le logiciel qui s’y trouve, ou bien d’effectuer des tâches de maintenance sur ce logiciel ?
-
Comment faire pour limiter la taille de mon conteneur ?
Parmi les très nombreuses images de base disponibles sur un registre de conteneurs public comme le Docker Hub, on trouvera au moins deux catégories d’images qui sont très souvent utilisées comme images de base :
-
Les images basées sur des distributions Linux fournissent chacune un environnement minimal avec tous les logiciels et bibliothèques installés par défaut au sein d’une distribution Linux. Ainsi ces images contiennent toujours un système de fichiers Linux classique (avec
bin,home,usr, etc.), ainsi que les outils utilitaires UNIX habituels (grep,ls,ps, etc.). Une image de ce type fournit également un installeur de paquets qui permet de facilement télécharger de nouveaux logiciels fournis par la distribution Linux considérée. On trouve de telles images pour la plupart des distributions Linux classiques, comme Debian, Ubuntu, ou Fedora. Une image de base très populaire est celle qui repose sur la distribution Alpine, car cette dernière est conçue pour occuper un espace disque minimal (ce qui permet de réduire la taille de l’image finale construite), et conçue pour une meilleure sécurité. -
Les images dédiées à des technologies particulières fournissent chacune un environnement déjà prêt pour faire fonctionner un logiciel développé avec une technologie particulière. Ces images sont généralement elle-mêmes basées sur les images décrites dans le point précédent, et ajoutent les exécuteurs et bibliothèques adaptés à un contexte particulier. On trouve de telles images pour différents langages et technologies comme R, Python (avec l’outil
pip), Node.js (avec l’outilnpm), ou encore Java (avec différentes images comme Eclipse Temurin pour l’exécution de programmes Java avec la plateforme d’exécution Temurin, Gradle pour la compilation de programmes Java avec l’outil Gradle, ou encore Maven pour la compilation de programmes Java avec l’outil Maven).
On retiendra qu’il est souvent préférable d’utiliser la seconde catégorie d’images car elle "prémâche" efficacement le travail de préparation, en fournissant directement les compilateurs et/ou exécuteurs nécessaires pour nos logiciels.
| Même si les images de base dédiées à des technologies sont très pratiques, elles peuvent être inadaptées lors de déploiements pour des raisons de sécurité. En effet, il est recommandé d’éviter de déployer des conteneurs qui contiennent des outils de construction de logiciels, comme des compilateurs ou des gestionnaires de paquets − pour plus d’informations se référer aux recommandations de l’ANSSI concernant les conteneurs. Nous reparlerons de ces aspects en fin de semestre lorsque nous traiterons de comment construire une image de conteneur. |