📘 Conteneuriser (manuellement) un logiciel en image de conteneur

1. Motivation

Nous avons vu tout au long du cours qu’il Ă©tait possible d’utiliser une image de conteneur dĂ©jĂ  disponible "sur Ă©tagĂšre" afin de facilement crĂ©er un nouveau conteneur. Une premiĂšre catĂ©gorie d’images de conteneur sont celles qui fournissent chacune un environnement minimal sur la base d’un systĂšme d’exploitation (Ubuntu, Alpine, etc.) ou dĂ©diĂ©e Ă  une technologie (Python, Julia, Java, etc.). Une seconde catĂ©gorie d’images sont celles qui permettent chacune de dĂ©ployer facilement un logiciel spĂ©cifique (et l’environnement minimal associĂ©), qui aura Ă©tĂ© prĂ©-installĂ© et prĂ©-configurĂ© au sein de la dite image.

Dans ce cours, il nous reste Ă  comprendre comment faire pour crĂ©er une nouvelle image de conteneur, et particuliĂšrement une image de conteneur de la seconde catĂ©gorie (Ă  savoir une image qui fournit un logiciel spĂ©cifique). En effet, prĂ©parer une image de conteneur immutable et complĂšte est une maniĂšre trĂšs robuste et efficace de permettre le dĂ©ploiement d’un logiciel fraichement dĂ©veloppĂ©.

On utilise parfois l’expression "conteneuriser un logiciel" pour parler de la crĂ©ation d’une image de conteneur pour un logiciel.

2. Principe

Pour rappel, une image de conteneur est quasiment de mĂȘme nature qu’un conteneur : elle dĂ©finit un mini-environnement et tout ce dont il est composĂ© (fichiers, rĂ©pertoires, etc.). La principale diffĂ©rence est qu’une image de conteneur est immutable − c’est Ă  dire qu’elle est figĂ©e dans un Ă©tat non modifiable − tandis qu’un conteneur est mutable − c’est Ă  dire que son Ă©tat peut ĂȘtre modifiĂ©. Une consĂ©quence de cette dĂ©finition est qu’il n’est pas possible d’exĂ©cuter un processus au sein d’une image de conteneur, puisqu’un processus nĂ©cessite un environnement mutable − c’est pour cette raison qu’il faut toujours crĂ©er un nouveau conteneur Ă  partir d’une image pour dĂ©marrer un processus.

Ainsi, puisqu’une image de conteneur n’est rien d’autre qu’un conteneur immutable, la crĂ©ation d’une nouvelle image de conteneur peut s’accomplir en crĂ©ant un nouveau conteneur temporaire Ă  partir d’une image de conteneur existante dite "de base", puis en modifiant ce conteneur temporaire, et enfin en rendant ce conteneur temporaire immutable.

La crĂ©ation d’une nouvelle image de conteneur Ă  partir d’une image de base peut se rĂ©sumer ainsi :

image creation.drawio
Figure 1. Étapes principales lors de la crĂ©ation d’une nouvelle l’image de conteneur
  1. (si nĂ©cessaire) L’image de base pressentie est tĂ©lĂ©chargĂ©e auprĂšs d’un registre distant, et est stockĂ©e dans le stockage local d’images de l’environnement utilisĂ© pour construire notre image.

  2. Un conteneur temporaire est instanciĂ© Ă  partir de l’image de base.

  3. Ce conteneur temporaire est modifiĂ© afin d’y ajouter notamment :

    • le logiciel que l’on souhaitera dĂ©ployer,

    • toutes les dĂ©pendances du logiciel Ă  dĂ©ployer (bibliothĂšques, exĂ©cuteurs, utilitaires, etc.).

    • si pertinent, les fichiers/rĂ©pertoires nĂ©cessaires Ă  l’exĂ©cution de base du logiciel − par exemple des fichiers de configuration,

  4. Le conteneur temporaire (dĂ©sormais modifiĂ©) est enregistrĂ© en nouvelle image de conteneur au sein du stockage local d’images. Lors de cet enregistrement, on vient greffer Ă  cette image un ensemble de mĂ©tadonnĂ©es nĂ©cessaires au fonctionnement de l’image, en particulier :

    • Le point d’entrĂ©e, qui prend la forme d’une commande qui sera exĂ©cutĂ©e lors du dĂ©marrage d’un conteneur créé Ă  partir de l’image,

    • Si besoin, le rĂ©pertoire de dĂ©part Ă  partir duquel sera exĂ©cutĂ© la commande du point d’entrĂ©e de l’image,

    • Si besoin, des valeurs constantes affectĂ©es Ă  certaines variables d’environnement utilisĂ©es par le logiciel.

  5. Enfin, si souhaitĂ©, l’image de conteneur créée est tĂ©lĂ©versĂ©e vers un registre de conteneurs distant, afin de rendre l’image disponible aux utilisateur⋅ices souhaitĂ©s.

Mais comment crĂ©er une image sans partir d’un conteneur ?

