Aller au contenu

Gérer les images des conteneurs Docker

Objectifs :

  • Télécharger une image

  • Afficher l'historique d'une image

  • Ajouter un tag à une image

  • Construire une première image depuis un Dockerfile

  • Supprimer une image

  • Afficher l'espace disque utilisé et faire le ménage sur le système

Une architecture en couches

Docker utilise un modèle en couches - ou layers - pour les conteneurs. Pour en avoir une vague idée, pensez au fonctionnement de la commande diff sous Linux. Une image de conteneur occupe relativement peu d'espace disque parce qu'elle ne garde que la trace des modifications apportées à une image. Ces modifications sont enregistrées dans une couche - ou un layer - à part.

À titre d'exemple, si toutes vos images sont basées sur une image Alpine Linux, les images nouvellement créées n'occuperont de l'espace disque que pour ce que vous avez ajouté à l'image Alpine de base.

Les couches d'une image sont en lecture seule. Chaque couche est un delta - ou une différence - de tous les changements par rapport à la couche sous-jacente.

Si vous êtes familiarisés avec le logiciel de gestion de versions Git, dites-vous que l'architecture en couches de Docker est organisée de manière similaire. Chaque fois que le code source est modifié, seul ce commit est enregistré directement, mais il renverra toujours à l'ensemble du code.

Cette manière de procéder permet non seulement de réduire l'espace de stockage utilisé. Elle accélère aussi considérablement la reconstruction des images après une modification mineure.

Télécharger une image

Jetons un œil sur les couches qui constituent une image. Pour commencer, téléchargez l'image du serveur web Nginx :

$ docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
f7a1c6dad281: Pull complete
4d3e1d15534c: Pull complete
9ebb164bd1d8: Pull complete
59baa8b00c3c: Pull complete
a41ae70ab6b4: Pull complete
e3908122b958: Pull complete
Digest: sha256:1c13bc6de5dfca749c377974146ac05256791ca2fe1979fc8e8278bf0121d285
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest

La commande docker pull télécharge l'image dont le nom est fourni en argument vers l'hôte local.

Une fois que l'image a été rapatriée, la commande docker images (ou docker image ls) permet de vérifier sa présence sur le système :

$ docker image ls
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
almalinux    latest    6bfdc3e60b10   2 days ago     198MB
nginx        latest    c919045c4c2b   10 days ago    142MB
rockylinux   latest    210996f98b85   2 months ago   205MB
alpine       latest    c059bfaa849c   3 months ago   5.59MB
centos       latest    5d0da3dc9764   5 months ago   231MB

Sans trop rentrer dans les détails, la commande docker history nous permet d'afficher la séquence de commandes qui ont été utilisées pour construire l'image :

$ docker history nginx
IMAGE          CREATED       CREATED BY ...
c919045c4c2b   10 days ago   /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon…
<missing>      10 days ago   /bin/sh -c #(nop)  STOPSIGNAL SIGQUIT
<missing>      10 days ago   /bin/sh -c #(nop)  EXPOSE 80
<missing>      10 days ago   /bin/sh -c #(nop)  ENTRYPOINT ["/docker-entr… 
<missing>      10 days ago   /bin/sh -c #(nop) COPY file:09a214a3e07c919a…
<missing>      10 days ago   /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7…
<missing>      10 days ago   /bin/sh -c #(nop) COPY file:0b866ff3fc1ef5b0…
<missing>      10 days ago   /bin/sh -c #(nop) COPY file:65504f71f5855ca0…
<missing>      10 days ago   /bin/sh -c set -x     && addgroup --system -…
<missing>      10 days ago   /bin/sh -c #(nop)  ENV PKG_RELEASE=1~bullseye
<missing>      10 days ago   /bin/sh -c #(nop)  ENV NJS_VERSION=0.7.2
<missing>      10 days ago   /bin/sh -c #(nop)  ENV NGINX_VERSION=1.21.6
<missing>      10 days ago   /bin/sh -c #(nop)  LABEL maintainer=NGINX Do…
<missing>      11 days ago   /bin/sh -c #(nop)  CMD ["bash"]
<missing>      11 days ago   /bin/sh -c #(nop) ADD file:d48a85028743f16ed…

Afficher l'identifiant complet d'une image

Ce qu'il faut retenir ici, c'est que le résultat de la commande docker history affiche ligne par ligne chacune des couches successives avec - en abrégé - les commandes correspondantes pour les construire.

L'option --no-trunc indique à Docker de ne pas tronquer l'affichage :

$ docker images --no-trunc
...

Cette fois-ci, Docker n'a pas raccourci les sommes de contrôle SHA256 pour chaque image. Autrement dit, nous pouvons voir le nom complet que Docker utilise effectivement pour se référer à l'image en interne, par exemple :

sha256:c919045c4c2b0b0007c606e763ed2c830c7b1d038ce878a3c0d6f5b81e6ab80b

Les tags des images Docker

Un tag est utilisé pour transmettre des informations utiles. Dans la plupart des cas, le tag vous donnera la version précise d'une image. En l'absence de tag, c'est le tag par défaut latest qui sera utilisé :

