Tutorial 1 ready for 2023

This commit is contained in:
nemunaire 2022-09-13 23:19:59 +02:00
commit 885a410b77
26 changed files with 462 additions and 256 deletions

View file

@ -3,12 +3,17 @@
Découvrons Docker
=================
Entrons sans plus attendre dans le vif du sujet: Docker.
Tous les programmes d'exécution de conteneurs (*container runtimes*) ne
fonctionnent pas de la même manière et n'apportent pas les mêmes
fonctionnalités. Dans leurs couches les plus basses, chacun de ces programmes
va certes utiliser les mêmes fonctionnalités du système d'exploitation afin de
créer les couches d'isolation, mais chacun apporte un enrobage et une manière
d'utiliser les conteneurs différents.
Ce projet, dont les sources ont été rendues libres en 2013, a tout de
suite remporté un engouement indéniable. Le projet a énormément grossi
depuis, et il s'est aussi bien stabilisé. Aujourd'hui de nombreuses
entreprises n'hésitent plus à l'utiliser en production.
Nous allons commencer sans plus attendre par découvrir Docker. Ce projet, dont
les sources ont été rendues libres en 2013, a tout de suite remporté un
engouement indéniable. Le projet a énormément grossi depuis, et il s'est aussi
bien stabilisé.
Dans ce chapitre, nous allons partir à la découverte de cet outil:
après l'avoir installé, nous apprendrons d'abord les concepts clefs puis

View file

@ -0,0 +1,91 @@
::::: {.exercice}
Faire persister les données à l'heure du cloud
----------------------------------------------
Aïe, c'est un sujet épineux... On peut entendre ici et là que les conteneurs
permettent de monter en puissance facilement, mais lorsque l'on est lié à un
espace de stockage classique, on se heurte bien vite à des réalités physiques.
Même si l'on utilise un système de fichiers partagé pour stocker nos images et
ainsi répartir la puissance de calcul entre plusieurs machines, il va vite
arriver un moment où la bande passante du disque réseau ne suivra plus. Il faut
donc passer sur une classe de stockage d'un autre type: l'Object Storage.
L'Object Storage est une méthode de stockage des objets adaptée au monde du
Cloud Computing. Il s'agit d'une API HTTP avec laquelle on peut interagir pour
accéder, ajouter, partager ou gérer un flot d'octets, que l'on nomme *objet* dans
le jargon, mais il s'agit ni plus ni moins que d'un fichier.
Il est de la responsabilité de l'administrateur du service (Amazon, Microsoft,
Google ...) de faire en sorte que les données soient suffisamment réparties
pour éviter les goulots d'étranglement et les SPOF. Ce n'est plus notre
problème de gérer la distribution de la bande passante, ni l'usure des disques,
ni même la capacité de notre espace de stockage.
Toutes les applications ne sont malheureusement pas nativement compatibles avec
ce système de stockage. Mais il se trouve que youp0m le supporte!
Nous allons déployer le service `minio`, qui est une implémentation libre de
l'API d'Amazon S3. `minio` dispose d'une interface graphique pour gérer nos
*buckets*, c'est un volume auquel on attribue des droits particuliers,
notamment quel utilisateur y a accès. C'est au sein d'un *bucket* que l'on va
pouvoir envoyer nos fichiers. Ceux-ci seront référencés avec une *clef*
(l'équivalent du chemin): il n'y a pas de notion d'arborescence ou de dossier,
mais par convention, lorsque l'on place un `/` dans le nom de la clef, on
considère qu'il s'agit d'un séparateur de dossier.
::::: {.question}
Comme il n'y a pas de notion de dossier, il n'y a pas besoin de créer
l'arborescence avant d'écrire un objet. Chaque objet est référencé par sa clef,
sans qu'il ne soit question d'arborescence ou de dossier.
À partir d'un *bucket* vide, vous pouvez donc directement ajouter le fichier:
`images/next/racoon.jpg`, sans vous préoccuper de l'existence ou non des
dossiers `images` et `next`, la séparation n'est qu'une convention, l'objet
n'est pas enregistré comme étant le fichier `racoon.jpg` du dossier `next`,
mais bien comme `images/next/racoon.jpg`.
:::::
Au sein de l'interface minio, vous devrez donc créer un bucket (par exemple
`youp0m` puisque ce sera un *bucket* dédié à cette application). Ainsi qu'un
compte de service (sous *Identity* > *Service Accounts*). Cela vous permettra
de récupérer une *Access Key* ainsi que sa *Secret Key* associée. Ce sont des
informations qui permettront au conteneur `youp0m` de s'identifier auprès de
l'API.
Il faudra ensuite lancer un conteneur youp0m avec les
options suivantes:
<div lang="en-US">
```
S3_ENDPOINT=http://NOM_DU_CONTENEUR_MINIO:9000
S3_BUCKET=NOM_DU_BUCKET
S3_ACCESS_KEY=ACCESS_KEY_GENEREE
S3_SECRET_KEY=SECRET_KEY_CORRESPONDANTE
S3_PATH_STYLE=true
```
</div>
::::: {.more}
La dernière option `S3_PATH_STYLE` est importante lorsque l'on utilise minio,
car il y a une subtile différence entre l'API d'Amazon et celle de minio
lorsque l'on ne le configure pas davantage.
Un *bucket* pour Amazon S3 correspond à un sous-domaine (par exemple
`youp0m.nemunai.re.s3.amazonaws.com.` pour le bucket
`youp0m.nemunai.re`). Cependant, dans notre installation de minio, nous n'avons
pas de nom de domaine à proprement parler. L'option est donc là pour indiquer
que le nom du *bucket* est attendu comme premier paramètre de l'URL et non pas
comme sous-domaine.
:::::
Votre instance de `youp0m` est désormais réellement prête pour passer en
production. Bravo!
:::::