Jusqu’ici nous avons vu que :

  • un conteneur est toujours créé Ă  partir d’une image de conteneur,

  • une image de conteneur peut ĂȘtre créée Ă  partir d’un conteneur (lui-mĂȘme créé Ă  partir d’une image de base).

Nous sommes ici face Ă  un problĂšme de type "l’Ɠuf ou la poule" : comment a pu ĂȘtre créé le premier conteneur ou la premiĂšre image de conteneur, si chacun⋅e dĂ©pend de l’autre pour exister ?

Il est en fait possible de crĂ©er une image de conteneur Ă  partir de zĂ©ro (en anglais, "from scratch"), sans partir d’un conteneur existant, et donc sans partir d’une image de base existante. On procĂšde alors en construisant "Ă  la main" une arborescence de fichiers contenant exactement le contenu souhaitĂ© dans l’image. C’est comme cela qu’est construite une image de conteneur qui fournit juste un systĂšme d’exploitation minimal.

On ne s’intĂ©ressera pas dans ce cours Ă  ce mode de fonctionnement, car c’est un cas plus rare et plus difficile. Il par ailleurs aujourd’hui plus habituel de toujours reposer sur une image de base pour construire une nouvelle image.

3. Bien concevoir une image de conteneur

3.1. Problématique

Une fois une image de conteneur prĂ©parĂ©e pour un logiciel donnĂ©, elle sera mise entre les mains des administrateur⋅ices systĂšme qui seront chargé⋅es de dĂ©ployer le dit logiciel. Lors de cette "phase d’usage" de l’image, ces personnes devront correctement la configurer et l’exĂ©cuter
 sans se tromper !

creation utilisation image.drawio
Figure 2. Distinction entre phase de crĂ©ation et phase d’usage d’image de conteneur
Lors des TPs vous avez souvent été dans cette situation, à savoir devoir utiliser une image de conteneur "sur étagÚre" en devant lire soigneusement sa documentation.

Pour que cette phase d’usage se dĂ©roule sans accroc, il est prĂ©fĂ©rable avant de crĂ©er une image de bien concevoir et documenter l’image de conteneur en prĂ©vision de son usage final. En effet, lorsqu’un conteneur sera créé Ă  partir de l’image, il faudra s’assurer que le processus logiciel exĂ©cutĂ© reçoive toutes les donnĂ©es, valeurs de configuration, et accĂšs au rĂ©seau dont il pourrait avoir besoin. L’image de conteneur doit anticiper cela au travers de son Ă©tat initial et de sa documentation.

Dans toutes les Ă©tapes qui suivent, un point rĂ©current sera de bien distinguer ce qui doit prĂ©-exister (autrement dit des "constantes") dans l’image de conteneur − valeurs de configuration par dĂ©faut, fichiers, etc. − et ce qui sera apportĂ© (autrement dit des "variables") uniquement lors de la crĂ©ation d’un conteneur Ă  partir de l’image − valeurs de configurations, fichiers, montages, accĂšs rĂ©seau, etc.

Si on fait un parallĂšle avec la programmation, on peut voir une image de conteneur comme une sorte de fonction prĂȘte Ă  l’emploi, que l’on peut ensuite appeler lorsque c’est nĂ©cessaire. Or une fonction doit ĂȘtre clairement conçue en terme de donnĂ©es entrĂ©es (paramĂštres) et donnĂ©es de sorties (valeurs de retour), et doit ĂȘtre bien documentĂ©e pour expliquer comment chaque entrĂ©e peut affecter les sorties possibles.

3.2. Étape 1 : dĂ©finir le mode de configuration de l’image

Tout au long du cours, nous avons vu qu’une prĂ©occupation est de bien configurer un logiciel que l’on dĂ©ploie. Ainsi, si on souhaite rendre un logiciel dĂ©ployable via une image de conteneur, il est utile de se poser les questions suivantes :

  • avant tout, est-ce que le logiciel que l’on souhaite conteneuriser est configurable ?

    • via des arguments en ligne de commande ?

    • via un fichier de configuration ?

    • via des variables d’environnement ?

  • souhaite-t-on prĂ©-configurer le logiciel au sein de l’image, avec certaines constantes ?

  • souhaite-t-on que l’utilisateur de l’image puisse configurer le logiciel (partiellement ou entiĂšrement) ?

  • de quelle maniĂšre est-il pertinent de permettre la configuration du logiciel lors de l’utilisation de l’image ?

Concernant le dernier point, comme il n’est pas pratique de passer des arguments en ligne de commande Ă  un logiciel conteneurisĂ©, on privilĂ©gie surtout les deux autres mĂ©thodes :

  • soit en passant un fichier de configuration par montage de fichier,

  • soit en passant des variables d’environnement au conteneur directement,

  • soit via un mĂ©lange de ces deux mĂ©thodes.

3.2.1. Méthode A : passer un fichier de configuration par montage de fichier