TAG: latest

Pour créer un tag pour une image donnée, utilisez la commande docker tag :

$ docker tag --help

Usage:  docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]

Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE

Admettons que nous comptons utiliser l'image Nginx pour héberger notre blog :

$ docker tag nginx:latest nginx:monblog_prod

La colonne IMAGE ID de l'affichage de docker image ls nous montre exactement la même somme de hachage (ou le même ID) c919045c4c2b pour les images nginx:latest et nginx:monblog_prod. Docker n'a pas utilisé 142 Mo d'espace disque supplémentaire pour refaire un tag à partir de l'image originale, mais il a simplement créé un alias vers l'original :

$ docker image ls
REPOSITORY   TAG            IMAGE ID       CREATED        SIZE
almalinux    latest         6bfdc3e60b10   2 days ago     198MB
nginx        latest         c919045c4c2b   11 days ago    142MB
nginx        monblog_prod   c919045c4c2b   11 days ago    142MB
rockylinux   latest         210996f98b85   2 months ago   205MB
alpine       latest         c059bfaa849c   3 months ago   5.59MB
centos       latest         5d0da3dc9764   5 months ago   231MB

Ne vous inquiétez pas si par mégarde vous supprimez l'image originale. La nouvelle image restera en place. Docker sait gérer intelligemment ce genre de cas de figure.