View file

@ -0,0 +1,47 @@
::::: {.exercice}
Faire persister les données: niveau 1
--------------------------------------
Le service `youp0m` que nous avons déployé fonctionne comme on pourrait s'y
attendre, mais vous imaginez bien que ce n'est pas très pratique de devoir
réimporter les données à chaque fois que l'on met à jour le conteneur ou sa
configuration.
Maintenant que nous savons utiliser les volumes nous allons les utiliser pour
rendre notre service plus pérenne.
Le service stocke par défaut les images dans le dossier `/images` du
conteneur. Pour les sauvegarder hors du conteneur, nous devons donc créer un
volume vers ce dossier:
<div lang="en-US">
```
42sh$ docker volume create youp0m_images
42sh$ docker run -v youp0m-image:/images -p 8080:8080 registry.nemunai.re/youp0m
```
</div>
Ajoutons quelques images puis arrêtons et supprimons le conteneur. Relançons
ensuite un nouveau conteneur avec les mêmes options:
<div lang="en-US">
```
42sh$ docker run -v youp0m-image:/images -p 8080:8080 registry.nemunai.re/youp0m
```
</div>
::::: {.question}
Nous ne recréons pas le volume, il est important de ne pas l'avoir supprimé
ici, puisque c'est ce volume qui assure la persistance des images.
:::::
Nos images sont bien persistantes d'une instance à l'autre de notre contenu.
Nous voici prêt à déployer en production notre service, sans crainte de perdre
les jolies contributions. Mais... est-ce que ce sera suffisant pour répondre aux
milliers de visiteurs attendus?
:::::

View file