Si le logiciel conteneurisĂ© a Ă©tĂ© Ă  l’origine conçu pour se configurer en lisant un fichier de configuration, alors il est possible de directement reposer sur ce mode de configuration. Il sera alors nĂ©cessaire pour l’utilisateur⋅irce future de l’image de devoir Ă©crire un fichier de configuration, et de le fournir au logiciel conteneurisĂ© Ă  l’aide d’un montage de fichier.

Il sera pertinent de documenter l’image de conteneur pour expliquer qu’un fichier de configuration est attendu Ă  un emplacement prĂ©cis (c’est Ă  dire, l’emplacement oĂč il sera cherchĂ© par le processus en exĂ©cution) dans le futur conteneur.

3.2.2. MĂ©thode B : dĂ©finir et/ou utiliser des variables d’environnement

Une stratĂ©gie trĂšs populaire pour configurer un logiciel conteneurisĂ© est de reposer sur des variables d’environnement. Le grand avantage de cette approche est qu’elle est repose sur un mĂ©canisme assez universel, et que les moteurs de conteneurisation permettent trĂšs facilement de dĂ©finir des variables d’environnement lors de l’exĂ©cution d’un conteneur.

Lorsqu’on prĂ©pare une image de conteneur, deux cas de figure sont possibles :

  • Dans le cas le plus simple, le logiciel conteneurisĂ© a Ă©tĂ© dĂšs le dĂ©part construit pour se configurer au travers de variables d’environnement. Il faut alors bien analyser les variables d’environnement comprises par le logiciel et en dĂ©duire :

    • celles pour lesquelles il faut fixer des valeurs constantes,

    • celles qu’il faudra laisser Ă  configurer pour l’utilisateur final de l’image de conteneur.

  • Dans un cas plus complexe, il est possible que le logiciel que l’on conteneurise n’ait pas Ă©tĂ© au dĂ©part conçu pour se configurer via des variables d’environnement. Si on souhaite tout de mĂȘme pouvoir permettre une configuration via des variables d’environnement, on peut dĂ©finir de nouvelles variables d’environnement juste pour l’image de conteneur. Cependant, comme elles ne seront pas lues par le logiciel, il est nĂ©cessaire que l’image de conteneur possĂšde un point d’entrĂ©e qui fera usage de ces nouvelles variables d’environnement − par exemple en les transformant en arguments en ligne de commande.

Dans tous les cas, il sera pertinent de documenter l’image de conteneur pour expliquer les choix rĂ©alisĂ©s.

3.3. Étape 2 : dĂ©finir le point d’entrĂ©e

Comme dĂ©jĂ  vu dans le cours, le point d’entrĂ©e d’une image de conteneur dĂ©finit quel processus logiciel est exĂ©cutĂ© lors du dĂ©marrage d’un conteneur qui instancie cette image. C’est tout simplement une commande exĂ©cutĂ©e dans le conteneur lors du dĂ©marrage de ce dernier.

En principe, le point d’entrĂ©e est donc simplement la commande qui permet de dĂ©marrer le logiciel conteneurisĂ©. Cependant, si le logiciel Ă  exĂ©cuter nĂ©cessite des arguments en ligne de commande, il est nĂ©cessaire de les lui fournir dans le point d’entrĂ©e :

  • soit sous la forme de constantes,

  • soit sous la forme de variables d’environnement dont les valeurs seront fixĂ©es lors de la future crĂ©ation du conteneur.

Et Ă  nouveau, il sera pertinent de documenter l’image de conteneur pour expliquer les choix rĂ©alisĂ©s.

DĂ©finir un point d’entrĂ©e Ă  l’aide d’un script dĂ©diĂ©

Si le point d’entrĂ©e souhaitĂ© devient trop complexe − par exemple si on fait l’usage d’un grand nombre de nouvelles variables d’environnement − il peut ĂȘtre nĂ©cessaire d’Ă©crire un script qui sera ajoutĂ© Ă  l’image de conteneur et qui sera utilisĂ© comme point d’entrĂ©e. Pour les curieux, voir par exemple le le script utilisĂ© comme point d’entrĂ©e dans l’image de conteneur MariaDB.

3.4. Étape 3 : anticiper les points de montage nĂ©cessaires

Si le logiciel conteneurisé devra lire ou écrire des fichiers durant son exécution, alors il est utile de se poser les questions suivantes :

  • Est-ce que le logiciel utilisera des fichiers constants, qui seront toujours les mĂȘmes, d’une exĂ©cution Ă  une autre ?

    • Si oui, ces fichiers peuvent ĂȘtre ajoutĂ©s Ă  lors de la crĂ©ation de l’image.

  • Est-ce que le logiciel devra lire des fichiers fournis par l’utilisateur⋅ice du logiciel ?

    • Si oui, ces fichiers devront ĂȘtre fournis Ă  l’aide de montages de rĂ©pertoires/fichiers.

  • Est-ce que le logiciel devra produire des fichiers qui devront ĂȘtre rĂ©cupĂ©rĂ©s par l’utilisateur⋅ice du logiciel ?

    • Si oui, un point de montage devra ĂȘtre créé par l’utilisateur⋅ice pour pouvoir rĂ©cupĂ©rer les dits fichiers.

  • Est-ce que le logiciel stocke son Ă©tat sous la forme de fichiers, et est-ce qu’on souhaite faire persister cet Ă©tat d’une exĂ©cution du logiciel Ă  une autre ?

    • Si oui, un point de montage devra ĂȘtre créé par l’utilisateur⋅ice pour garantir la persistence des dits fichiers en cas de supression du conteneur.