Les Dockerfiles

  • Ouvrez la page d'accueil de Docker Hub et effectuez une recherche sur nginx.

  • Cliquez sur l'image officielle de Nginx.

  • Sur la page de Nginx, repérez la section Supported tags and respective Dockerfile links.

  • Cliquez sur le tag en question (en l'occurrence latest) pour afficher le Dockerfile correspondant.

Voici une version simplifiée du Dockerfile pour Nginx :

FROM debian:bullseye-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"

ENV NGINX_VERSION   1.21.6
ENV NJS_VERSION     0.7.2
ENV PKG_RELEASE     1~bullseye

RUN apt-get update \
    && apt-get install -y nginx \
    && apt-get clean

RUN ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log

COPY index.html /var/www/html/
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Le Dockerfile vous permet de regarder de près l'ensemble des commandes qui ont servi à construire l'image. Non content de cela, il vous permet également de reconstruire l'image en y apportant éventuellement quelques modifications.

L'instruction FROM indique à Docker l'image de base qu'il faudra utiliser. Si vous exécutez une commande docker build avec ce Dockerfile, la ligne FROM signifie qu'une couche sera créée au-dessus de l'image de base fournie en argument :

FROM debian:bullseye-slim

L'entrée LABEL est l'endroit idéal pour fournir vos coordonnées de contact :

LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"

Les instructions ENV introduisent une série de variables d'environnement tout comme celles utilisées par le shell Bash :

ENV NGINX_VERSION   1.21.6
ENV NJS_VERSION     0.7.2
ENV PKG_RELEASE     1~bullseye

L'instruction RUN indique à Docker d'exécuter une commande et contribuer ainsi une brique logicielle pour la construction de l'image. Certaines commandes devront être adaptées pour fonctionner correctement. Les gestionnaires de paquets, par exemple, ne pourront pas fonctionner en mode interactif.

RUN apt-get update \
    && apt-get install -y nginx \
    && apt-get clean

RUN ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log
  • Ici, on utilise la double esperluette && pour concaténer les instructions RUN, ce qui évite la création d'un nouveau layer pour chaque commande individuelle. Le but de cette opération est de réduire le nombre de couches au-dessus de l'image de base.

  • Tout le travail du gestionnaire de paquets est effectué en une seule couche.

  • La commande apt-get clean supprime les paquets téléchargés, ce qui permet de réduire la taille de l'image résultante.

La commande COPY copie un fichier depuis l'hôte local vers l'image lors de l'opération de construction de l'image :

COPY index.html /var/www/html/
  • L'instruction COPY est généralement utilisée pour copier des fichiers de configuration de taille modeste dans une image.

La ligne EXPOSE commande à Docker d'ouvrir (ou exposer) le port 80 lorsqu'un conteneur est lancé depuis cette image.

EXPOSE 80
  • Le port 80 est le port standard pour servir du trafic HTTP, ce qui est exactement ce que fait Nginx.

Enfin, l'instruction CMD exécute le binaire Nginx depuis le conteneur. Dans notre cas de figure, deux options sont utilisées :

CMD ["nginx", "-g", "daemon off;"]
  • La syntaxe en vigueur est un tableau (array) JSON, qui signifie JavaScript Object Notation.

  • L'option daemon off fait tourner Nginx en avant-plan du conteneur. Sans cette précision, le conteneur s'arrêterait net juste après son lancement.

  • En règle générale, la plupart des applications nécessitent des modifications mineures lorsqu'elles sont conteneurisées.

Créez un répertoire ~/Test et rangez-y le Dockerfile. Il nous manque encore un fichier index.html pour la page par défaut. Nous pouvons le créer comme ceci :

$ echo "<h1>Mon serveur Nginx</h1>" > ~/Test/index.html

À présent je peux lancer la construction de l'image en utilisant la commande docker build :

$ ls
Dockerfile  index.html
$ docker build -t mon_nginx .
Sending build context to Docker daemon  3.072kB
Step 1/10 : FROM debian:bullseye-slim
bullseye-slim: Pulling from library/debian
f7a1c6dad281: Already exists
Digest: sha256:d5cd7e54530a8523168473a2dcc30215f2c863bfa71e09f77f58a085c419155b
Status: Downloaded newer image for debian:bullseye-slim
 ---> bfb07174f19e
...
Step 9/10 : EXPOSE 80
 ---> Running in 7dad932bda6d
Removing intermediate container 7dad932bda6d
 ---> 38cbfece5f30
Step 10/10 : CMD ["nginx", "-g", "daemon off;"]
 ---> Running in 23b746b20d51
Removing intermediate container 23b746b20d51
 ---> 25048f7356b2
Successfully built 25048f7356b2
Successfully tagged mon_nginx:latest
  • L'option -t me permet de spécifier un nom et/ou un tag pour l'image.

  • N'oubliez pas le point . qui dit à Docker d'utiliser le Dockerfile situé dans le répertoire courant.

Maintenant que nous avons l'image, nous pouvons l'utiliser pour lancer un conteneur :

$ docker run -dit mon_nginx
1a45be278e3e6a3008bdef4d20abd1749172dd45caf938caa736278dbd179846

Jetez un œil aux différentes couches (layers) qui constituent notre image :

$ docker history mon_nginx

Supprimer les images

À présent, faisons un peu de ménage sur notre système :

$ docker rmi nginx:monblog_prod
Untagged: nginx:monblog_prod
  • Docker nous informe qu'il a supprimé le tag de l'image et non pas l'image elle-même, étant donné qu'il ne s'agissait que d'un alias.
$ docker rmi nginx:latest
Untagged: nginx:latest
Untagged: nginx@sha256:1c13bc6de5dfca749c377974146ac05256791ca2fe1979fc8e8...
Deleted: sha256:c919045c4c2b0b0007c606e763ed2c830c7b1d038ce878a3c0d6f5b81e6ab80b
Deleted: sha256:397dbc5767fa9031bfbe1523c4f0de80ad7ce8103b8cc2b58e6f36554c7f93eb
Deleted: sha256:347cfffd04bb8392ba1999a911a3d9b5c2327a8b516a943d17965b41c88e1216
Deleted: sha256:1b611a5b7c5d88f05e9eedeec09ba67bb81821a18dd21c6a0644a2e5017ac524
Deleted: sha256:78dd14d2a308dfd875fc3baae9b8b2e1a15c7fbee828cdbdd8e55b18c64c6128
Deleted: sha256:f5d1b591ffdcdfd05874cf66cb351506c6ef683cb58d2883bde24819b5a4f37d
  • Cette fois-ci, le résultat de la commande docker rmi est différent. Nous avons effectivement supprimé un certain nombre de couches, et ce faisant nous avons libéré de l'espace disque.

Dans certains cas il peut être nécessaire de forcer la suppression d'une image :

$ docker rmi -f debian:bullseye-slim

Rappelez-vous que chaque commande dispose d'une aide en ligne contextuelle. En l'occurrence, voici ce que nous avons fait pour découvrir l'option -f :

$ docker rmi --help

Usage:  docker rmi [OPTIONS] IMAGE [IMAGE...]

Remove one or more images

Options:
  -f, --force      Force removal of the image
      --no-prune   Do not delete untagged parents

Faire le ménage sur le disque dur

Docker utilise une terminologie qui lui est propre pour désigner toutes les couches d'images qui ne sont utilisées par aucune image disposant d'un tag. Ces layers inutilisés sont des images en suspens (ou dangling images).

La sous-commande prune permet de supprimer ces images en suspens :

$ docker image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y

Pour supprimer aussi bien les images inutilisées que les images en suspens, utilisez la commande suivante :

$ docker system prune -a
WARNING! This will remove:
  - all stopped containers
  - all networks not used by at least one container
  - all images without at least one container associated to them
  - all build cache

Are you sure you want to continue? [y/N] y
...

Si vous souhaitez savoir la quantité d'espace disque utilisé par Docker, invoquez la commande suivante :

$ docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          4         2         229.8MB   85.99MB (37%)
Containers      2         2         1.097kB   0B (0%)
Local Volumes   0         0         0B        0B
Build Cache     0         0         0B        0B

Exercice

  • Rendez-vous sur Docker Hub.

  • Téléchargez la dernière image Docker du magasin de données en mémoire Memcached.

  • Téléchargez l'image Docker du serveur web Apache dans sa mouture 2-alpine.

  • Affichez les images que vous venez de télécharger.

  • Affichez l'espace disque occupé par ces images.

  • Supprimez l'image de Memcached.

  • Supprimez l'image d'Apache.

  • Vérifiez que vous n'avez plus aucune image Docker locale sur votre système.


La rédaction de ces cours demande du temps et des quantités significatives de café espresso. Vous appréciez cette formation ? Offrez un café au formateur en cliquant sur la tasse.