```
@@ -67,7 +67,7 @@ intermédiaire.
::::: {.warning}
-Par cette méthode, vous êtes limité à 10 scans par mois.
+Par cette méthode, vous êtes limité à 10 scans par mois avec un compte gratuit.
:::::
@@ -102,17 +102,17 @@ commencer notre analyse :
```
-42sh$ docker scan nemunaire/fic-admin
+42sh$ docker scan nemunaire/youp0m
-Testing nemunaire/fic-admin...
+Testing nemunaire/youp0m...
Package manager: apk
-Project name: docker-image|nemunaire/fic-admin
-Docker image: nemunaire/fic-admin
+Project name: docker-image|nemunaire/youp0m
+Docker image: nemunaire/youp0m
Platform: linux/amd64
-Base image: alpine:3.14.2
+Base image: alpine:3.16.2
-✓ Tested 16 dependencies for known vulnerabilities, no vulnerable paths found.
+✓ Tested 15 dependencies for known vulnerabilities, no vulnerable paths found.
According to our scan, you are currently using the most secure version of
the selected base image
@@ -142,9 +142,9 @@ Package manager: deb
Project name: docker-image|mysql
Docker image: mysql
Platform: linux/amd64
-Base image: mysql:8.0.26
+Base image: mysql:8.0.30
-Tested 135 dependencies for known vulnerabilities, found 79 vulnerabilities
+Tested 119 dependencies for known vulnerabilities, found 24 vulnerabilities
According to our scan, you are currently using the most secure version of
the selected base image
@@ -152,8 +152,8 @@ the selected base image
Ce dernier exemple est sans appel : `mysql` est une image officielle, et sa
-dernière version à l'écriture de ses lignes contient pas moins de 79
-vulnérabilités dont 11 *high*.
+dernière version à l'écriture de ses lignes contient pas moins de 24
+vulnérabilités dont 9 *high* (pourtant corrigées dans des versions suivantes).
## Trivy
@@ -176,16 +176,16 @@ Tentons à nouveau d'analyser l'image `mysql` :
```
-42sh$ docker run --rm aquasec/trivy mysql
-2021-09-22T10:27:46.509Z INFO Need to update DB
-2021-09-22T10:27:46.509Z INFO Downloading DB... 100.00% 14.41 MiB p/s 2s
-2021-09-22T10:27:56.556Z INFO Detected OS: debian
-2021-09-22T10:27:56.556Z INFO Detecting Debian vulnerabilities...
-2021-09-22T10:27:56.579Z INFO Number of language-specific files: 0
+42sh$ docker run --rm aquasec/trivy image mysql
+INFO Need to update DB
+INFO Downloading DB... 100.00% 14.41 MiB p/s 2s
+INFO Detected OS: oracle
+INFO Detecting Oracle vulnerabilities...
+INFO Number of language-specific files: 0
-mysql (debian 10.10)
-====================
-Total: 158 (UNKNOWN: 5, LOW: 19, MEDIUM: 64, HIGH: 61, CRITICAL: 9)
+mysql (oracle 8.6)
+==================
+Total: 5 (UNKNOWN: 0, LOW: 0, MEDIUM: 4, HIGH: 1, CRITICAL: 0)
```
@@ -193,24 +193,24 @@ Les résultats sont un peu différents qu'avec `docker scan`, mais on constate
que l'image `mysql` contient vraiment de nombreuses vulnérabilités. Même si
elles ne sont heureusement pas forcément exploitables directement.
-Voyons maintenant s'il y a des différentes avec l'image `nemunaire/fic-admin` :
+Voyons maintenant s'il y a des différentes avec l'image `nemunaire/youp0m` :
```
-42sh$ docker run --rm aquasec/trivy nemunaire/fic-admin
-2021-09-22T10:29:48.091Z INFO Need to update DB
-2021-09-22T10:29:48.091Z INFO Downloading DB... 100.00% 15.98 MiB p/s 1s
-2021-09-22T10:29:51.902Z INFO Detected OS: alpine
-2021-09-22T10:29:51.902Z INFO Detecting Alpine vulnerabilities...
-2021-09-22T10:29:51.903Z INFO Number of language-specific files: 1
-2021-09-22T10:29:51.903Z INFO Detecting gobinary vulnerabilities...
+42sh$ docker run --rm aquasec/trivy image nemunaire/youp0m
+INFO Need to update DB
+INFO Downloading DB... 100.00% 15.98 MiB p/s 1s
+INFO Detected OS: alpine
+INFO Detecting Alpine vulnerabilities...
+INFO Number of language-specific files: 1
+INFO Detecting gobinary vulnerabilities...
-nemunaire/fic-admin (alpine 3.14.2)
+nemunaire/youp0m (alpine 3.16.2)
===================================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
-srv/admin (gobinary)
+srv/youp0m (gobinary)
====================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
```
@@ -218,7 +218,7 @@ Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
Nous pouvons remarque que Trivy, en plus de faire l'analyse statique des
vulnérabilités de l'image, a aussi fait une analyse des dépendances du binaire
-`/srv/admin`.
+`/srv/youp0m`.
Trivy est en effet capable de rechercher des vulnérabilités par rapport aux
dépendances connues de certains langages : Python, PHP, Node.js, .NET, Java,
diff --git a/tutorial/dockerfiles/dockerfile.md b/tutorial/dockerfiles/dockerfile.md
index 75230ac..1c03c42 100644
--- a/tutorial/dockerfiles/dockerfile.md
+++ b/tutorial/dockerfiles/dockerfile.md
@@ -8,7 +8,7 @@ que l'on a réussi à faire précédemment en utilisant le `Dockerfile` suivant
```dockerfile
-FROM ubuntu:latest
+FROM ubuntu:jammy
RUN apt-get update
RUN apt-get install -y nano
@@ -21,7 +21,16 @@ est suivie de ses arguments.
Dans notre exemple, nous utilisons `FROM`{.dockerfile} qui indique une image de
départ à utiliser ; `RUN`{.dockerfile} est une commande qui sera exécutée dans
-le conteneur, dans le but de le construire.
+le conteneur intermédiaire, dans le but de construire l'image. De la même
+manière que les `docker container run` de la partie précédente.
+
+::::: {.warning}
+
+Vous avez remarqué que la première instruction que l'on utilise est
+`FROM`. Chaque image construite par un `Dockerfile` doit dépendre d'une autre
+image. Ici nous avons choisi de partir de l'image `ubuntu`.
+
+:::::
Pour lancer la construction de la nouvelle image, créons un nouveau dossier ne
contenant que notre fichier `Dockerfile`, plaçons-nous ensuite dedans, puis
@@ -33,6 +42,23 @@ docker image build --tag=my_editor .
```
+On utilise l'option `--tag` pour donner un nom et un tag à l'image qui
+résultera de l'exécution de cette construction.
+
+::::: {.warning}
+
+#### Attention de ne pas oublier le point à la fin de la commande ! {-}
+
+Vous n'êtes plus sans savoir que Docker se compose d'un client et d'un
+serveur. Et c'est la partie serveur qui va s'occuper de construire l'image.
+
+Le client transmet donc tout le contexte autour du Dockerfile (les fichiers,
+dossiers, sons-dossiers) à partir du chemin qu'on lui indique en dernier
+argument. Le point représente donc ici simplement le dossier courant. Tous les
+fichiers et dossiers présents ici seront transmis au daemon.
+
+:::::
+
Une fois la construction de l'image terminée, nous pouvons la lancer et
constater l'existence de notre éditeur favori :
@@ -85,13 +111,17 @@ RUN service mysqld start && mysql -u root -p toor virli < /db.sql
```
-Après le `RUN`{.dockerfile}, MySQL sera de nouveau tué.\
+Après le `RUN`{.dockerfile}, MySQL sera de nouveau tué, mais la seconde
+commande aura entre-temps pu ajouter des données.\
-En aucun cas, une commande exécutée par un `RUN`{.dockerfile} se retrouvera en
+::::: {.warning}
+
+**En aucun cas, une commande exécutée par un `RUN`{.dockerfile} se retrouvera en
cours d'exécution lorsque l'on invoquera un conteneur par `docker container
run`. Seul la commande fournie par l'utilisateur ou la commande par défaut de
-l'image sera exécutée au lancement d'un conteneur.
+l'image sera exécutée au lancement d'un conteneur.**
+:::::
### Exposer des ports
@@ -131,6 +161,51 @@ Dans un autre terminal, lançons un `docker container ls`, pour consulter la col
Rendez-vous ensuite dans votre navigateur sur
.
+
+### Copier des fichiers dans l'image
+
+Une autre action très courante est de vouloir recopier un fichier ou un binaire
+dans notre image : un fichier de configuration, un produit de compilation, des
+scripts pour contrôler l'exécution, ...
+
+On va utiliser pour cela l'instruction `COPY` :
+
+
+```
+COPY myconfig.conf /etc/nginx/conf.d/my.conf
+```
+
+
+Cette instruction permet également de copier l'arborescence d'un dossier :
+
+
+```
+COPY myconfs/ etc/nginx/conf.d/
+COPY mywebsite /usr/share/nginx/html/
+```
+
+
+::::: {.warning}
+
+Le comportement de la copie de dossier est différente du comportement que l'on
+a l'habitude d'avoir avec `cp -r`. Si la source du `COPY` est un dossier, c'est
+son contenu qui sera recopié récursivement, habituellement avec `cp` le dossier
+recopié puis son contenu.
+
+Pour obtenir le même comportement, il faut bien indiquer une cible
+incluant le nom du dossier :
+
+
+```
+COPY docker-entrypoint.d /docker-entrypoint.d
+```
+
+
+Le dossier sera créé s'il n'existe pas, et le contenu du dossier source ser
+recopié.
+
+:::::
+
:::::: {.exercice}
#### À vous de jouer {-}
@@ -166,11 +241,76 @@ build`.
Les couches du cache peuvent être partagées entre plusieurs conteneurs, c'est
ainsi que vous pouvez partager facilement une plus grosse partie du système de
-fichiers (rappelez-vous le principe d'union FS).
+fichiers.\
-Pour profiter du cache, on va placer de préférence les étapes les plus
-génériques (qui seraient les plus susceptibles d'apparaître dans d'autres
-images), en haut du `Dockerfile`.
+Pour profiter au mieux du cache, on place les instructions qui sont le moins
+susceptibles de changer en haut du `Dockerfile`, celles qui changent le plus
+régulièrement à la fin. Ainsi, lorsqu'une reconstruction de l'image sera
+nécessaire, on gagnera du temps puisque le cache sera utilisé jusqu'à la
+première instruction changeante. Un `Dockerfile` bien ordonné peu facilement
+faire gagner de nombreuses minutes à ses utilisateurs.
+
+::::: {.question}
+
+#### Quelle place cela prend-t-il sur mon disque ? {-}
+
+Nous pouvons afficher la taille de chaque image via la commande `docker image
+ls` :
+
+
+```
+42sh$ docker image ls
+REPOSITORY TAG IMAGE ID CREATED SIZE
+nginx latest 2d389e545974 6 days ago 142MB
+debian stable 9b4953ae981c 7 days ago 124MB
+nemunaire/youp0m latest 2c06880e48aa 12 days ago 25MB
+```
+
+
+Si vous avez beaucoup d'images, cela peut paraître beaucoup, mais rappelez-vous
+que les images sont composées de couches qui sont souvent partagées entre
+plusieurs conteneurs.
+
+Si on regarde l'espace vraiment utilisé, il est moindre :
+
+
+```
+42sh$ docker system df
+TYPE TOTAL ACTIVE SIZE RECLAIMABLE
+Images 3 3 167MB 0B
+Containers 0 0 0B 0B
+Local Volumes 0 0 0B 0B
+Build Cache 0 0 0B 0B
+```
+
+
+:::::
+
+Les couches partagées sont un gain non négligeable pour l'espace de stockage !
+
+Par exemple, prenons le `Dockerfile` suivait :
+
+
+```Dockerfile
+FROM python:3.10
+COPY build /usr/lib/python/grapher
+EXPOSE 8080
+RUN pip install pillow pygal
+```
+
+
+Il y a de fortes chances pour que vous travailliez sur le code de
+l'application, le dossier `build` sera donc très souvent mis à jour, alors que
+les dépendances ne bougeront sans doute plus ...
+
+Avec un tel `Dockerfile`, dès que le dossier `build` sera mis à jour les
+dépendances seront à nouveau téléchargées, puisque toutes les couches suivant
+la première qui change sont invalidées.
+
+Une approche plus optimale serait donc de faire la `COPY` en dernier, car c'est
+l'opération qui changera le plus souvent. L'idéal étant que 90 % des
+reconstructions ne refassent que la dernière instruction, toutes les autres
+devraient être récupérées du cache.
### Métadonnées pures
@@ -181,7 +321,7 @@ sous forme de clef/valeur.
Une métadonnée courante[^MAINTAINER] est d'indiquer le nom du
mainteneur de l'image :
-[^MAINTAINER]: Voir par exemple :
+[^MAINTAINER]: Voir par exemple :
```dockerfile
@@ -222,6 +362,9 @@ constatez via un `docker container ls` que le conteneur s'arrête directement,
retirez cette option pour voir ce qui ne va pas, ou utilisez la commande
`docker container logs`.
+Comme les `LABEL`, ce n'est pas une instruction qui change régulièrement. On la
+place plutôt au début du `Dockerfile`.
+
### Construire son application au moment de la construction du conteneur ?
@@ -257,8 +400,8 @@ CMD ["/hello"]
```
-Dans cet exemple, deux conteneurs distincts sont créés : le premier à partir de
-l'image `gcc`, il contient tout le nécessaire pour compiler notre
+Dans cet exemple, deux images distinctes sont créées : la première à partir de
+l'image `gcc`, elle contient tout le nécessaire pour compiler notre
`hello.c`. Mais l'image finale (le dernier `FROM`{.dockerfile} de notre
`Dockerfile`) est l'image vide, dans laquelle nous recopions simplement le
produit de notre compilation.
@@ -302,10 +445,23 @@ sélectionnera ainsi avec l'option `--target` l'un ou l'autre en fonction de
l'environnement dans lequel on souhaite se déployer.
+### Déclarer des volumes
+
+Tout comme nous pouvons déclarer préalablement dans le `Dockerfile` les ports qui
+sont normalement exposés par le conteneur, nous pouvons déclarer les
+volumes. L'instruction pour cela est `VOLUME`.
+
+Il convient de l'utiliser pour déclarer les emplacements qui vont par défaut
+contenir des données à faire persister. Ce serait le cas de `/var/lib/mysql`
+pour les conteneurs MariaDB ou MySQL, `/images/` pour notre image `youp0m` ...
+
+
### D'autres instructions ?
-Consultez pour la liste
-complète des instructions reconnues.
+Nous avons fait le tour des principales instructions et de leurs différents
+usages *classiques*. Il existe quelques autres instructions que nous n'avons
+pas présentées ici, pour aller plus loin, consultez la référence sur :\
+
::::: {.exercice}
@@ -324,11 +480,16 @@ Pour compiler le projet, vous pouvez utiliser dans votre `Dockerfile`
```dockerfile
-FROM golang:1.16
+FROM golang:1.18
COPY . /go/src/git.nemunai.re/youp0m
WORKDIR /go/src/git.nemunai.re/youp0m
RUN go build -tags dev -v
```
+Remarquez la puissance de Docker : vous n'avez sans doute pas de compilateur Go
+installé sur votre machine, et pourtant, en quelques minutes et à partir du
+seul code source de l'application et d'un `Dockerfile`, vous avez pu compiler sur
+votre poste le binaire attendu. WOW, non ?
+
:::::
diff --git a/tutorial/dockerfiles/entrypoint.md b/tutorial/dockerfiles/entrypoint.md
index f75ec13..3e104cf 100644
--- a/tutorial/dockerfiles/entrypoint.md
+++ b/tutorial/dockerfiles/entrypoint.md
@@ -70,7 +70,7 @@ possibilité de le surcharger au moyen d'un argument :
```
-::::: {.exerice}
+::::: {.exercice}
## Personnalisation basique
@@ -187,3 +187,23 @@ You are not allowed to perform this request.
:::::
+
+
+## Étendre un `ENTRYPOINT` existant
+
+Vous venez de réaliser un script d'*entrypoint* pour votre conteneur. Il ajoute
+assurément de nombreuses fonctionnalités indispensables. Mais que se passe-t-il
+si quelqu'un souhaite étendre votre image, ou simplement pour ajouter une
+fonctionnalité ?
+
+La plupart des images officielles[^cf-nginx] prêtes à l'emploi disposent d'un
+dossier `/docker-entrypoint.d`, à la racine de l'image ; et d'un script
+d'*entrypoint* qui va se charger d'appeler chacun des scripts du dossier avant
+de lancer la commande par défaut.
+
+[^cf-nginx]: Consultez le dépôt de l'image `nginx` par exemple. Il possède 3
+ scripts pour 3 fonctionnalités différentes.
+
+Chaque fonctionnalité distincte de l'*entrypoint* est placée dans un script
+séparé, et quelqu'un qui souhaite ajouter son propre script peut le faire
+facilement, soit au moyen d'un volume, soit en étendant l'image.
diff --git a/tutorial/dockerfiles/first.md b/tutorial/dockerfiles/first.md
index 9a2b0c1..f57c153 100644
--- a/tutorial/dockerfiles/first.md
+++ b/tutorial/dockerfiles/first.md
@@ -13,7 +13,7 @@ faire fonctionner notre service de monitoring.
-## `RUN` ou script ?
+## `RUN` ou script ?
### InfluxDB
@@ -23,7 +23,7 @@ n'est pas disponible dans les dépôts[^debrepos]. La
télécharger le paquet mis à disposition puis à l'installer via `dpkg -i`.
[^debrepos]: Le projet met à disposition des dépôts, si vous préférez cette
- méthode, consultez la
+ méthode, consultez-la
[documentation d'installation](https://docs.influxdata.com/influxdb/v1.6/introduction/installation/#ubuntu-debian).
Deux solutions s'offrent à nous :
@@ -33,7 +33,7 @@ Deux solutions s'offrent à nous :
préalable de `wget`/`curl`).
La copie étant définitive (supprimer le fichier ne le supprimera pas des
-couches où il a pu exister), on préférera la seconde méthode, malgré que `wget`
+couches où il a pu exister), on préférera la seconde méthode, bien que `wget`
restera en déchet. La première méthode aura plus sa place dans un dépôt de
projet où les binaires auront été préalablement compilés, il ne restera plus
qu'à les copier dans le conteneur au bon emplacement.
@@ -48,8 +48,8 @@ d'InfluxDB, qui va l'installer et supprimer le fichier.
Il est possible que vous ayez à écraser le fichier de configuration via un
`COPY` (ou de manière plus maligne en utilisant `--volume` au moment du `docker
-run`, cela ne fonctionne pas qu'avec les dossiers !). Ou peut-être ferez-vous
-un `ENTRYPOINT` ?
+run`, cela ne fonctionne pas qu'avec les dossiers !). Ou peut-être ferez-vous
+un `ENTRYPOINT` ?
### `telegraf`
diff --git a/tutorial/dockerfiles/goodpractices.md b/tutorial/dockerfiles/goodpractices.md
index d127a9b..3efd6ff 100644
--- a/tutorial/dockerfiles/goodpractices.md
+++ b/tutorial/dockerfiles/goodpractices.md
@@ -1,5 +1,3 @@
-\newpage
-
Les bonnes pratiques
--------------------
@@ -247,3 +245,28 @@ processus par conteneur. Il est préférable de répartir chaque application dan
un conteneur distinct qui n'effectue que le travail pour lequel il est
chargé. Les options de liaison entre conteneur sont à votre disposition pour
vous aider à cette tâche.
+
+
+## De l'intérêt de faire des images minimales
+
+À l'inverse de langages comme Javascript, Python, Java et bien
+d'autres, le langage Go compile, comme le C, vers du code directement
+exécutable par le processeur. Tandis que les langages interprétés ont
+besoin de leur interpréteur et de leur environnement d'exécution, les
+langages compilés n'ont pas besoin d'être distribués avec leur
+compilateur.
+
+Prenons le temps de regarder les tailles des images :
+
+
+```
+42sh$ docker image ls -f reference=golang -f reference=youp0m
+REPOSITORY TAG IMAGE ID CREATED SIZE
+golang 1-alpine 155ead2e66ca 3 months ago 328MB
+nemunaire/youp0m latest 2c06880e48aa 2 days ago 25MB
+```
+
+
+L'image contenant le compilateur Go est bien plus lourde que l'image
+minimale que l'on a construite avec le binaire compilé. C'est autant
+d'espace et de performances gagnées.
diff --git a/tutorial/dockerfiles/image-inheritance.png b/tutorial/dockerfiles/image-inheritance.png
new file mode 100644
index 0000000..c63d8a5
Binary files /dev/null and b/tutorial/dockerfiles/image-inheritance.png differ
diff --git a/tutorial/dockerfiles/interactive.md b/tutorial/dockerfiles/interactive.md
index d226e0c..5f99af0 100644
--- a/tutorial/dockerfiles/interactive.md
+++ b/tutorial/dockerfiles/interactive.md
@@ -59,18 +59,86 @@ avec la commande `commit` :
```bash
-docker container commit CONTAINER my_nano
+42sh$ docker ps
+CONTAINER ID IMAGE COMMAND STATUS NAMES
+91d17871d730 ubuntu "bash" Exited (0) musing_tu
+
+docker container commit 91d17871d730 my_nano
```
-en remplaçant `CONTAINER` par le nom ou l'identifiant du container qui
+en remplaçant `91d17871d730` par le nom ou l'identifiant du container qui
doit servir de modèle. `my_nano` est le nom que vous voudrez utiliser
à la place d'`ubuntu`.
+L'action de *commit*, malgré le fait qu'elle crée une nouvelle image est très
+rapide : il se trouve que seules les différences avec l'image parente sont
+packagées. Les images sont en fait composées de couches : empilant les
+différences depuis le système de base !
-Cette action va figer la couche la plus haute de systèmes de fichiers, qui
-était jusqu'alors en lecture-écriture pour le conteneur ; afin d'en faire la
-dernière couche de notre nouvelle image.
+
+### À propos des couches
+
+Revenons quelque-peu en arrière : lorsque nous avons fait notre premier `docker
+run hello-world`, rappelez-vous, Docker a téléchargé l'image en nous affichant
+la progression, juste avant de lancer le conteneur.
+
+Analysons ensemble ces quelques lignes pour mieux comprendre de quoi les images
+se composent. Nous allons pour cela utiliser la commande `pull` pour récupérer
+une nouvelle image :
+
+
+```bash
+42sh$ docker image pull python:3
+3: Pulling from library/python
+23858da423a6: Pull complete
+326f452ade5c: Pull complete
+a42821cd14fb: Pull complete
+8471b75885ef: Pull complete
+8ffa7aaef404: Pull complete
+15132af73342: Pull complete
+aaf3b07565c2: Pull complete
+736f7bc16867: Pull complete
+94da21e53a5b: Pull complete
+Digest: sha256:e9c35537103a2801a30b15a77d4a56b35532c964489b125ec1ff24f3d5b53409
+Status: Downloaded newer image for python:3
+docker.io/library/python:3
+```
+
+
+On remarque que plusieurs téléchargement ont lieu, chacun associé à un
+identifiant particulier. Une image est généralement découpée en plusieurs
+éléments. On parle en fait de *couches* puisqu'on les empile, dans un ordre
+précis.
+
+Les couches sont une astuce formidable pour optimiser tant le téléchargement,
+l'espace de stockage nécessaire au cache d'images, que la création des
+conteneurs. De nombreux conteneurs vont utiliser les mêmes images de base :
+`debian`, `ubuntu`, `alpine`, ... il serait futile de systématiquement
+récupérer et stocker autant de systèmes de fichiers de base que d'images. Avec
+les couches, si deux images partagent la même version du système de fichiers de
+base, il ne sera téléchargé qu'une seule fois. On pourait le schématiser ainsi :
+
+![L'héritage des principales images officielles](image-inheritance.png)
+
+Dans les faits, cela va même encore plus loin car Docker crée de nombreuses
+couches intermédiaires, chacune peut être l'occasion d'une bifurcation.\
+
+Chaque couche est en fait un différentiel des dossiers et fichiers qui sont
+ajoutés, modifiés ou supprimés par rapport à la couche précédente.
+
+::::: {.question}
+
+#### Comment supprimer les couches d'images que je n'utilise plus ? {-}
+
+Docker gère lui-même les couches, vous n'avez pas à vous en préoccuper. Si une
+image est mise à jour ou supprimée, toutes les couches rendues inutiles seront
+automatiquement supprimées.
+
+:::::
+
+Revenons au *commit* que nous avons fait précédemment. Nous avons ajouté `nano`
+par-dessus une image `ubuntu`. Naturellement, voici ce qu'il s'est passé :
![`docker commit`](commit.png)
@@ -82,8 +150,13 @@ docker container run -it my_nano /bin/bash
```
-Vous constatez cette fois que vous pouvez lancer `nano`, alors que vous ne
-pouvez toujours pas le faire dans un conteneur issu d'une image `ubuntu` !
+Vous constatez cette fois que vous pouvez lancer `nano` !
+
+Nous avons donc créé une couche, elle contient juste le différentiel des
+fichiers ajoutés, à savoir le binaire `nano` et sa configuration par défaut
+(mais aussi le cache du gestionnaire de paquets `apt`).
+
+Voyons maintenant comment automatiser cela.
### Scripté ?
diff --git a/tutorial/dockerfiles/others.md b/tutorial/dockerfiles/others.md
index 682eed9..680e5f2 100644
--- a/tutorial/dockerfiles/others.md
+++ b/tutorial/dockerfiles/others.md
@@ -3,7 +3,7 @@ D'autres méthodes pour créer des images
Les images utilisées par Docker pour lancer les conteneurs répondent avant tout
aux spécifications OCI. Le format étant standard, il est normal que d'autres
-outils puissent utiliser mais aussi créer des images. Nous allons voir dans
+outils puissent utiliser, mais aussi créer des images. Nous allons voir dans
cette partie l'avenir des `Dockerfile` ou simplement d'autres outils plus
spécifiques.
@@ -30,7 +30,7 @@ vous pouvez l'installer comme ceci :