@ -1,5 +1,3 @@
\newpage
Mon premier webservice
----------------------
@ -14,7 +12,7 @@ Nous pouvons télécharger et lancer le service grâce à :
<div lang="en-US">
```bash
docker container run -i nemunaire/youp0m
docker container run -i registry.nemunai.re/youp0m
```
</div>
@ -37,7 +35,7 @@ Nous pouvons rediriger le port avec l'argument <span lang="en-US">`-p dst_host:s
<div lang="en-US">
```bash
docker container run -i -p 8080:8080 nemunaire/youp0m
docker container run -i -p 8080:8080 registry.nemunai.re/youp0m
```
</div>
@ -75,7 +73,7 @@ On utilise l'option `-d` pour lancer le conteneur en tâche de fond:
<div lang="en-US">
```bash
docker container run -d -p 8080:8080 nemunaire/youp0m
docker container run -d -p 8080:8080 registry.nemunai.re/youp0m
```
</div>
@ -98,11 +96,11 @@ absolument un clone de <https://food.p0m.fr/>!
Il s'agit du même service, mais ce ne sont pas les mêmes images.
On ne peut pas utiliser le même port sur la machine hôte, mais pour le reste,
il s'agit des mêmes options\:
il s'agit des mêmes options:
<div lang="en-US">
```bash
docker container run -d -p 8081:8080 nemunaire/youp0m
docker container run -d -p 8081:8080 registry.nemunai.re/youp0m
```
</div>
@ -141,7 +139,7 @@ Pour ajouter une variable d'environnement, cela se passe dans la commande
<div lang="en-US">
```bash
docker container run -e YOUP0M_PASSWORD=foobar -p 8080:8080 nemunaire/youp0m
docker container run -e YOUP0M_PASSWORD=foobar -p 8080:8080 registry.nemunai.re/youp0m
```
</div>

View file

