virli/tutorial/docker-internals/registry.md

192 lines
5.3 KiB
Markdown
Raw Normal View History

2020-09-14 13:46:13 +00:00
\newpage
Registres
=========
2021-09-23 00:55:18 +00:00
**Outils nécessaires :** `curl`, `gunzip`, `jq`, `tar`.
* * * * *
2020-10-28 22:16:34 +00:00
Nous allons appréhender le fonctionnement d'un registre OCI,
2021-09-23 00:55:18 +00:00
et préparer le *rootfs* d'une image de base (Debian, Ubuntu, hello, ...) : en
nous préoccupant simplement de la couche la plus basse (qui ne contient pas de
2021-09-23 00:55:18 +00:00
modification ou de suppression : chaque fichier est normal).
## Authentification
L'authentification est facultative et est laissée à l'appréciation du
fournisseur de service. Étant donné que nous allons utiliser le [Docker
Hub](https://hub.docker.com/), le registre par défaut de `docker`, nous allons
2021-09-23 00:55:18 +00:00
devoir nous plier à leur mécanisme d'authentification : chaque requête au
2020-09-14 13:46:13 +00:00
registre doit être effectuée avec un jeton, que l'on obtient en s'authentifiant
auprès d'un service dédié. Ce service peut délivrer un jeton sans authentifier
2022-02-24 19:43:43 +00:00
l'interlocuteur, en restant anonyme ; dans ce cas, on ne pourra accéder qu'aux
images publiques. Ça tombe bien, c'est ce qui nous intéresse aujourd'hui !
2022-02-24 19:43:43 +00:00
Il n'en reste pas moins que le jeton est forgé pour un service donné (ici
`registry.docker.io`) et avec un objectif bien cerné (pour nous, on souhaite
récupérer le contenu du dépôt[^quiddepot] `hello-world` : <span
lang="en-US">`repository:hello-world:pull`</span>). Ce qui nous donne :
[^quiddepot]: Dans un registre, les fichiers qui composent l'image forment un
dépôt (*repository*).
<div lang="en-US">
```bash
42sh$ curl "https://auth.docker.io/token?service=registry.docker.io&"\
2020-09-14 13:46:13 +00:00
"scope=repository:library/hello-world:pull" | jq .
2018-11-15 22:38:25 +00:00
```
```json
{
"token": "lUWXBCZzg2TGNUdmMy...daVZxGTj0eh",
"access_token": "eyJhbGciOiJSUzI1NiIsI...N5q469M3ZkL_HA",
"expires_in": 300,
"issued_at": "2012-12-12T12:12:12.123456789Z"
}
```
</div>
C'est le `token` qu'il faudra fournir lors de nos prochaines requêtes au
registre.
2021-09-23 00:55:18 +00:00
Avec `jq`, on peut l'extraire grâce à :
<div lang="en-US">
```bash
| jq -r .token
```
</div>
2022-02-24 19:43:43 +00:00
::::: {.warning}
Le token expire ! Pensez à le renouveler régulièrement.
:::::
2020-09-14 13:46:13 +00:00
En cas d'erreur inexplicable, vous pouvez ajouter un `-v` à la ligne de
commande `curl`, afin d'afficher les en-têtes. Prêtez une attention toute
particulière à `Www-Authenticate`.
## Lecture de l'index d'images
Une fois en possession de notre jeton, nous pouvons maintenant demander l'index
2021-09-23 00:55:18 +00:00
d'images à notre registre :
<div lang="en-US">
```bash
curl -s \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/vnd.docker.distribution.manifest.list.v2+json" \
"https://registry-1.docker.io/v2/library/hello-world/manifests/latest" | jq .
```
</div>
Dans la liste des manifests retournés, nous devons récupérer son `digest`. Dans
tout l'écosystème OCI, les `digest` servent à la fois de chemin d'accès et de
somme de contrôle.
## Lecture du manifest
Demandons maintenant le manifest correspondant à notre matériel et à notre
2021-09-23 00:55:18 +00:00
système d'exploitation :
<div lang="en-US">
```bash
curl -s \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: ${MEDIATYPE}" \
2022-02-24 19:43:43 +00:00
"https://registry-1.docker.io/v2/library/hello-world/manifests/${MNFST_DGST}"
```
</div>
Nous voici donc maintenant avec le manifest de notre image. Nous pouvons
2022-02-24 19:43:43 +00:00
constater qu'il n'a bien qu'une seule couche, ouf !
## Récupération de la configuration et de la première couche
Les deux éléments que l'on cherche à récupérer vont se trouver dans le
2020-09-14 13:46:13 +00:00
répertoire `blobs`, il ne s'agit en effet plus de manifest. Si les manifests
sont toujours stockés par le registre lui-même, les blobs peuvent être délégués
à un autre service, par exemple dans le cloud, chez Amazon S3, un CDN, etc.
2021-09-23 00:55:18 +00:00
Pour récupérer la configuration de l'image :
<div lang="en-US">
```bash
curl -s --location \
-H "Authorization: Bearer ${TOKEN}" \
2022-02-24 19:43:43 +00:00
"https://registry-1.docker.io/v2/library/hello-world/blobs/${CONFIG_DIGEST}"
```
</div>
2021-09-23 00:55:18 +00:00
Enfin, armé du `digest` de notre couche, il ne nous reste plus qu'à la demander gentiment :
<div lang="en-US">
```bash
wget --header "Authorization: Bearer ${TOKEN}" \
"https://registry-1.docker.io/v2/library/hello-world/blobs/${LAYER_DIGEST}"
```
</div>
## Extraction
2022-02-24 19:43:43 +00:00
Le type indiqué par le manifest pour cette couche était :
application/vnd.docker.image.rootfs.diff.tar.gzip
Il s'agit donc d'une tarball compressée au format gzip :
<div lang="en-US">
```bash
mkdir rootfs
tar xzf ${DL_LAYER} -C rootfs
```
</div>
2021-09-23 00:55:18 +00:00
Et voilà, nous avons extrait notre première image, nous devrions pouvoir :
<div lang="en-US">
```bash
42sh# chroot rootfs /hello
Hello from Docker!
[...]
```
</div>
## Exercice {-}
2021-09-23 00:55:18 +00:00
Réalisez un script pour automatiser l'ensemble de ces étapes :
<div lang="en-US">
```bash
42sh$ cd $(mktemp)
2018-11-15 22:38:25 +00:00
2020-09-14 13:46:13 +00:00
42sh$ ~/workspace/registry_play.sh library/hello-world:latest
2018-11-15 22:38:25 +00:00
42sh$ find
.
./rootfs
./rootfs/hello
2018-11-15 22:38:25 +00:00
42sh# chroot rootfs /hello
Hello from Docker!
[...]
```
</div>
2020-09-14 13:46:13 +00:00
Pensez également à tester avec d'autres images, comme par exemple
`nemunaire/youp0m`. Il vous faudra alors extraire plusieurs couches.
2021-11-19 23:00:30 +00:00
Pour gérer les différentes couches, vous pouvez utiliser une stratégie
2022-02-24 19:43:43 +00:00
similaire au driver `vfs` : en extrayant chaque tarball l'une au dessus de
2021-11-19 23:00:30 +00:00
l'autre, en essayant de gérer les *whiteout files*. Ou bien en suivant le
driver `overlayfs`, en montant un système de fichier à chaque couche (dans ce
cas, votre script devra être lancé en `root`).