Encore une fois, il sera pertinent de documenter l’image de conteneur pour expliquer les choix rĂ©alisĂ©s.

3.5. Étape 4 : anticiper les accĂšs rĂ©seau nĂ©cessaires

Si le logiciel conteneurisĂ© aura besoin de communiquer avec d’autres processus logiciels, il est important de documenter Ă  la fois :

  • les ports rĂ©seaux sur lesquels le logiciel conteneurisĂ© est susceptible d’Ă©couter,

  • les logiciels extĂ©rieurs auxquels le logiciel conteneurisĂ© souhaitera se connecter (par exemple un accĂšs Ă  une base de donnĂ©es), et comment configurer accĂšs rĂ©seaux Ă  ces logiciels (par exemple l’adresse rĂ©seau et le port auxquels se connecter).

À nouveau, il sera pertinent de documenter l’image de conteneur pour expliquer les choix rĂ©alisĂ©s.

3.6. Exemples

Exemple 1. Conception de l’image de conteneur Automatic-Image-Converter

On souhaite prĂ©parer une image de conteneur pour Automatic-Image-Converter, notre logiciel capable de convertir en continu des photos d’un type de fichier Ă  un autre.

Étape 1 : dĂ©finir le mode de configuration de l’image

Automatic-Image-Converter est un logiciel qui s’attend Ă  trouver quatre arguments en ligne de commande pour s’exĂ©cuter, dans un ordre strict : le rĂ©pertoire d’entrĂ©e, puis le rĂ©pertoire de sortie, puis le format vers lequel convertir, et enfin la durĂ©e entre chaque passe de conversion.

On fait le choix que les deux rĂ©pertoires (entrĂ©e et sortie) seront des constantes au sein du conteneur, Ă  savoir /inputs et /outputs, et on dĂ©cide que la configuration du format et de la durĂ©e pourra se faire lors de la phase d’usage Ă  l’aide de deux variables d’environnement FORMAT DURATION.

Étape 2 : dĂ©finir le point d’entrĂ©e

En supposant que le logiciel soit copiĂ© dans l’image de conteneur Ă  l’emplacement /app.jar, la commande pour exĂ©cuter Automatic-Image-Converter prend la forme :

java -jar /app.jar <répertoire entrée> <répertoire sortie> <format cible> <durée>

Sur la base des choix faits en Ă©tape 1, on fixe les deux constantes choisies pour les deux premiers arguments, et les deux variables choisies pour les deux derniers arguments, ce qui donne le point d’entrĂ©e suivant :

java -jar /app.jar /inputs /outputs $FORMAT $DURATION

Étape 3 : anticiper les points de montage nĂ©cessaires

Le logiciel aura besoin de lire des fichiers fournis par l’utilisateur, et de produire des fichiers en consĂ©quence, dans deux rĂ©pertoires distincts (Ă  savoir les constantes /inputs et /outputs fixĂ©es dans les Ă©tapes prĂ©cĂ©dentes). On documente donc pour notre image que :

  • un montage de rĂ©pertoire est nĂ©cessaire Ă  l’emplacement /inputs pour pouvoir fournir des images Ă  notre logiciel,

  • un montage de rĂ©pertoire est nĂ©cessaire Ă  l’emplacement /outputs pour pouvoir rĂ©cupĂ©rer les images produites par notre logiciel.

Étape 4 : anticiper les accĂšs rĂ©seau nĂ©cessaires

Automatic-Image-Converter n’a pas besoin de communiquer avec d’autres processus via le rĂ©seau.

Exemple 2. Conception de l’image de conteneur Simplestblog

On souhaite préparer une image de conteneur pour Simplestblog, notre logiciel de blog accessible par le web.

Étape 1 : dĂ©finir le mode de configuration de l’image

Simplestblog est un logiciel qui requiert un fichier de configuration pour fonctionner. Petite subtilitĂ©, le logiciel ira chercher ce fichier de configuration Ă  l’emplacement spĂ©cifiĂ© par la variable d’environnement SIMPLESTBLOG_SETTINGS_FILE.

On fait le choix que le fichier de configuration devra ĂȘtre fourni lors de la phase d’usage, et on fait le choix qu’il sera toujours fourni Ă  un emplacement constant au sein du conteneur, Ă  savoir l’emplacement /config.py. Par consĂ©quent, on fixe une valeur constante /config.py dans la variable SIMPLESTBLOG_SETTINGS_FILE (ce qui signifie que l’on considĂšre que l’utilisateur n’aura pas Ă  utiliser dĂ©finir variable).

