`
+sert de *whiteout file*, ce qui peut créer des conflits avec des fichiers de
+l'utilisateur (ou réduire ses choix de noms de fichiers).
+
+Un problème similaire s'applique aux dossiers : est-ce qu'il faut supprimer
+chaque fichier contenu dans le dossier ou la simple présence d'un *opaque
+directory* empêche toute découverte ?
+
+L'usage de la mémoire peut vite devenir incontrôlable, surtout si
+l'implémentation autorise beaucoup de branches, car si on veut que le système
+soit performant il faudra avoir en mémoire les topologies de chaque système de
+fichiers.
+
+L'implémentation de `mmap(2)` est nécessairement un cauchemar : lorsqu'un
+fichier est modifié par deux processus qui le `mmap(2)`, on s'attend
+normalement à voir les modifications dans les deux processus, or le premier à
+faire une modification a créer un nouveau fichier dans la branche accessible en
+écriture. Il est ardu de réconcilier les pointeurs deux des processus.
+
+D'une manière similaire, il faut penser à la gestion des *hard links* : tous
+les pointeurs d'un contenu mis à jour devrait être modifié dans la couche en
+écriture, cependant il n'y a pas d'index des pointeurs, il n'est donc pas
+facile de retrouver les fichiers à mettre à jour.
+
+Ajoutons aussi que les systèmes de fichiers sous-jacents de chacune des
+branches n'ont pas forcément les mêmes contraintes (tailles des noms de
+fichiers, attributs étendus, métadonnées, encodage des accents, ...) et qu'il
+faut réussir à jongler entre chaque, tout en retournant des erreurs cohérentes
+le cas échéant.
+
+Et bien d'autres encore. Notamment `readdir(2)` qui doit être stable malgré
+les turbulences qui pourraient arriver entre deux appels, ...
+
+Voir cette série d'articles résumant les différentes implémentations, leurs
+choix et différences : ,
+.
+
+:::::
+
+Afin de satisfaire les contraintes d'intégration au noyau, le minimum de
+fonctionnalités ont été retenues : on ne peut notamment avoir qu'une seule
+couche en écriture, qui se positionne nécessairement au sommet, en
+superposition des autres. C'est de là que vient le nom du système de fichiers,
+puisqu'il s'agit davantage d'une superposition (*overlay*) d'un système de
+fichiers sur un autre, plutôt qu'une union de plusieurs systèmes aux politiques
+d'écritures potentiellement plus variées.
+
+
+### Utilisation
+
+L'usage d'OverlayFS est plus complexe que la plupart des autres systèmes de
+fichiers. Il faut bien évidemment indiquer le/les systèmes de fichiers à
+utiliser comme branches basses, ainsi que l'éventuelle couche en
+lecture/écriture, mais il faut aussi disposer d'un dossier de travail, qui
+permettra à l'implémentation de préparer certaines actions qui nécessitent
+d'être atomiques.
+
+On peut réaliser une opération atomique en déplaçant un fichier préalablement
+créé et rempli (plutôt qu'en le créant et en l'écrivant en place). Afin de
+pouvoir satisfaire à l'atomicité, le répertoire *upper* et le dossier de
+travail doivent être obligatoirement sur le même système de fichiers. Dans le
+cas contraire, un appel à `rename(2)` retournerait `EXDEV` et l'opération ne
+pourrait alors pas être atomique.
+
+Voici un exemple général de création d'une union simple entre un système de
+fichiers en lecture seule et un en lecture/écriture :
+
+
+```
+mount -t overlay -olowerdir=/lower,upperdir=/upper,workdir=/work ignored /merged
+```
+
+
+Le type à utiliser est `overlay`, avec les options `lowerdir` qui indique
+l'emplacement du/des dossiers à combiner en lecture seule (on les sépare par
+des `:` lorsqu'il y en a plusieurs), on indique également le répertoire
+contenant le système en lecture/écriture dans l'option `upperdir`, et on il
+faut pas oublier l'option `workdir` un chemin sur la même partition que
+l'`upperdir`, qui doit être vide.
+
+On termine l'appel par donner le périphérique source, qui est inutile dans
+notre cas (`ignored` ou tout autre chaîne fera l'affaire), et enfin le dossier
+vers lequel sera monté notre union : `/merged` dans l'exemple.
+\
+
+Analysons un conteneur Docker en cours d'exécution pour en apprendre davantage.
+
+D'abord, on vérifie que l'on utilise bien le *storage driver* `overlay2` :
+
+
+```
+42sh$ docker info | grep "Storage Driver"
+ Storage Driver: overlay2
+```
+
+
+C'est le cas (en fonction de la configuration de votre noyau, Docker aura
+peut-être choisi un *driver* différent), commençons donc l'analyse :
+
+
+```
+42sh$ docker container run --rm -it debian
+ incntr$ mount | grep "on / "
+ overlay on / type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/B62UNV3UB3X4TBWQMM6XCMM6W5:/var/lib/docker/overlay2/l/V6HGFN3C3PEW6CZ6XWRSHHDKJH,upperdir=/var/lib/docker/overlay2/2a353708e5b16ea7775cf1a33dd23ce31430faaa504bcde5508691b230f9d700/diff,workdir=/var/lib/docker/overlay2/2a353708e5b16ea7775cf1a33dd23ce31430faaa504bcde5508691b230f9d700/work)
+```
+
+
+On remarque que 2 `lowerdir` sont utilisés. Il s'agit de liens symboliques
+pointant vers les dossiers identifiant les couches (les noms des liens sont
+aléatoires, il s'agit en fait d'avoir un chemin raccourci par rapport au chemin
+complet vers le système de fichiers de la couche, car le nombre de caractères
+que l'on peut passer à l'appel système `mount(2)` est limité).
+
+La branche la plus basse (le plus à droite du paramètre `lowerdir`) contient
+l'unique couche de notre image `debian`, celle un peu plus à gauche superpose
+un certain nombre de fichiers de configuration nécessaire à l'exécution du
+conteneur (`/etc/hosts`, `resolv.conf`, ...).
+
+La branche en lecture/écriture est également enregistrée dans le dossier
+`/var/lib/docker/overlay2` et l'on peut voir son identifiant. L'`upperdir` se
+trouve dans le dossier `diff`, tandis que le `workdir` est dans le dossier
+`work`, sous le même identifiant de couche.
+
+On peut également voir les dossiers utilisés en inspectant notre conteneur :
+
+
+```
+42sh$ docker container inspect youthful_wilbur | jq .[0].GraphDriver.Data
+```
+```json
+{
+ "LowerDir": "/var/lib/docker/overlay2/22753d0d81...8706f1a31-init/diff:/var/lib/docker/overlay2/2cc3656c06...c0fb91d6/diff",
+ "MergedDir": "/var/lib/docker/overlay2/22753d0d81...8706f1a31/merged",
+ "UpperDir": "/var/lib/docker/overlay2/22753d0d81...8706f1a31/diff",
+ "WorkDir": "/var/lib/docker/overlay2/22753d0d81...8706f1a31/work"
+}
+```
+
+
+Si on teste avec une image avec plus de couches, on obtient davantage de
+`lowerdir`, un par couche. N'hésitez pas à faire la même série de commandes
+avec l'image `python` par exemple.
+
+
+### Ajout de fichiers
+
+À ce stade, si nous regardons le contenu de notre dossier `upperdir`, nous
+pouvons remarqué que celui-ci est vide. C'est normal puisque nous n'avons apporté
+aucune modification.
+
+Dans notre conteneur précédemment lancé, apportons une modification, en
+ajoutant un fichier :
+
+
+```
+incntr$ echo "newfile" > /root/foobar
+```
+
+
+
+```
+42sh$ tree /var/lib/docker/overlay2/2a353708e5...91b230f9d700/diff
+/var/lib/docker/overlay2/2a353708e5...91b230f9d700/diff
+└── root
+ └── foobar
+```
+
+
+Notre nouveau fichier, qui n'est pourtant pas le seul dans l'arborescence que
+l'on voit dans le conteneur, a été ajouté comme on pouvait s'y attendre, dans
+la branche en lecture/écriture.
+
+
+### Modification de fichiers
+
+Si nous apportons une modification à un fichier, par exemple en ajoutant une
+ligne, ce n'est pas seulement la différence qui est stockée dans la branche en
+écriture, mais bien tout le fichier, tel qu'il a été modifié :
+
+
+```
+incntr$ echo "Bienvenue dans le conteneur" >> /etc/issue
+```
+
+
+
+```
+42sh$ tree /var/lib/docker/overlay2/2a353708e5...91b230f9d700/diff
+/var/lib/docker/overlay2/2a353708e5...91b230f9d700/diff
+└── etc
+ └── issue
+```
+
+
+
+```
+42sh$ cat /var/lib/docker/overlay2/2a353708e5...91b230f9d700/diff/etc/issue
+Debian GNU/Linux 11 \n \l
+Bienvenue dans le conteneur
+```
+
+
+
+### Suppression de fichiers
+
+Lorsque l'on souhaite supprimer un fichier que l'on vient d'ajouter, il n'y a
+pas grand chose à faire puisque supprimer ce fichier de la branche en écriture
+fera bien disparaître le fichier de l'arborescence montée.
+
+Lorsqu'il s'agit de supprimer un fichier présent dans une branche en lecture
+seule, il faut réussir à faire en sorte de masquer ce fichier au moyen d'un
+marqueur. En fonction du *storage driver*, ce marqueur est différent : dans
+`OverlayFS`, une suppression est matérialisée par un fichier spécial de type
+caractère du même nom.
+
+
+```
+incntr$ rm /etc/adduser.conf
+```
+
+
+
+```
+42sh$ tree /var/lib/docker/overlay2/2a353708e5...91b230f9d700/diff
+/var/lib/docker/overlay2/1531651afa872006a4b2b9b913d5d8ee317cf12be7883517ba77f3d094f871b4/diff
+└── etc
+ └── adduser.conf
+```
+
+
+
+```
+42sh$ cat /var/lib/docker/overlay2/2a353708e5...91b230f9d700/diff/etc/adduser.conf
+cat: No such device or address
+
+42sh$ stat /var/lib/docker/overlay2/2a353708e5...91b230f9d700/diff/etc/adduser.conf
+ File: /var/lib/docker/overlay2/2a353708e5...91b230f9d700/diff/etc/adduser.conf
+ Size: 0 Blocks: 0 IO Block: 4096 character special file
+Device: fe0bh/65035d Inode: 515773 Links: 2 Device type: 0,0
+```
+
+
+Notons ici `Device type: 0,0`.
+
+Pour créer nous-mêmes un fichier similaire, il faudrait utiliser :
+
+
+```
+42sh$ mkdir /var/lib/docker/overlay2/2a353708e5...91b230f9d700/diff/bin
+42sh$ mknod /var/lib/docker/overlay2/2a353708e5...91b230f9d700/diff/bin/sh c 0 0
+```
+
+
+::::: {.warning}
+
+Faire cette commande `mknod` alors que l'union de système de fichiers est
+montée par ailleurs ne va pas faire disparaître le fichier `/bin/sh` car les
+modifications qui pourraient être apportées aux branches en dehors du système
+monté conduisent à des résultats explicitement indéfinis.
+
+:::::
+
+
+### Suppressions pour `unionfs` et AuFS
+
+Le concept de *whiteout file*, comme on a pu le voir, diffère en fonction du
+système de fichiers. Il s'avère que même si l'OverlayFS a été intégré dans le
+noyau Linux après maintes péripéties, Docker, lorsqu'à été spécifié le format
+des archives utilisées pour distribuer les couches, utilise aujourd'hui le
+format d'AuFS pour représenter les suppressions. Il est donc important de le
+voir également.
+
+Au lieu d'utiliser un fichier spécial, AuFS crée un fichier standard
+`.wh.`, où `` est le nom du fichier à masquer.
+
+Afin de s'adapter au *storage driver*, lors de la décompression de l'archive,
+Docker s'emploie à convertir[^MOBYWHITEOUT] les *whiteout files* qu'il rencontre dans le
+format attendu.
+
+[^MOBYWHITEOUT]: Voir le code
+
diff --git a/tutorial/docker-internals/Makefile b/tutorial/docker-internals/Makefile
index bfd45d2..83b74dc 100644
--- a/tutorial/docker-internals/Makefile
+++ b/tutorial/docker-internals/Makefile
@@ -1,6 +1,12 @@
include ../pandoc-opts.mk
-SOURCES = tutorial.md oci.md registry.md runc.md linuxkit.md linuxkit-content.md rendu.md
+SOURCES = tutorial.md \
+ registry.md \
+ ../4/filesystem.md ../4/filesystem-ex.md ../4/filesystem-more.md \
+ oci.md \
+ runc.md \
+ linuxkit.md linuxkit-content.md \
+ rendu.md
all: tutorial.pdf
diff --git a/tutorial/docker-internals/linuxkit-content.md b/tutorial/docker-internals/linuxkit-content.md
index 2a37d0f..49e830c 100644
--- a/tutorial/docker-internals/linuxkit-content.md
+++ b/tutorial/docker-internals/linuxkit-content.md
@@ -1,10 +1,12 @@
## Prérequis
-Si vous n'avez pas déjà le binaire `linuxkit`, vous pouvez télécharger ici :\
+Si vous n'avez pas déjà le binaire `linuxkit`, vous pouvez le télécharger
+ici :\
.
Notez qu'étant donné qu'il est écrit en Go, aucune dépendance n'est nécessaire
-en plus du binaire[^lollibc] ;-)
+en plus du binaire[^lollibc]. Un simple `chmod +x` vous permettra de l'exécuter
+depuis n'importe quel dossier
[^lollibc]: à condition tout de même que vous utilisiez une libc habituelle.
@@ -19,51 +21,77 @@ parties :
- `kernel` : il est attendu ici une image OCI contenant le nécessaire pour
pouvoir utiliser un noyau : l'image du noyau, ses modules et un initramfs ;
- `init` : l'ensemble des images OCI de cette liste seront fusionnés pour
- donner naissance au *rootfs* de la machine. On n'y place normalement qu'un
- gestionnaire de conteneur, qui sera chargé de lancer chaque conteneur au bon
+ donner naissance au *rootfs* de la machine. On y place normalement qu'un
+ gestionnaire de conteneurs, qui sera chargé de lancer chaque conteneur au bon
moment ;
- `onboot`, `onshutdown` et `services` : il s'agit de conteneurs qui seront
- lancés par le système disponible dans l'`init`, au bon moment. Les conteneurs
- indiqués dans `onboot` seront lancés **séquentiellement** au démarrage de la
- machine, ceux dans `onshutdown` seront lancés lors de l'arrêt de la
- machine. Les conteneurs dans `services` seront lancés simultanément une fois
- que le dernier conteneur de `onboot` aura rendu la main ;
-- `files` : des fichiers supplémentaires à placer dans le rootfs.
+ lancés par le gestionnaire disponible dans l'`init`, au moment désigné.\ Les
+ conteneurs indiqués dans `onboot` seront lancés **séquentiellement** au
+ démarrage de la machine, ceux dans `onshutdown` seront lancés lors de l'arrêt
+ de la machine. Les conteneurs dans `services` seront lancés simultanément une
+ fois que le dernier conteneur de `onboot` aura rendu la main ;
+- `files` : des fichiers supplémentaires à placer dans le système de fichier à
+ l'emplacement déterminé.
Le format est documenté ici :
-## Hello?
+### Hello?
L'image la plus simple que l'on puisse réaliser pourrait être :
```yaml
kernel:
- image: linuxkit/kernel:5.10.76
+ image: linuxkit/kernel:5.10.104
cmdline: "console=tty0 console=ttyS0"
init:
- - linuxkit/init:eb597ef74d808b5320ad1060b1620a6ac31e7ced
- - linuxkit/runc:21dbbda709ae138de0af6b0c7e4ae49525db5e88
- - linuxkit/containerd:2f0907913dd54ab5186006034eb224a0da12443e
+ - linuxkit/init:8f1e6a0747acbbb4d7e24dc98f97faa8d1c6cec7
+ - linuxkit/runc:f01b88c7033180d50ae43562d72707c6881904e4
+ - linuxkit/containerd:de1b18eed76a266baa3092e5c154c84f595e56da
onboot:
- name: dhcpcd
- image: linuxkit/dhcpcd:v0.8
+ image: linuxkit/dhcpcd:52d2c4df0311b182e99241cdc382ff726755c450
command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"]
services:
- name: getty
- image: linuxkit/getty:ed32c71531f5998aa510847bb07bd847492d4101
+ image: linuxkit/getty:76951a596aa5e0867a38e28f0b94d620e948e3e8
env:
- INSECURE=true
-trust:
- org:
- - linuxkit
```
-L'image `getty` est très pratique pour déboguer, car elle permet d'avoir un
-shell sur la machine !
+On retrouve nos différentes sections : `kernel` indique qu'il faut récupérer
+l'image `linuxkit/kernel` depuis le registre Docker : il ne s'agit pas d'une
+image qui sera lancé, elle est plutôt utilisée comme une archive de stockage
+pour le noyau et ses modules. LinuxKit au moment de la construction de l'image
+se chargera de placer les fichiers aux bons endroits.
+
+Ensuite, nous avons une section `init` qui déclare 3 images Docker :
+- `linuxkit/init` contient les fichiers de base et un binaire `/sbin/init` qui
+ servira de système d'initialisation ;
+- `linuxkit/runc` nous donnera les outils pour lancer des conteneurs ;
+- `linuxkit/containerd` apporte un daemon pour gérer les conteneurs pendant
+ leur durée de vie.
+
+Ces trois images ne sont pas non plus des images Docker conventionnelles, dans
+le sens où on ne peut pas les utiliser pour faire un `docker container
+run`. Elles contiennent chacune une partie de l'arborescence du système de
+fichiers, uniquement les fichiers nécessaire, en plus, au fonctionnement du
+programme qu'elles ajoutent. Les images déclarées dans la section `init` seront
+fusionnées ensemble et formeront le système de fichiers de base de notre image
+LinuxKit.
+
+Enfin les sections `onboot` et `services` sont plus conventionnelles : il
+s'agit bien d'images Docker, les conteneurs seront lancés comme tel, à partir
+d'une configuration `runc` qui sera générée au moment de la construction de
+l'image LinuxKit.
+
+L'image `getty` est notamment très pratique pour déboguer, car elle permet
+d'avoir un shell sur la machine !
+
+::::: {.more}
On notera cependant que, positionné dans `services`, le shell que nous
obtiendrons sera lui-même exécuté dans un conteneur, nous n'aurons donc pas un
@@ -74,6 +102,8 @@ la partie `init`, elle sera alors lancée comme un équivalent de
[^infogetty]: Plus d'infos à
+:::::
+
## `namespaces`
@@ -81,7 +111,7 @@ Chaque nouveau conteneur est donc lancé dans un espace distinct où il ne pourr
pas interagir avec les autres, ou déborder s'il s'avérait qu'il expose une
faille exploitable.
-Néanmoins, contrairement à Docker qui va de base nous dissocier du maximum de
+Néanmoins, contrairement à Docker qui, de base, va nous dissocier du maximum de
*namespaces*, `linuxkit` ne le fait pas pour les *namespaces* `net`, `ipc` et
`uts`. C'est-à-dire que, par défaut, la pile réseau est partagée entre tous les
conteneurs, tout comme les IPC et le nom de la machine.
@@ -173,10 +203,29 @@ serveur SSH aux `services` :
```yaml
- name: sshd
- image: linuxkit/sshd:add8c094a9a253870b0a596796628fd4ec220b70
+ image: linuxkit/sshd:4696ba61c3ec091328e1c14857d77e675802342f
+ binds.add:
+ - /root/.ssh:/root/.ssh
```
+::::: {.question}
+
+#### Que fait la ligne `binds.add` ? {-}
+\
+
+Avec l'instruction `binds.add`, LinuxKit va créer un *bind mount* selon le même
+principe que les volumes des conteneurs Docker. Ici nous allons partager le
+dossier `/root/.ssh` de notre image LinuxKit avec celui du conteneur `sshd`.
+\
+
+Un certain nombre de *bind mounts* sont effectués par défaut. Ceux-ci sont
+déclarés dans les métadonnées des images. Pour avoir la liste, il convient de
+regarder le fichier `build.yml` de chaque image :\
+
+
+:::::
+
Comme nous n'avons défini aucun mot de passe, il va falloir utiliser une clef
SSH pour se connecter. Voilà un bon début d'utilisation de la section `files` :
@@ -188,12 +237,10 @@ SSH pour se connecter. Voilà un bon début d'utilisation de la section `files`
```
-Ceci va aller chercher votre clef RSA publique sur votre machine, pour
-la placer directement comme contenu du fichier `authorized_keys`.
+Ceci va aller chercher votre clef RSA publique sur votre machine, pour la
+placer directement comme contenu du fichier `authorized_keys`. À adapter en
+fonction de votre situation.
-Notons qu'il est inutile d'ajouter un *bind mount* du dossier `.ssh` ainsi
-recopié, car le *package* `linuxkit/sshd` défini déjà cela :\
-cf.
## Interface réseau virtuelle
diff --git a/tutorial/docker-internals/oci.md b/tutorial/docker-internals/oci.md
index 0a50526..a71ffde 100644
--- a/tutorial/docker-internals/oci.md
+++ b/tutorial/docker-internals/oci.md
@@ -7,6 +7,8 @@ Formée en juin 2015, l'Open Container Initiative (OCI) a pour but d'établir le
standard commun aux programmes de contenerisation, afin d'éviter une
fragmentation de l'écosystème.
+## Spécifications
+
Trois spécifications ont été écrites :
- [`runtime-spec`](https://github.com/opencontainers/runtime-spec/blob/master/spec.md#platforms): définis les paramètres du démarrage d'un conteneur ;
@@ -14,7 +16,7 @@ Trois spécifications ont été écrites :
- [`distribution-spec`](https://github.com/opencontainers/distribution-spec/blob/master/spec.md): définis la manière dont sont partagées et récupérées les images.
-## `runtime-spec`
+### `runtime-spec`
`runc` est l'implémentation de cette spécification ; elle a été extraite de
`docker`, puis donnée par Docker Inc. à l'OCI.
@@ -38,7 +40,7 @@ plus de conteneur a proprement parlé, il fait seulement en sorte d'atteindre
l'état voulu par cette spécification, avant de passer la main à `runc`.
-## `image-spec`
+### `image-spec`
Une image OCI est composée d'un manifest, d'une suite de couches de systèmes de
fichiers, d'une configuration ainsi qu'un index d'image optionnel.
@@ -68,14 +70,14 @@ là-dedans que finissent toutes les métadonnées que l'on inscrit dans nos
des couches du système de fichiers, ainsi que l'historique de l'image.
-## `distribution-spec`
+### `distribution-spec`
Dernière née de l'organisme, cette spécification fédère la notion de
*registre* : une API REST sur HTTP où l'on peut récupérer des images, mais
aussi en envoyer.
-## Pour aller plus loin {-}
+### Pour aller plus loin {-}
Si maintenant `docker` fait appel à un programme externe pour lancer
effectivement nos conteneurs, c'est que l'on peut changer cette
diff --git a/tutorial/docker-internals/registry.md b/tutorial/docker-internals/registry.md
index c5f038c..a0d84e0 100644
--- a/tutorial/docker-internals/registry.md
+++ b/tutorial/docker-internals/registry.md
@@ -49,7 +49,7 @@ registre.
Avec `jq`, on peut l'extraire grâce à :
-```bash
+```
| jq -r .token
```
@@ -149,7 +149,7 @@ tar xzf ${DL_LAYER} -C rootfs
Et voilà, nous avons extrait notre première image, nous devrions pouvoir :
-```bash
+```
42sh# chroot rootfs /hello
Hello from Docker!
[...]
@@ -164,7 +164,7 @@ Hello from Docker!
Réalisez un script pour automatiser l'ensemble de ces étapes :
-```bash
+```
42sh$ cd $(mktemp -d)
42sh$ ./registry_play library/hello-world:latest
diff --git a/tutorial/docker-internals/runc.md b/tutorial/docker-internals/runc.md
index a40f326..91c3847 100644
--- a/tutorial/docker-internals/runc.md
+++ b/tutorial/docker-internals/runc.md
@@ -1,7 +1,5 @@
-\newpage
-
`runc`
-======
+------
`runc` est le programme qui est responsable de la création effective du
conteneur : c'est lui qui va mettre en place toute la machinerie, les points de
@@ -18,14 +16,14 @@ Pour appréhender l'utilisation de `runc` sans l'aide de Docker, nous allons
essayer de lancer un shell `alpine` avec un volume dans notre home.
-## Prérequis
+### Prérequis
Vous devriez avoir le binaire `runc` ou `docker-runc`. Si ce n'est pas le cas,
vous pouvez télécharger la dernière version :
.
-## Extraction du rootfs
+### Extraction du rootfs
À l'aide du script d'extraction de registre déjà réalisé, extrayons le
*rootfs* d'alpine : `library/alpine` dans le registre Docker.
@@ -40,7 +38,7 @@ docker image save alpine | tar xv -C rootfs
-## Modèle de configuration
+### Modèle de configuration
L'écriture complète d'un fichier `config.json` pour `runc` est plutôt
fastidieux et répétitif, nous allons donc gagner du temps et utiliser la
@@ -57,7 +55,7 @@ Pour savoir à quoi correspondent tous ces éléments, vous pouvez consulter :\
Rassurez-vous, il n'y a que très peu de champs à modifier.
-## Test brut
+### Test brut
Voici comment nous pouvons tester le fonctionnement de notre *bundle* :
@@ -87,7 +85,7 @@ virli1 12345 running /tmp/work/runctest 2012-12-12T12:12:12.123456789Z root
-## Attacher notre `home`
+### Attacher notre `home`
Dans le modèle de `config.json`, il y a déjà de nombreux systèmes de fichiers
qui sont montés. Nous pouvons les filtrer avec :