@ -16,11 +16,11 @@ pare-feu (mais cette dernière partie n'est pas demandée, gardez simplement en
tête que cela doit pouvoir être fait manuellement au cas par cas: sur une
machine sans pare-feu configurée, cela ne demande pas d'étape supplémentaire).
Votre script devra se limiter aux notions vues durant cette partie du TP
(ie. sans utiliser `docker-compose` ou `docker stack` que l'on verra par la
suite). Il pourra cependant faire usage des commandes `docker OBJECT inspect`
pour ne pas avoir à faire d'analyse syntaxique sur les retours des commandes
lisibles par les humains.
Votre script devra se limiter aux notions vues durant cette partie (ie. sans
utiliser `docker-compose` ou `docker stack` que l'on verra par la suite). Il
pourra cependant faire usage des commandes `docker OBJECT inspect` pour ne pas
avoir à faire d'analyse syntaxique sur les retours des commandes lisibles par
les humains.
Cette instance devra utiliser une base de données MySQL (lancée par votre
script dans un autre conteneur) et contenir ses données dans un ou plusieurs
@ -46,3 +46,14 @@ http://localhost:12345/
</div>
:::::
### Au secours, ça veut pas se connecter!
Lorsque nous lançons pour la première fois notre conteneur MySQL ou MariaDB, un
script est chargé d'initialiser le volume attaché à `/var/lib/mysql`. Les
démarrages suivant, ou si vous réutilisez un volume déjà initialisé avec une
base de données, le script ne refait pas d'initialisation. Même si les
variables d'environnement ont changé.
Si vous rencontrez des difficultés pour connecter votre conteneur à
`my-db`, prenez le temps de recréer un volume.

View file

@ -1,5 +1,3 @@
\newpage
Mon premier conteneur
---------------------

View file

@ -52,13 +52,6 @@ distribution:
<https://docs.docker.com/engine/install/debian/>
::::: {.question}
**Et Kali Linux alors?** Kali étant basée sur Debian, référez-vous à
la procédure d'installation de cette distribution.
:::::
### Sous Windows et macOS
Bien que les fonctionnalités de contenerisation de Docker que nous utiliserons
@ -80,9 +73,9 @@ la machine virtuelle sous-jacente.
::::: {.warning}
Depuis septembre 2021, ces applications passent sous une licence payante pour
les grosses entreprises[^DockerSubscription]. Cela ne nous concerne pas, car la
licence est gratuite pour un usage éducatif ou personnel.
Depuis septembre 2021, ces applications sont passées sous une licence payante
pour les grosses entreprises[^DockerSubscription]. Cela ne vous concerne pas,
car la licence est gratuite pour un usage éducatif ou personnel.
Notez que cela ne concerne pas le projet ou le binaire Docker: ceux-ci restent
libres. Seules les applications Docker Desktop sont concernées.
@ -155,7 +148,7 @@ Server:
#### `no such file or directory`?
Si vous avez cette erreur: `dial unix /var/run/docker.sock: no such file or
directory.`, le deamon n'est sans doute pas lancé. Lancez-le:
directory.`, le daemon n'est sans doute pas lancé. Lancez-le:
<div lang="en-US">
```bash

View file

@ -1,15 +1,3 @@
### Au secours, ça veut pas se connecter!
Lorsque nous lançons pour la première fois notre conteneur MySQL ou MariaDB, un
script est chargé d'initialiser le volume attaché à `/var/lib/mysql`. Les
démarrages suivant, ou si vous réutilisez un volume déjà initialisé avec une
base de données, le script ne refait pas d'initialisation. Même si les
variables d'environnement ont changé.
Si vous rencontrez des difficultés pour connecter votre conteneur à
`my-db`, prenez le temps de recréer un volume.
### Entrer dans un conteneur en cours d'exécution
Dans certaines circonstances, les journaux ne sont pas suffisants pour déboguer

View file

@ -73,8 +73,7 @@ On pourra également faire de même avec un conteneur MySQL:
<div lang="en-US">
```bash
docker container run --name mydb -e MYSQL_ROOT_PASSWORD=my-secret-pw \
--mount source=prod_db,target=/var/lib/mysql\
mysql
--mount source=prod_db,target=/var/lib/mysql mysql
```
</div>
@ -88,7 +87,7 @@ Lorsque vous n'avez pas besoin de stocker les données et que vous ne désirez
pas qu'elles persistent (des données sensibles par exemple) ou si cela peut
améliorer les performances de votre conteneur, il est possible de créer des
points de montages utilisant le système de fichiers `tmpfs` et donc résidant
exclusivement en RAM\:
exclusivement en RAM:
<div lang="en-US">
```bash

View file

@ -1,40 +1,56 @@
\newpage
Composition de Docker
---------------------
Docker est un écosystème d'outils de haut niveau, permettant d'utiliser des
*conteneurs*.
Docker est une suite d'outils de haut niveau, permettant d'utiliser des
*conteneurs*. Le projet en lui-même utilise de nombreuses dépendances,
originellement développées par l'entreprise Docker Inc., puis laissé dans le
domaine public lors des efforts de standardisation en 2015.
Docker est composé d'un daemon lancé au démarrage de votre machine, avec lequel
vous interagissez via un client (le programme `docker`). La communication entre
le daemon et le client s'effectuant sur une API REST généralement au travers
d'une socket.
Commençons par planter le décor, en détaillant les principes de base de Docker.
Le client peut d'ailleurs ne pas être sur la même machine qui exécutera
### Séparation des compétences
Le projet s'article autour d'un daemon lancé au démarrage de la machine, avec
lequel on interagit via un client (le programme `docker`). La communication
entre le daemon et le client s'effectuant sur une API REST généralement au
travers d'une socket.
::::: {.more}
Tous les programmes d'exécution de conteneurs n'utilisent pas une architecture
avec un daemon et un client. `podman` que l'on peut généralement substituer à
Docker n'emploie pas de daemon. Chaque utilisateur de la machine peut donc
disposer de ses propres conteneurs, sans interférer avec ceux de ses voisins.
D'un point de vue de la sécurité, le daemon Docker est exécuté en tant que
super-utilisateur. C'est sur ce daemon que repose la sécurité de la machine, ce
qui peut être beaucoup de responsabilité. Gardez en tête que le modèle
d'exécution de Docker n'est pas unique. Nous avons le choix.
:::::
Le processus client peut d'ailleurs ne pas être sur la même machine qui exécute
effectivement les conteneurs.[^dockermachine]
C'est ce qu'il se passe lorsqu'on utilise *Docker4Windows* ou *Docker4Mac*:
C'est ce qu'il se passe lorsqu'on utilise Docker sur Windows ou macOS:
une machine virtuelle Linux est lancée parallèlement au système de base et
chaque commande `docker` tapée est passée au deamon dans la machine virtuelle.
chaque commande `docker` tapée est passée au daemon dans la machine virtuelle.
[^dockermachine]: Il suffit de modifier la variable d'environnement
`DOCKER_HOST` ou de passer le paramètre `-H` suivi de l'URL de la socket à
`docker`. Voir aussi: <https://docs.docker.com/machine/overview/>
Commençons par planter le décor, en détaillant les principaux mécanismes de
Docker.
### Les images Docker
Une image Docker est un système de fichiers en lecture seule. Elle est formée
d'un ensemble de couches, agrégées selon le principe d'UnionFS.
Comme nous l'avons vu en introduction, les images sont un moyen de récupérer
facilement un environnement d'exécution complet, prêt à l'emploi. Une image
Docker donc est un système de fichiers en lecture seule.
Une image peut, par exemple, contenir:
* un système Ubuntu opérationnel,
* le programme `busybox`,
* un serveur web et votre application web, prêts à l'emploi,
* votre site web personnel, prêt à l'emploi,
* ...
Les images sont utilisées comme **modèle** qui sera ensuite dupliqué à chaque
@ -43,36 +59,34 @@ fois que l'on démarrera un nouveau conteneur.
Il y a deux méthodes pour obtenir des images Docker: soit les construire avec
les outils fournis, soit les récupérer depuis un registre.
### Les registres Docker (*Docker registries*)
Les registres sont des plates-formes de stockage, publiques ou privées,
contenant des images. Ils permettent de récupérer des images, mais également
d'en envoyer.
Le registre utilisé de base est le [Docker Hub](https://hub.docker.com/): il
contient à la fois des images officielles (ubuntu, debian, nginx, ...), des
images créées par des utilisateurs, mais aussi des images de grands éditeurs,
payantes, à destination des entreprises.
Des registres alternatifs existent comme celui de [quay.io](https://quay.io/search),
et les dépôts de sources tels que
[GitHub](https://github.blog/2020-09-01-introducing-github-container-registry/)
et [GitLab](https://docs.gitlab.com/ee/user/packages/container_registry/) le
proposent également.
Lorsque vous ne précisez pas l'adresse d'un registre, Docker va aller chercher
sur le [Docker Hub](https://hub.docker.com/). C'est son registre par défaut, chaque
### Les conteneurs Docker
### Les plugins Docker
Alors que les images constituent la partie immuable de Docker, les conteneurs
sont sa partie vivante. Chaque conteneur est créé à partir d'une image: à
chaque fois que nous lançons un conteneur, une couche lecture/écriture est
ajoutée au-dessus de l'image. Cette couche est propre au conteneur et
temporaire: l'image n'est pas modifiée par l'exécution d'un conteneur.
L'architecture de Docker est devenue très modulable. Le projet est parti dans
de nombreuses directions, chacun voulant tirer la couverture vers soit, et
l'équipe maintenant le projet a parfois eu du mal à arbitrer les bonnes choses
à ajouter ou non au projet.
![Couches d'un conteneur](layers-multi-container.png "Couches d'un conteneur"){ width=70% }
Afin de palier aux besoins complémentaires, parfois accessoires, parfois
salvateurs, un système de plugins a été intégré. Il permet d'appeler d'autres
programmes comme s'il s'agissait de composant de Docker.
Chaque conteneur s'exécute dans un environnement restreint et distinct de
l'environnement principal (où vous avez votre bureau). Par exemple, dans cet
environnement, vous ne pouvez pas voir les processus qui sont situés en dehors,
ni accéder aux fichiers extérieurs.
Certains plugins ajoutent des options à la ligne de commande (`docker-compose`,
`docker-scan`, `docker-buildx` ...). D'autres ajoutent des typologies de
réseaux, de gestion du stockage ou ajoutent de l'authentification.
Pour ajouter un plugin à Docker, il suffit de l'ajouter dans un sous-dossier de
`/usr/lib/docker/cli-plugins` pour qu'il soit accessible à tous les
utilisateurs de notre machine ou dans `$HOME/.docker/` si l'on veut l'installer
seulement pour nous.
Par exemple, les plugins ajoutant des commandes iront dans
`$HOME/.docker/cli-plugins`. Par exemple, si l'on souhaite pouvoir disposer de
la commande `docker compose`, on téléchargera le plugin vers l'emplacement:
`$HOME/.docker/cli-plugins/docker-compose`.
Plus récemment, de nouveaux plugins ont vu le jour et se basent directement sur
des conteneurs que l'on peut télécharger depuis un registre d'images.