Étape 2 : dĂ©finir le point d’entrĂ©e

En supposant que l’image de conteneur ait pour rĂ©pertoire initial le rĂ©pertoire avec le code source de Simplestblog, la commande pour exĂ©cuter le logiciel prend la forme :

waitress-serve --call simplestblog:create_app

Il n’y a pas d’arguments particuliers Ă  fournir, donc on peut garder la commande telle quelle.

Étape 3 : anticiper les points de montage nĂ©cessaires

Simplestblog n’a pas d’Ă©tat Ă  stocker sous forme de fichier (car il stocke son Ă©tat dans une base de donnĂ©es), et ne produit aucun fichier. En revanche, comme Ă©voquĂ© plus haut, Simplestblog requiert un fichier de configuration Ă  l’emplacement constant /config.py fixĂ© prĂ©cĂ©demment.

On documente donc pour notre image qu’un montage de fichier Ă  l’emplacement /config.py est attendu lors de la phase d’usage.

Étape 4 : anticiper les accĂšs rĂ©seau nĂ©cessaires

Simplestblog souhaite communiquer avec des processus extérieurs de deux maniÚres :

  • d’une part, Simplestblog Ă©coute sur le port rĂ©seau 5000 et s’attend Ă  ĂȘtre sollicitĂ© par des navigateurs webs (ex. Firefox) sur ce port.

  • d’autre part, Simplestblog cherche Ă  contacter une base de donnĂ©es compatible MySQL situĂ©e sur un rĂ©seau accessible ; plus prĂ©cisĂ©ment, Simplestblog se basera sur la valeur MYSQL_DATABASE_HOST du fichier de configuration attendu pour savoir Ă  quelle adresse rĂ©seau se connecter pour trouver une base de donnĂ©es, et sur la valeur MYSQL_DATABASE_PORT pour savoir sur quel port rĂ©seau se connecter.

On Ă©crit bien ces informations dans la documentation de l’image.

4. Utiliser Podman pour créer et gérer des images de conteneur

4.1. CrĂ©ation d’une image Ă  partir d’un conteneur

Supposons qu’un conteneur temporaire ait Ă©tĂ© créé Ă  partir d’une image dite "de base", puis que ce conteneur ait Ă©tĂ© modifiĂ© pour contenir un logiciel Ă  dĂ©ployer ainsi que ses dĂ©pendances. La commande Ă  utiliser pour crĂ©er une image de conteneur Ă  partir de ce conteneur temporaire est la suivante :

$ podman container commit  <nom du conteneur> \
                           <options> \
                           <nom donné>:<étiquette donnée>

avec :

  • <nom du conteneur> : le nom du conteneur Ă  partir duquel on crĂ©e une nouvelle image,

  • <nom donnĂ©> : le nom que l’on souhaite donner Ă  cette nouvelle image,

  • <Ă©tiquette donnĂ©e> l’Ă©tiquette que l’on souhaite donner Ă  cette nouvelle image.

S’il y avait dĂ©jĂ  une image stockĂ©e localement avec le mĂȘme nom et la mĂȘme Ă©tiquette, elle est alors remplacĂ©e par la nouvelle image.

Dans <options> nous allons en utiliser une seule appelée --change, qui permet de définir des métadonnées pour notre nouvelle image. Plus précisément, nous allons utiliser trois variantes de cette option :

  • --change 'WORKDIR <rĂ©pertoire>' : spĂ©cifie le rĂ©pertoire de dĂ©part Ă  partir duquel le point d’entrĂ©e est exĂ©cutĂ©,

  • --change 'CMD <point d’entrĂ©e>' : spĂ©cifie le point d’entrĂ©e, c’est Ă  dire la commande qui sera exĂ©cutĂ©e lorsqu’un conteneur sera créé Ă  partir de cette nouvelle image,

  • --change 'ENV <clĂ©>=<valeur>' : affecte <valeur> Ă  une variable d’environnement <clĂ©> lors de l’exĂ©cution du point d’entrĂ©e.

Exemple 3. CrĂ©ation d’une image figlet Ă  partir d’un conteneur

On souhaite prĂ©parer une image jouet qui conteneurise l’utilitaire Unix figlet qui permet d’Ă©crire en art ASCII un message sur la ligne de commande.

On conçoit notre image de la maniÚre suivante :

  • l’image de base sera l’image officielle Ubuntu Linux en version 25.10, disponible sur le Docker Hub,

  • on installera la commande figlet avec le gestionnaire de paquet apt,

  • on souhaite que l’utilisateur futur de l’image puisse spĂ©cifier un message Ă  afficher Ă  l’aide d’une variable d’environnement MESSAGE.

On commence par préparer un conteneur temporaire :

$ podman container run -ti --name figlet_template docker.io/ubuntu:25.10

Dans ce conteneur temporaire, on installe figlet :

[sur le conteneur]
$ apt update
$ apt install figlet

