Construire une image¶
Dans cette section, il est expliqué comment construire une image Docker à partir de zéro.
Dockerfile¶
Un Dockerfile
est comme une recette. C'est un fichier texte contenant des instructions et leurs arguments permettant à Docker de construire automatiquement des images. C'est l'élément de base d'une image.
Voici une liste d'instructions:
Instructions | Explications |
---|---|
FROM |
Pour spécifier l'image de base. |
LABEL |
Pour spécifier les informations de métadonnées de l'image Docker. |
RUN |
Pour exécuter des commandes pendant le processus de construction de l'image (e.g. commandes linux...). |
COPY |
Copie les fichiers et répertoires locaux dans l'image. |
ADD |
Similaire à l'instruction COPY avec des fonctionnalités supplémentaires comme l'extraction de tar uniquement locale et le support des URLs distantes. Cependant, préférez l'instruction COPY à ADD . |
ENV |
Définit les variables d'environnement à l'intérieur de l'image qui seront disponibles pendant la construction et aussi dans le conteneur en cours d'exécution. Pour définir uniquement les variables au moment de la construction, utilisez l'instruction ARG . |
ARG |
Définit les variables de construction avec une clé et une valeur. Les variables ARG ne sont pas disponibles dans le conteneur en cours d'exécution. Pour les variables persistantes dans un conteneur en cours d'exécution, utilisez ENV . |
WORKDIR |
Définit le répertoire de travail courant. Vous pouvez réutiliser cette instruction dans un fichier Docker pour définir un répertoire de travail différent. Si WORKDIR est défini, les instructions comme RUN , CMD , ADD , COPY , ou ENTRYPOINT sont exécutées dans ce répertoire. Préférez l'utilisation de WORKDIR plutôt que des instructions comme RUN cd .. |
USER |
Définit le nom d'utilisateur et l'UID lors de l'exécution du conteneur. Utilisez le pour définir un utilisateur non-root du conteneur. L'utilisateur doit être créé s'il n'existe pas. |
ENTRYPOINT |
Spécifie les commandes qui seront toujours exécutées au démarrage du conteneur Docker. ENTRYPOINT ne peut être "surchargé" qu'avec l'option --entrypoint . Il prend la forme de tableau JSON (ex : ENTRYPOINT ["ls","-l"] ) ou de texte. Il ne peut y avoir qu'une seule instruction ENTRYPOINT . Cette instruction n'est pas éxecutée lors de la phase de build mais lors de la phase de run . |
CMD |
Exécute une commande dans un conteneur en cours d'exécution.Elle peut aussi servir à spécifier des arguments par défaut qui seront envoyés à l'ENTRYPOINT ; dans ce cas-ci les instructions CMD et ENTRYPOINT doivent être spécifiées au format de tableau JSON. Il ne peut y avoir qu'une seule instruction CMD mais peut être remplacée par la CLI de Docker. Cette instruction n'est pas éxecutée lors de la phase de build mais lors de la phase de run . |
EXPOSE |
Spécifie le port à exposer pour le conteneur Docker. |
VOLUME |
Il est utilisé pour créer ou monter le volume pour le conteneur Docker. |
Mon premier build¶
Le but ici est de créer une image jupyter notebook personnalisée en y installant un plugin vscode
.
Selon la documentation officielle, dans la plupart des cas, il est préférable de placer chaque Dockerfile
dans un répertoire vide.
Ensuite, ajoutez à ce répertoire seulement les fichiers nécessaires à la construction du Dockerfile; donc :
- Créez un répertoire vide appelé
build
:
- Copiez-y le dossier
python_codes
:
- Créez un fichier nommé
Dockerfile
(il est possible de remplacer code-server_xxx_amd64.deb par code-server_xxx_arm64.deb pour les systèmes ARM):
FROM quay.io/jupyter/minimal-notebook:lab-4.2.6
LABEL maintainer="<amar.hami@lpnhe.in2p3.fr> & <aurelien.bailly-reyre@lpnhe.in2p3.fr>"
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Switch to root user to install packages:
USER root
# Container updated and some packages installed:
RUN apt-get update --yes && apt-get upgrade --yes &&\
apt-get install --yes --quiet --no-install-recommends \
curl \
bsdmainutils \
file \
man-db && \
apt-get --quiet clean && rm -rf /var/lib/apt/lists/*
# Code Server and server-proxy/vscode-proxy modules installed to integrate VSCode inside JupyerLab:
ENV CODE_VERSION=4.18.0
RUN curl -fOL https://github.com/coder/code-server/releases/download/v$CODE_VERSION/code-server_${CODE_VERSION}_amd64.deb \
&& dpkg -i code-server_${CODE_VERSION}_amd64.deb \
&& rm -f code-server_${CODE_VERSION}_amd64.deb
WORKDIR /tmp
RUN git clone https://github.com/betatim/vscode-binder \
&& cd vscode-binder \
&& pip install . \
&& cd $HOME \
&& rm -rf /tmp/vscode-binder
RUN mamba install -c conda-forge jupyter-server-proxy jupyter-vscode-proxy
# Jupyter ressource usage
#RUN mamba install -c conda-forge jupyter-resource-usage
# Cleaning
RUN mamba clean --all
# Switch back to jovyan to avoid accidental container running as root:
ENV USERNAME="jovyan"
USER $USERNAME
# Switch back to jovyan's home:
WORKDIR /home/$USERNAME
COPY --chown=jovyan:users python_codes work
Remarque
Aucune instruction ENTRYPOINT
ou CMD
n'apparaît ici. L'image résultante de ce Dockerfile
hérite en réalité de l'instruction CMD ["start-notebook.py"]
de l'image de base (jupyter/minimal-notebook:lab-4.0.7
) sur laquelle elle est construite.
La construction de l'image se fait avec la commande docker build
:
où les arguments:
-f
(--file
): permet de spécifier le nom du fichier contenant la recette. Si le fichier se nommeDockerfile
, l'option-f Dockerfile
peut être omise;-t
(--tag
) : spécifie le nom de l'image et optionnellement un tag. Si celui-ci n'est pas spécifié l'image aura automatiquement le tag par défautlatest
;<path>
: spécifie le chemin du répertoire contenant leDockerfile
.
Exercice
Construire l'image à partir du Dockerfile
ci-dessus et vérifier qu'elle a bien été créée avec le tag latest
.
On peut ajouter un ou plusieurs tag à une image a posteriori:
Exercice
Ajouter un tag v1
à l'image créée et vérifier ensuite son ajout.
Solution
On remarque quemyjupyter:latest
et myjupyter:v1
ont le même IMAGE ID
.
Layer cache¶
Une image Docker est consituée de couches, layers en anglais. Ces couches sont créées par chacune des instructions qui sont exécutées linéairement lors de la génération de l'image (cf le schéma ci-dessous qui provient du site docker). L'image finale créée n'est autre qu'un pointeur pointant vers la derniére couche générée.
Lorsque l'on lance le build de l'image, docker tente d'utiliser des couches déjà existentes (générées lors de précédentes constructions d'image). Ce mécanisme de cache permet de gagner du temps lors la construction d'une image en évitant ainsi de regénérer inutilement les couches intermédiaires inchangées. Si l'une d'entre elles se trouve modifiée par rapport au précédent build, alors elle doit être regénérée ainsi que les couches suivantes. L'ordre des instructions a donc son importance si l'on souhaite bénéficier au mieux du mécanisme de cache.
Pour voir l'ensemble des couches intermédiaires d'une image, vous pouvez utiliser la commande:
Exercice
Voir l'ensemble des couches de l'image générée précedemment. Puis pour mettre en évidence l'efficacité du mécanisme de cache, décommenter la ligne dans le Dockerfile
:
Solution
ENTRYPOINT
ou CMD
?¶
Les instructions ENTRYPOINT
et CMD
ont des effets similaires lorsqu'elles sont utilisées séparément. Mais elles peuvent être aussi combinées, ce qui peut s'avérer intéressant dans certains cas.
Comparez l'effet de ces deux instructions à l'aide d'exemples.
Ces deux instructions ne sont pas éxecutés lors de la phase de build
mais lors de la phase de run
.
Tout d'abord créez un Dockerfile
contenant:
exemple
:
Exercice
Essayer d'anticiper ce que font les commandes suivantes avant de les tester:
Solution
Le conteneur exécute la commande définie par l'ENTRYPOINT
à savoir echo Hello
.
L'instruction CMD
(optionnelle), quant à elle, sert à spécifier un arguement par défaut, ici World
à l'ENTRYPOINT
.
Donc par défaut lorsque le conteneur est exécuté, "Hello World" est affiché.
Il est possible de changer la valeur par défaut de CMD
par l'intermédiaire de la CLI docker à l'image de la deuxième et troisième commande.
Si l'on souhaite modifier la commande à exécuter dans le conteneur, ici htop
, il faut le préciser à l'aide de l'option --entrypoint <commande>
.Dans le même répertpoire créer un fichier Dockerfile.cmd
:
À l'aide d'un fichier Dockerfile.cmd
contenant la recette suivante:
créez une image exemple
avec le tag cmd
:
Exercice
Exécuter les commandes suivante et conclure :
Solution
Ici, le conteneur exécute par défaut la commande qui se trouve dans l'instruction CMD
. Comme dans l'exercice précédent, on peut surcharger cette instruction
à l'aide de la CLI Docker avec une commande qui existe dans le conteneur. Ce qui n'est pas le cas avec la seconde commande, puisque You
n'est pas une commande.