C’Ă©tait l’unique Ă©lĂ©ment Ă  ajouter pour notre image, on peut donc quitter l’interprĂ©teur de commande de notre conteneur temporaire :

[sur le conteneur]
# exit

Puis on peut utiliser commit pour crĂ©er une nouvelle image Ă  partir de notre conteneur temporaire. Lors de cette crĂ©ation d’image, on fixe un point d’entrĂ©e via la mĂ©tadonnĂ©e CMD qui exĂ©cute la commande figlet en lui demandant de lire la variable d’environnement MESSAGE :

$ podman container commit  figlet_template \
                           --change 'CMD figlet $MESSAGE' \
                           figlet:v1

Suite Ă  cette commande, nous avons créé une nouvelle image de conteneur stockĂ©e localement (dĂ©notĂ© localhost). On peut alors utiliser notre image de la maniĂšre suivante, en n’oubliant pas de fixer une valeur Ă  la variable d’environnement MESSAGE :

podman container run --rm --env MESSAGE="hello world" localhost/figlet:v1
 _          _ _                            _     _
| |__   ___| | | ___   __      _____  _ __| | __| |
| '_ \ / _ \ | |/ _ \  \ \ /\ / / _ \| '__| |/ _` |
| | | |  __/ | | (_) |  \ V  V / (_) | |  | | (_| |
|_| |_|\___|_|_|\___/    \_/\_/ \___/|_|  |_|\__,_|
Exemple 4. CrĂ©ation d’une image Automatic-Image-Converter Ă  partir d’un conteneur

Automatic-Image-Converter est un logiciel dĂ©veloppĂ© en Java, il nous faut donc Ă  minima un environnement fournissant la commande java. Si on fait l’hypothĂšse que le logiciel n’a pas Ă©tĂ© prĂ©-compilĂ©, alors nous avons Ă©galementbesoin d’un environnement avec les commandes nĂ©cessaires pour cette compilation.

Le code de Automatic-Image-Converter repose sur l’outil Maven pour la compilation, il nous faut donc une image nous fournissant la commande maven. On se rend sur la page de l’image officielle Maven, et on regarde les versions. On souhaite Java en version 17, et on souhaite utiliser Eclipse Temurin comme distribution Java Ă  la fois pour la compilation et l’exĂ©cution. On choisit donc l’Ă©tiquette 3-eclipse-temurin-17 comme image de base pour notre conteneur temporaire :

$ podman container run -ti --name aic_template \
                       docker.io/maven:3-eclipse-temurin-17 \
                       bash
On choisit de dĂ©marrer explicitement notre conteneur temporaire avec le point d’entrĂ©e bash, et non avec la commande mvn qui est le point d’entrĂ©e par dĂ©faut de l’image maven.

En parallĂšle, dans un second terminal, on rĂ©cupĂšre le projet de dĂ©veloppement de Automatic-Image-Converter dans un rĂ©pertoire aic, puis on copie tout le code source au sein du conteneur Ă  l’emplacement prĂ©vu :

$ podman container cp ./aic simplestblog_template:/install

De retour dans le conteneur, on peut vĂ©rifier que tout est bien arrivĂ© Ă  l’emplacement prĂ©vu :

[sur le conteneur]
# cd /install
# ls
acceptance_testing  Containerfile.nobuild      Containerfile.twostages  README.adoc
ansible             Containerfile.singlestage  pom.xml                  src

Puis on peut utiliser mvn pour Ă  la fois installer les bibliothĂšques requises par Automatic-Image-Converter , et compiler Automatic-Image-Converter au passage :

[sur le conteneur]
# mvn package

Cette commande produit dans le conteneur le fichier /install/target/automatic-image-converter-1.0-SNAPSHOT.jar qui contient tout notre logiciel compilé avec ses dépendances.

À partir d’ici, notre conteneur contient exactement ce que l’on souhaite comme fichiers au sein de notre future image, Ă  savoir :

  • un environnement Java,

  • le logiciel que l’on souhaitera dĂ©ployer (Ă  savoir Automatic-Image-Converter),

  • les dĂ©pendances du dit logiciel (Ă  savoir les bibliothĂšques Java requises par Automatic-Image-Converter)

On peut donc quitter l’interprĂ©teur de commande de notre conteneur temporaire :

[sur le conteneur]
# exit

PrĂ©cĂ©demment, nous avons anticipĂ© la conception de notre image Automatic-Image-Converter avec un point d’entrĂ©e qui anticipe la dĂ©finition de certaines variables d’environnement en phase d’usage. On en dĂ©duit donc les mĂ©tadonnĂ©es suivantes :

WORKDIR /install
CMD java -jar target/automatic-image-converter-1.0-SNAPSHOT.jar /inputs /outputs $FORMAT $DURATION

Ainsi, Ă  partir du conteneur temporaire simplestblog_template, on peut crĂ©er une image appelĂ©e simplestblog et Ă©tiquettĂ©e v1 Ă  l’aide de la commande suivante :

$ podman container commit  aic_template \
                           --change 'WORKDIR /install' \
                           --change 'CMD java -jar target/automatic-image-converter-1.0-SNAPSHOT.jar /inputs /outputs $FORMAT $DURATION' \
                           aic:v1

En supposant avoir préparé localements les répertoires /data/input_images et /data/input_images, on peut ensuite par exemple utiliser notre image de conteneur de la maniÚre suivante :

podman container run   --name aic \
                       --detach \
                       --rm \
                       --mount type=bind,source="/data/input_images",target="/inputs" \
                       --mount type=bind,source="/data/output_images",target="/outputs" \
                       --env FORMAT="png" \
                       --env DURATION="5000" \
                       localhost/aic:v1
Exemple 5. CrĂ©ation d’une image Simplestblog Ă  partir d’un conteneur

Simplestblog Ă©tant une application web dĂ©veloppĂ©e en Python, il nous faut donc Ă  minima un environnement fournissant la commande python. On choisit d’utiliser l’image officielle Python comme image de base. L’image en question est disponible sur le Docker Hub et on se rend sur sa page de description. On souhaite une version de l’image qui contienne Python version 3, et qui soit aussi lĂ©gĂšre (slim) que possible. On se dĂ©cide Ă  utiliser l’Ă©tiquette 3-slim.

$ podman container run -ti --name simplestblog_template \
                       docker.io/python:3-slim \
                       bash
On choisit de dĂ©marrer explicitement notre conteneur Python avec le point d’entrĂ©e bash, et non avec l’interprĂ©teur Python qui est le point d’entrĂ©e par dĂ©faut de l’image docker.io/python.

En parallĂšle, dans un second terminal, on rĂ©cupĂšre le projet de dĂ©veloppement de Simplestblog dans un rĂ©pertoire simplestblog, puis on copie tout le code source situĂ© dans le sous-rĂ©pertoire src au sein du conteneur Ă  l’emplacement prĂ©vu :

$ podman container cp simplestblog/src simplestblog_template:/install

De retour dans le conteneur, on peut vĂ©rifier que tout est bien arrivĂ© Ă  l’emplacement prĂ©vu :

[sur le conteneur]
# cd /install
# ls
Containerfile  Simplestblog.egg-info  example  instance        reports		 simplestblog
LICENSE.rst    dist		      htmlcov  pyproject.toml  requirements.txt  tests

Puis on peut utiliser pip pour installer les bibliothĂšques Python requises par Simplestblog :

[sur le conteneur]
# pip install -r requirements.txt

À partir d’ici, notre conteneur contient exactement ce que l’on souhaite comme fichiers au sein de notre future image, Ă  savoir :

  • un environnement Python,

  • le logiciel que l’on souhaitera dĂ©ployer (Ă  savoir Simplestblog),

  • les dĂ©pendances du dit logiciel (Ă  savoir les bibliothĂšques Python requises par Simplestblog)

On peut donc quitter l’interprĂ©teur de commande de notre conteneur temporaire :

[sur le conteneur]
# exit

PrĂ©cĂ©demment, nous avons anticipĂ© la conception de notre image Simplestblog avec un point d’entrĂ©e et une constante fixĂ©e dans la variable d’environnement SIMPLESTBLOG_SETTINGS_FILE. On en dĂ©duit donc les mĂ©tadonnĂ©es suivantes :

WORKDIR /install
CMD waitress-serve --call simplestblog:create_app
ENV SIMPLESTBLOG_SETTINGS_FILE=/config.py

Ainsi, Ă  partir du conteneur temporaire simplestblog_template, on peut crĂ©er une image appelĂ©e simplestblog et Ă©tiquettĂ©e v1 Ă  l’aide de la commande suivante :

$ podman container commit  simplestblog_template \
                           --change 'WORKDIR /install' \
                           --change 'CMD waitress-serve --call simplestblog:create_app' \
                           --change 'ENV SIMPLESTBLOG_SETTINGS_FILE=/config.py' \
                           simplestblog:v1

En supposant que l’on ait prĂ©parĂ© localement un fichier de configuration /data/simplestblog/test_config.py, on peut par exemple utiliser notre image de conteneur de la maniĂšre suivante :

podman container run --name simplestblog_app \
                       --detach \
                       --rm \
                       --mount type=bind,source=/data/simplestblog/test_config.py,target=/config.py \
                       --network simplestblog_network \
                       --publish 8100:5000 \
                       localhost/simplestblog:v1

4.2. Lister, inspecter des images

Pour lister toutes les images prĂ©sentes dans le stockage local :

$ podman image list
Exemple 6. Lister l’image Simplestblog

Suite Ă  la construction de l’image de Simplestblog prĂ©cĂ©demment, on liste les images prĂ©sentes sur le stockage local :

$ podman image list

REPOSITORY                TAG         IMAGE ID      CREATED        SIZE
localhost/simplestblog    v1          1bd95c71ec04  7 seconds ago  278 MB
docker.io/library/python  3-slim      c1c23a6632a4  13 days ago    136 MB

On observe que deux images sont prĂ©sentes :

  • La premiĂšre est l’image Simplestblog que nous avons créé, avec pour nom simplestblog et comme Ă©tiquette v1

  • La seconde est l’image Python qui a Ă©tĂ© tĂ©lĂ©chargĂ©e lors de l’instruction FROM, et qui a Ă©tĂ© utilisĂ©e comme base pour construire l’image Simplestblog.

Pour inspecter tous les dĂ©tails d’une image du stockage local, on peut faire :

$ podman image inspect <référence d'image>

Bien que trĂšs utile dans certains contextes, cette commande produit un rĂ©sultat en JSON trĂšs verbeux et assez peu lisible. À n’utiliser que lorsqu’on souhaite vĂ©rifier certaines propriĂ©tĂ©s d’une image (format de stockage, variables d’environnement, etc.).

4.3. Interagir avec un registre distant

Si on souhaite interagir avec un registre de conteneurs distant avec accĂšs restreint, il est d’abord nĂ©cessaire de s’authentifier avec un compte utilisateur auprĂšs de ce registre.

Pour s’authentifier auprĂšs d’un registre distant :

$ podman login <nom d'hĂŽte registre distant>

On doit ensuite saisir son nom d’utilisateur et son mot de passe. Si le login est validĂ©, alors toutes les futures commandes Podman pourront utiliser le registre privĂ©.

Cette authentification n’est pas permanente et ne dure que le temps d’une session utilisateur Linux. Il sera par exemple nĂ©cessaire de refaire cette authentification Ă  chaque sĂ©ance de TP.
Exemple 7. Connexion au registre d’images du Gitlab Nantes UniversitĂ©

Le registre d’images du Gitlab Nantes UniversitĂ© a le fonctionnement suivant :

  • Chaque projet de dĂ©veloppement peut hĂ©berger ses propres images de conteneurs, visibles dans l’interface Ă  l’aide du menu DĂ©ploiement → Registre de conteneur.

  • Pour pouvoir envoyer une image de conteneur au sein d’un projet, il est nĂ©cessaire d’utiliser un compte Gitlab qui possĂšde le statut Developer, Maintainer ou Owner au sein du projet.

  • Pour pouvoir tĂ©lĂ©charger une image de conteneur d’un projet, il y a deux cas de figure :

    • Si le projet est public, alors les images sont tĂ©lĂ©chargeables par n’importe qui sans besoin de compte Gitlab au prĂ©alable.

    • Si le projet est privĂ©, alors seul un membre du projet peut tĂ©lĂ©charger une image du projet.

Ainsi, pour que vous puissiez correctement envoyer et rĂ©cupĂ©rer des images, en particulier celles de vos projets de dĂ©veloppement privĂ©s, il vous faut d’abord faire :

$ podman login docker-registry.univ-nantes.fr

puis saisir votre identifiant et mot de passe université.

Pour tĂ©lĂ©charger manuellement une image distante vers le stockage local :

$ podman image pull <reference d'image>

Pour publier son image sur un registre distant :

$ podman image push <reference d'image locale> \
                    <chemin d'image distante>
Un push nĂ©cessite toujours un login prĂ©alable, et nĂ©cessite d’avoir les droits nĂ©cessaires pour dĂ©poser l’image Ă  l’emplacement indiquĂ© dans le chemin d’image distante.
Exemple 8. Publication de l’image Simplestblog

On souhaite publier l’image simplestblog:v1 construite prĂ©cĂ©demment. On suppose avoir dĂ©jĂ  fait l’authentification nĂ©cessaire auprĂšs du registre de conteneur de Nantes UniversitĂ©.

Le projet de développement de Simplestblog est hébergé sur le Gitlab Nantes Université et est visible sur cette page. Le chemin complet au sein du Gitlab (et, par extension, au sein du registre de conteneurs) est developpement_exploitation/resources/simplestblog.

À l’aide d’un compte utilisateur qui a suffisamment de droits dans ce projet, on exĂ©cute la commande suivante :

$ podman image push simplestblog:v1 \
                    docker-registry.univ-nantes.fr/gl/developpement_exploitation/resources/simplestblog:v1

Suite Ă  cela, sur la page du projet sur le Gitlab Nantes UniversitĂ©, on se rend sur la page DĂ©ploiement → Registre de conteneur, et on constate la prĂ©sence de l’Ă©tiquette v1 au cĂŽtĂ© des autres versions disponibles de l’image :

simplestblog gitlab registry annotated.drawio

4.4. Supprimer des images

Pour supprimer une image du stockage local :

$ podman image rm <référence image>
Cette commande Ă©chouera s’il existe localement au moins un conteneur instance de l’image rĂ©fĂ©rencĂ©e. Il faudra donc d’abord supprimer les conteneurs concernĂ©s.

Pour supprimer toutes les images actuellement non utilisĂ©es par des conteneurs :

$ podman image prune --all

5. Références