Dockerfile part done
This commit is contained in:
parent
02db9cc19c
commit
55817a2073
@ -1 +0,0 @@
|
|||||||
../dockerfiles/chronograf_setup.png
|
|
Before Width: | Height: | Size: 35 B After Width: | Height: | Size: 23 KiB |
BIN
tutorial/docker-advanced/chronograf_setup.png
Normal file
BIN
tutorial/docker-advanced/chronograf_setup.png
Normal file
Binary file not shown.
Before Width: | Height: | Size: 35 B After Width: | Height: | Size: 23 KiB |
@ -1,4 +1,4 @@
|
|||||||
SOURCES = tutorial.md interactive.md dockerfile.md first.md supervisor.md goodpractices.md split.md entrypoint.md multistaged.md rendu.md
|
SOURCES = tutorial.md interactive.md dockerfile.md goodpractices.md entrypoint.md rendu.md
|
||||||
PANDOCOPTS = --latex-engine=xelatex \
|
PANDOCOPTS = --latex-engine=xelatex \
|
||||||
--standalone \
|
--standalone \
|
||||||
--normalize \
|
--normalize \
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 23 KiB |
@ -1,13 +1,11 @@
|
|||||||
\newpage
|
\newpage
|
||||||
|
|
||||||
`Dockerfile`
|
Ma première image ... par `Dockerfile`
|
||||||
============
|
======================================
|
||||||
|
|
||||||
## Ma première image ... par `Dockerfile`
|
|
||||||
|
|
||||||
Pour construire une image, nous ne sommes pas obligés de passer par une série
|
Pour construire une image, nous ne sommes pas obligés de passer par une série
|
||||||
de commits. Docker dispose d'un mécanisme permettant d'automatiser la
|
de *commits*. Docker dispose d'un mécanisme permettant d'automatiser la
|
||||||
construction de nouvelles images. Vous pouvez arriver au même résultat que ce
|
construction de nouvelles images. Nous pouvons arriver au même résultat que ce
|
||||||
que l'on a réussi à faire précédemment en utilisant le `Dockerfile` suivant :
|
que l'on a réussi à faire précédemment en utilisant le `Dockerfile` suivant :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
@ -19,7 +17,7 @@ que l'on a réussi à faire précédemment en utilisant le `Dockerfile` suivant
|
|||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
La syntaxe d'un `Dockerfile` est simple, le premier mot de chaque ligne est
|
La syntaxe d'un `Dockerfile` est simple : le premier mot de chaque ligne est
|
||||||
l'intitulé d'une instruction (que l'on écrit généralement en majuscule), elle
|
l'intitulé d'une instruction (que l'on écrit généralement en majuscule), elle
|
||||||
est suivie de ses arguments.
|
est suivie de ses arguments.
|
||||||
|
|
||||||
@ -27,9 +25,9 @@ Dans notre exemple, nous utilisons `FROM` qui indique une image de départ à
|
|||||||
utiliser ; `RUN` est une commande qui sera exécutée dans le conteneur, dans le
|
utiliser ; `RUN` est une commande qui sera exécutée dans le conteneur, dans le
|
||||||
but de le construire.
|
but de le construire.
|
||||||
|
|
||||||
Pour lancer la construction de la nouvelle image, créer un nouveau dossier ne
|
Pour lancer la construction de la nouvelle image, créons un nouveau dossier ne
|
||||||
contenant que votre fichier `Dockerfile`, placez-vous dedans, puis utilisez la
|
contenant que votre fichier `Dockerfile`, plaçons-nous ensuite dedans, puis
|
||||||
commande `build` :
|
lançons la commande `build` :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```
|
```
|
||||||
@ -37,7 +35,7 @@ commande `build` :
|
|||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Une fois la construction de l'image terminée, vous pouvez la lancer et
|
Une fois la construction de l'image terminée, nous pouvons la lancer et
|
||||||
constater l'existence de notre éditeur favori :
|
constater l'existence de notre éditeur favori :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
@ -62,9 +60,21 @@ Cela signifie que l'exemple suivant **ne fonctionne pas** :
|
|||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Cet exemple ne fonctionne pas car le serveur MySQL qui est lancé dans le
|
Cet exemple ne fonctionne pas car le serveur MySQL est bien lancé dans le
|
||||||
premier `RUN`, n'est plus lancé au moment du deuxième `RUN`. En effet, chaque
|
premier `RUN`, mais il se trouve brûtalement arrêté dès lors que la commande
|
||||||
commande du `Dockerfile` a pour but de modifier le système de fichiers.
|
`service` se termine. En fait, à chaque instruction, Docker réalise
|
||||||
|
automatiquement un `run` suivi d'un `commit`. Et vous pouvez constater par
|
||||||
|
vous-même que, en créant l'image `tinysql` à partir d'un simple `apt install
|
||||||
|
mysql` :
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```
|
||||||
|
docker container run tinysql service mysqld start
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
rend la main directement, sans laisser de `mysqld` dans l'arborescence de
|
||||||
|
processus.
|
||||||
|
|
||||||
Pour avoir le résultat escompté, il faut exécuter les commandes ensemble :
|
Pour avoir le résultat escompté, il faut exécuter les commandes ensemble :
|
||||||
|
|
||||||
@ -75,13 +85,17 @@ Pour avoir le résultat escompté, il faut exécuter les commandes ensemble :
|
|||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Après le `RUN`, MySQL sera de nouveau arrêté, si on veut l'utiliser dans le
|
Après le `RUN`, MySQL sera de nouveau tué.
|
||||||
conteneur, il ne faudra pas oublier de lancer le processus.
|
|
||||||
|
En aucun cas, une commande exécutée par un `RUN` 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.
|
||||||
|
|
||||||
|
|
||||||
## Exposer des ports
|
## Exposer des ports
|
||||||
|
|
||||||
Construisons maintenant un conteneur avec un serveur web :
|
Construisons maintenant un conteneur avec un service web :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```
|
```
|
||||||
@ -95,30 +109,90 @@ Construisons maintenant un conteneur avec un serveur web :
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
L'instruction `EXPOSE` sera traitée plus tard par le client Docker (équivalent
|
L'instruction `EXPOSE` sera traitée plus tard par le client Docker (équivalent
|
||||||
à l'argument `--expose`). Il s'agit de préciser les ports sur lesquels votre
|
à l'argument `--expose`). Il s'agit d'une métadonnée qui sera attachée à
|
||||||
image écoute.
|
l'image (et à toutes ses images filles).
|
||||||
|
|
||||||
En utilisant l'option `-P` du `run`, vous allez pouvoir assigner une
|
En précisant tous les ports qu'exposent une image dans ses métadonnées, ces
|
||||||
redirection de port aléatoire sur la machine hôte vers votre conteneur :
|
ports seront automatiquement exposés en utilisant l'option `-P` du `run` : cela
|
||||||
|
assigne une redirection de port aléatoire sur la machine hôte vers votre
|
||||||
|
conteneur :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```
|
```
|
||||||
docker image build --tag=my_webserver .
|
42sh$ docker image build --tag=my_webserver .
|
||||||
docker container run -it -P my_webserver /bin/bash
|
42sh$ docker container run -it -P my_webserver /bin/bash
|
||||||
service nginx start
|
(cntnr)# service nginx start
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Dans un autre terminal, lancer un `docker ps` et consulter la colonne *PORTS*
|
Dans un autre terminal, lancer un `docker container ls` et consulter la colonne
|
||||||
pour connaître le port choisi par Docker pour effectuer la redirection.
|
*PORTS* pour connaître le port choisi par Docker pour effectuer la redirection.
|
||||||
|
|
||||||
Rendez-vous ensuite dans votre navigateur sur <http://localhost:49153/>.
|
Rendez-vous ensuite dans votre navigateur sur <http://localhost:49153/>.
|
||||||
|
|
||||||
*À vous de jouer :* utilisez l'instruction `COPY` pour afficher votre propre
|
*À vous de jouer :* utilisez l'instruction `COPY` pour afficher votre propre
|
||||||
`index.html` remplaçant celui installé de base par nginx.
|
`index.html` remplaçant celui installé de base par `nginx`. Si vous manquez
|
||||||
|
d'inspiration, utilisez [cette page de compte à
|
||||||
|
rebours](https://virli.nemunai.re/countdown.html).
|
||||||
|
|
||||||
|
|
||||||
## Lancement de commande automatique
|
## Les caches
|
||||||
|
|
||||||
|
Nous avons vu que chaque instruction de notre `Dockerfile` est exécutée dans un
|
||||||
|
conteneur, qui génère une image intermédiaire. Cette image intermédiaire sert
|
||||||
|
ensuite d'image de base pour le conteneur qui sera lancé avec l'instruction
|
||||||
|
suivante.
|
||||||
|
|
||||||
|
Lorsqu'on lance la reconstruction du même `Dockerfile`, les images
|
||||||
|
intermédiaires sont réutilisées, comme un cache d'instructions. Cela permet de
|
||||||
|
gagner du temps sur les étapes qui n'ont pas changées. Ainsi, lorsque vous
|
||||||
|
modifiez une instruction dans votre `Dockerfile`, les instructions précédentes
|
||||||
|
ne sont pas réexécutées mais sont ressorties du cache.
|
||||||
|
|
||||||
|
Le cache se base principalement sur le contenu de chaque instruction du
|
||||||
|
`Dockerfile` (pour les `COPY` et `ADD`, il va aussi regarder la date de
|
||||||
|
dernière modification de fichier à copier ou à ajouter). Donc tant qu'une
|
||||||
|
instruction n'est pas modifiée dans le `Dockerfile`, le cache sera utilisé.
|
||||||
|
|
||||||
|
Il est possible de ne pas utiliser le cache et de relancer toutes les étapes du
|
||||||
|
`Dockerfile` en ajoutant l'option `--no-cache` au moment du `docker image
|
||||||
|
build`.
|
||||||
|
|
||||||
|
Les couches du cache peuvent être partagées entre plusieurs conteneur, c'est
|
||||||
|
ainsi que vous pouvez partager facilement une plus grosse partie du système de
|
||||||
|
fichiers (rappelez-vous le principe d'union FS).
|
||||||
|
|
||||||
|
Pour profiter du cache, on va placer de préférences les étapes les plus
|
||||||
|
génériques (qui seraient les plus susceptibles d'apparaître dans d'autres
|
||||||
|
images), en haut du `Dockerfile`.
|
||||||
|
|
||||||
|
|
||||||
|
## Métadonnées pures
|
||||||
|
|
||||||
|
L'instruction LABEL permet d'ajouter une métadonnée à une image, sous forme de
|
||||||
|
clef/valeur.
|
||||||
|
|
||||||
|
Une métadonnée
|
||||||
|
[courante](https://github.com/nginxinc/docker-nginx/blob/master/mainline/stretch/Dockerfile#L3)
|
||||||
|
est d'indiquer le nom du mainteneur de l'image :
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```
|
||||||
|
LABEL maintainer="Pierre-Olivier Mercier <nemunaire@nemunai.re>"
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Dans notre `Dockerfile`, indiquez juste après l'image de base, vos noms,
|
||||||
|
prénoms et mails de contact avec l'instruction `LABEL maintainer`, pour
|
||||||
|
indiquer que c'est vous qui maintenez cette image, si des utilisateurs ont
|
||||||
|
besoin de vous avertir pour le mettre à jour ou s'ils rencontrent des
|
||||||
|
difficultés par exemple.
|
||||||
|
|
||||||
|
On le place dès le début, car comme c'est une information qui n'est pas amener
|
||||||
|
à changer, elle sera toujours retrouvée en cache.
|
||||||
|
|
||||||
|
|
||||||
|
## Commande par défaut
|
||||||
|
|
||||||
Vous pouvez placer dans un `Dockerfile` une instruction `CMD` qui sera exécutée
|
Vous pouvez placer dans un `Dockerfile` une instruction `CMD` qui sera exécutée
|
||||||
si aucune commande n'est passée lors du `run`, par exemple :
|
si aucune commande n'est passée lors du `run`, par exemple :
|
||||||
@ -131,8 +205,8 @@ si aucune commande n'est passée lors du `run`, par exemple :
|
|||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```
|
```
|
||||||
docker image build --tag=my_nginx .
|
42sh$ docker image build --tag=my_nginx .
|
||||||
docker container run -d -P my_nginx
|
42sh$ docker container run -d -P my_nginx
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -142,16 +216,104 @@ retirez cette option pour voir ce qui ne va pas, ou utilisez la commande
|
|||||||
`docker container logs`.
|
`docker container logs`.
|
||||||
|
|
||||||
|
|
||||||
|
## Construire son application au moment de la construction du conteneur ?
|
||||||
|
|
||||||
|
Comment faire lorsque l'on a besoin de compiler une application avant de
|
||||||
|
l'intégrer dans le conteneur ?
|
||||||
|
|
||||||
|
On peut vouloir lancer la compilation sur notre machine, mais cela ne sera pas
|
||||||
|
très reproductible et cela aura nécessité d'installer le compilateur et les
|
||||||
|
outils liés au langage que l'on souhaite compiler. Peut-être que plusieurs
|
||||||
|
versions de ces outils existent, laquelle choisir ? ... Ok c'est trop
|
||||||
|
compliqué.
|
||||||
|
|
||||||
|
D'un autre côté, si l'on fait cela dans un conteneur, celui-ci contiendra dans
|
||||||
|
ses couches des données inutiles à l'exécution : les sources, les produits
|
||||||
|
intermédiaires de compilation, le compilateur, n'ont rien à faire dans les
|
||||||
|
couches de notre image.
|
||||||
|
|
||||||
|
Le meilleur des deux mondes se trouve dans les *Multi-stage builds* : au sein
|
||||||
|
du même `Dockerfile`, on va réaliser les opérations de préparation dans un ou
|
||||||
|
plusieurs conteneurs, avant d'agréger le contenu compilé au sein du conteneur
|
||||||
|
final :
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```
|
||||||
|
FROM gcc:4.9
|
||||||
|
COPY . /usr/src/myapp
|
||||||
|
WORKDIR /usr/src/myapp
|
||||||
|
RUN gcc -static -static-libgcc -o hello hello.c
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
COPY --from=0 /usr/src/myapp/hello /hello
|
||||||
|
CMD ["/hello"]
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
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
|
||||||
|
`hello.c`. Mais l'image finale (le dernier `FROM` de notre `Dockerfile`) est
|
||||||
|
l'image vide, dans laquelle nous recopions simplement le produit de notre
|
||||||
|
compilation.
|
||||||
|
|
||||||
|
L'image ainsi générée est minime, car elle ne contient rien d'autre que le
|
||||||
|
strict nécessaire pour s'exécuter.
|
||||||
|
|
||||||
|
### Étapes nommées
|
||||||
|
|
||||||
|
Nous avons utilisé `--from=0` pour désigner la première image de notre
|
||||||
|
`Dockerfile`. Lorsque l'on réalise des montages plus complexe, on peut vouloir
|
||||||
|
donner des noms à chaque image, plutôt que de devoir jongler avec les
|
||||||
|
numéros. Dans ce cas, on indiquera :
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```
|
||||||
|
FROM gcc:4.9 as builder
|
||||||
|
COPY . /usr/src/myapp
|
||||||
|
WORKDIR /usr/src/myapp
|
||||||
|
RUN gcc -static -static-libgcc -o hello hello.c
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
COPY --from=builder /usr/src/myapp/hello /hello
|
||||||
|
CMD ["/hello"]
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Par défaut la dernière étape du `Dockerfile` est retenu comme étant l'image que
|
||||||
|
l'on souhaite `tagger`, mais il est possible de préciser quelle image
|
||||||
|
spécifiquement on souhaite construire avec l'option `--target` :
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```
|
||||||
|
42sh$ docker build --target builder -t hello-builder .
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Cela peut être particulièrement utile si l'on dispose d'une image de debug,
|
||||||
|
incluant tous les symboles, et une image de production, plus propre. On
|
||||||
|
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'autres instructions ?
|
## D'autres instructions ?
|
||||||
|
|
||||||
Consultez <https://docs.docker.com/engine/reference/builder/> pour la liste
|
Consultez <https://docs.docker.com/engine/reference/builder/> pour la liste
|
||||||
complète des instructions reconnues.
|
complète des instructions reconnues.
|
||||||
|
|
||||||
|
|
||||||
## Rendu
|
## Exercice
|
||||||
|
|
||||||
Rendez le fichier `Dockerfile` et son contexte (`index.html`, fichiers de conf
|
Pour mettre en application tout ce que nous venons de voir, réalisons le
|
||||||
éventuels, ...) que vous avez utilisé pour réaliser votre image `my_webserver`.
|
`Dockerfile` du service web [`youp0m`](https://you.p0m.fr/) que nous avons
|
||||||
|
utilisé la semaine dernière.
|
||||||
|
|
||||||
Une attention particulière sera apportée au respect des différentes bonnes
|
Pour réaliser ce genre de contribution, on ajoute généralement un `Dockerfile`
|
||||||
pratiques vues en cours pour l'écriture du `Dockerfile`.
|
à la racine du dépôt.
|
||||||
|
|
||||||
|
Vous pouvez cloner le dépôts de sources de `youp0m` à :
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```
|
||||||
|
https://git.nemunai.re/youp0m.git
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
@ -1,21 +1,114 @@
|
|||||||
\newpage
|
\newpage
|
||||||
|
|
||||||
Entrypoint
|
Personnalisation du point d'entrée du conteneur
|
||||||
==========
|
===============================================
|
||||||
|
|
||||||
Jusque là, à chaque redémarrage d'InfluxDB, il est nécessaire de reconfigurer
|
## Point d'entrée basique
|
||||||
Grafana pour lui indiquer la nouvelle IP du conteneur. En effet, le data
|
|
||||||
container préserve les données, mais un changement d'IP n'est pas
|
|
||||||
répercuté. Pour cela, il nous fait un script d'initialisation, qui va écrire
|
|
||||||
l'ip de notre conteneur Docker dans la table `data_source` :
|
|
||||||
|
|
||||||
Petit indice, les requêtes SQL sont les suivantes :
|
Afin de faire bénéficier à nos utilisateurs d'une immersion parfaite, nous
|
||||||
|
allons faire en sorte que notre image permette d'être utilisée ainsi :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```
|
```
|
||||||
DELETE FROM "data_source";
|
42sh$ docker run -d -p 80:80 youp0m -bind 80
|
||||||
INSERT INTO "data_source" VALUES(1,1,0,'influxdb','influx','direct','http://${}:8086/','user','pass','metrics',0,'','',0,'null','2015-10-29 09:00:00','2015-10-29 09:05:00');
|
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
La base se trouve dans `/var/lib/grafana/grafana.db`.
|
|
||||||
|
Plutôt que de laisser l'utilisateur se débrouiller avec le chemin interne dans
|
||||||
|
lequel il va trouver le bon binaire :
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```
|
||||||
|
42sh$ docker run -d -p 80:80 youp0m /srv/youp0m -bind 80
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Essayez les deux commandes, si vous avez utilisé l'instruction `CMD` dans votre
|
||||||
|
`Dockerfile` jusqu'à présent, vous devez vous trouver dans le deuxième cas.
|
||||||
|
|
||||||
|
Pour améliorer la situation, définissez
|
||||||
|
l'[`ENTRYPOINT`](https://docs.docker.com/engine/reference/builder/#entrypoint)
|
||||||
|
de votre image sur le binaire `/srv/youp0m`.
|
||||||
|
|
||||||
|
|
||||||
|
## Point d'entrée avancé
|
||||||
|
|
||||||
|
Dans certains cas, il peut être nécessaire au lancement d'un conteneur, de
|
||||||
|
faire un minimum d'étapes d'initialisation avant que le conteneur ne soit
|
||||||
|
opérationnel (rappelez-vous les options que l'on passait à l'image `mysql` pour
|
||||||
|
créer un utilisateur et une base).
|
||||||
|
|
||||||
|
Notre but, dans cette partie, sera de créer un utilisateur administrateur
|
||||||
|
(pouvant passer le contrôle d'accès <http://localhost:8080/admin/>) :
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```
|
||||||
|
42sh$ docker run -i --rm -p 8080:8080 -e YOUP0M_PASSWORD=admin youp0m
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
### Bases du script
|
||||||
|
|
||||||
|
Notre script d'`ENTRYPOINT` sera appelé avec en argument, ceux passés par
|
||||||
|
l'utilisateur après le nom de l'image, ou, à défaut, le contenu de `CMD`.
|
||||||
|
|
||||||
|
C'est donc l'`ENTRYPOINT` qui est responsable de la bonne utilisation de
|
||||||
|
ceux-ci, de leur modification, ...
|
||||||
|
|
||||||
|
À la fin d'un script d'`ENTRYPOINT`, afin de garder comme premier processus du
|
||||||
|
conteneur le programme qui nous intéresse, on réalise un `execve(2)`, sans
|
||||||
|
`fork(2)` :
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```shell
|
||||||
|
exec /srv/youp0m $@
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Dans cet exemple : `exec` est la commande interne à notre shell pour lui
|
||||||
|
indiquer de remplacer son fil d'exécution par cette commande (sans `exec`, il
|
||||||
|
va `fork(2)` avant). `$@` est ici pour transmettre tel quel la liste des
|
||||||
|
arguments passés au script (il s'agit de ceux donnés par l'utilisateur, sur la
|
||||||
|
ligne de commande du `run`, ou du contenu de `CMD` si l'utilisateur n'a rien
|
||||||
|
précisé).
|
||||||
|
|
||||||
|
|
||||||
|
### Format du fichier `htpasswd`
|
||||||
|
|
||||||
|
Le format attendu est celui d'un fichier `htpasswd` typique d'Apache. Vous
|
||||||
|
pourriez obtenir un fichier valide avec :
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```shell
|
||||||
|
(
|
||||||
|
echo -n "$YOUP0M_USERNAME"
|
||||||
|
echo -n ":"
|
||||||
|
openssl passwd -crypt "$YOUP0M_PASSWORD"
|
||||||
|
) > myhtpasswd
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Il faut ensuite passer le fichier sur la ligne de commande grâce à l'option
|
||||||
|
`-htpasswd`.
|
||||||
|
|
||||||
|
|
||||||
|
### Exercice
|
||||||
|
|
||||||
|
Écrivez un script d'`ENTRYPOINT`, analysant les variables d'environnement, à la
|
||||||
|
recherche de `YOUP0M_USERNAME` et `YOUP0M_PASSWORD` pour initialiser le fichier
|
||||||
|
`.htpasswd` qui sera ajouté à la liste des arguments à passer au service.
|
||||||
|
|
||||||
|
Par exemple :
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```
|
||||||
|
42sh$ docker run -d -p 8081:8081 -e YOUP0M_USERNAME=admin -e YOUP0M_PASSWORD=admin youp0m -bind=8081
|
||||||
|
|
||||||
|
42sh$ curl -u admin:badpasswd http://localhost:8081/admin/
|
||||||
|
You are not allowed to perform this request.
|
||||||
|
|
||||||
|
42sh$ curl -u admin:admin http://localhost:8081/admin/
|
||||||
|
<!DOCTYPE html>
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
@ -11,40 +11,6 @@ La machine (notre première image Docker) contiendra tout le nécessaire pour
|
|||||||
faire fonctionner notre service de monitoring.
|
faire fonctionner notre service de monitoring.
|
||||||
|
|
||||||
|
|
||||||
## Les caches
|
|
||||||
|
|
||||||
Nous avons vu que chaque instruction de notre `Dockerfile` est exécutée dans un
|
|
||||||
conteneur, qui génère ensuite une image intermédiaire. Cette image
|
|
||||||
intermédiaire sert ensuite d'image de base pour l'instruction suivante.
|
|
||||||
|
|
||||||
Lorsque l'on lance la reconstruction du même `Dockerfile`, les images
|
|
||||||
intermédiaires sont utilisées comme un cache d'instructions, permettant ainsi
|
|
||||||
de gagner du temps sur les étapes qui n'ont pas changées. Ainsi, lorsque vous
|
|
||||||
modifiez une instruction dans votre `Dockerfile`, les instructions précédentes
|
|
||||||
ne sont pas réexécutées mais sont ressorties du cache.
|
|
||||||
|
|
||||||
Le cache se base principalement sur le contenu de chaque instruction du
|
|
||||||
`Dockerfile` (pour les `COPY` et `ADD`, il va aussi regarder la date de
|
|
||||||
dernière modification de fichier à copier ou à ajouter). Donc tant qu'une
|
|
||||||
instruction n'est pas modifiée dans le `Dockerfile`, le cache sera utilisé.
|
|
||||||
|
|
||||||
Il est possible de ne pas utiliser le cache et de relancer toutes les étapes du
|
|
||||||
`Dockerfile` en ajoutant l'option `--no-cache` au moment du `docker image
|
|
||||||
build`.
|
|
||||||
|
|
||||||
Les couches du cache peuvent être partagées entre plusieurs conteneur, c'est
|
|
||||||
ainsi que vous pouvez partager facilement une plus grosse partie du système de
|
|
||||||
fichiers.
|
|
||||||
|
|
||||||
Pour profiter du cache, on va placer de préférences les étapes les plus
|
|
||||||
génériques (qui seraient les plus susceptibles d'apparaître dans d'autres
|
|
||||||
images), en haut du `Dockerfile`.
|
|
||||||
|
|
||||||
Commençons donc notre `Dockerfile` : choisissez une image de base pour remplir
|
|
||||||
votre `FROM`, et indiquez votre nom avec l'instruction `LABEL maintainer` (pour
|
|
||||||
indiquer que c'est vous qui maintenez cette image, si des utilisateurs ont
|
|
||||||
besoin de vous avertir pour le mettre à jour ou s'ils rencontrent des
|
|
||||||
difficultés par exemple).
|
|
||||||
|
|
||||||
|
|
||||||
## `RUN` ou script ?
|
## `RUN` ou script ?
|
||||||
|
@ -4,7 +4,7 @@ Retour sur les bonnes pratiques
|
|||||||
===============================
|
===============================
|
||||||
|
|
||||||
Pour chaque bonne pratique ci-dessous, vérifiez que vous la respectez
|
Pour chaque bonne pratique ci-dessous, vérifiez que vous la respectez
|
||||||
bien, faites les modifications nécessaires.
|
bien, faites les modifications nécessaires dans votre `Dockerfile`.
|
||||||
|
|
||||||
## Utilisez le fichier `.dockerignore`
|
## Utilisez le fichier `.dockerignore`
|
||||||
|
|
||||||
|
@ -38,23 +38,11 @@ Installons maintenant un programme :
|
|||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
En attendant la fin de l'installation, jetons un œil à la commande dans un
|
|
||||||
autre terminal :
|
|
||||||
|
|
||||||
<div lang="en-US">
|
|
||||||
```
|
|
||||||
docker container ls
|
|
||||||
```
|
|
||||||
</div>
|
|
||||||
|
|
||||||
Cette commande liste les conteneurs actifs. Notez le *Container ID* ainsi que
|
|
||||||
le *NAMES* du conteneur actuellement en cours d'installation de `nano`.
|
|
||||||
|
|
||||||
Lorsque l'installation de `nano` est terminée, quittez l'image en tapant
|
Lorsque l'installation de `nano` est terminée, quittez l'image en tapant
|
||||||
`exit`.
|
`exit`.
|
||||||
|
|
||||||
Sauvegardez votre image modifiée avec la commande `commit` pour pouvoir
|
Sauvegardez vos modifications en tant que nouvelle image Docker, avec
|
||||||
commencer directement de votre image avec `nano` :
|
la commande `commit` :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```
|
```
|
||||||
@ -62,9 +50,11 @@ commencer directement de votre image avec `nano` :
|
|||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
en remplaçant `CONTAINER` par le nom ou l'identifiant de votre
|
en remplaçant `CONTAINER` par le nom ou l'identifiant du container qui
|
||||||
container. `my_nano` est le nom que vous voudrez utiliser à la place
|
doit servir de modèle. `my_nano` est le nom que vous voudrez utiliser
|
||||||
d'`ubuntu` :
|
à la place d'`ubuntu`.
|
||||||
|
|
||||||
|
Testons sans plus attendre notre nouvelle image :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```
|
```
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
\newpage
|
|
||||||
|
|
||||||
Multi-stage build
|
|
||||||
=================
|
|
||||||
|
|
||||||
TODO
|
|
@ -1,3 +1,56 @@
|
|||||||
|
\newpage
|
||||||
|
|
||||||
|
Projet et rendu
|
||||||
|
===============
|
||||||
|
|
||||||
|
Projet
|
||||||
|
------
|
||||||
|
|
||||||
|
Avec l'aide d'un `Dockerfile` *multi-stage*, réalisez l'image la plus petite
|
||||||
|
possible (partant d'un `FROM scratch`), qui permette d'utiliser la [page de
|
||||||
|
compte à rebours](https://virli.nemunai.re/countdown.html) avec cette
|
||||||
|
configuration pour nginx :
|
||||||
|
|
||||||
|
```conf
|
||||||
|
events {}
|
||||||
|
|
||||||
|
http {
|
||||||
|
default_type text/html;
|
||||||
|
|
||||||
|
index countdown.html;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
|
||||||
|
root /srv/http;
|
||||||
|
|
||||||
|
rewrite "^/[0-9]+:[0-9]{2}$" /countdown.html;
|
||||||
|
rewrite "^/[0-9]+$" /countdown.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Vous pouvez envisager dans un premier temps d'extraire de l'image `nginx`, le
|
||||||
|
binaire `nginx` lui-même et observer les différents problèmes. Vous pourrez
|
||||||
|
ensuite par exemple envisager de compiler `nginx` (vous trouverez les sources
|
||||||
|
du projet : <http://nginx.org/download>).
|
||||||
|
|
||||||
|
Dans tous les cas, votre `Dockerfile` devra être facilement maintenable
|
||||||
|
(notamment en cas de nouvelle version du serveur web), et vous devrez apporter
|
||||||
|
une attention particulière au suivi des bonnes pratiques d'écriture des
|
||||||
|
`Dockerfile`.
|
||||||
|
|
||||||
|
### Exemple d'exécution
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```
|
||||||
|
42sh$ docker build -t countdown countdown
|
||||||
|
42sh$ docker run -d -P countdown
|
||||||
|
42sh$ firefox http://localhost:32198/42:23
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
Modalités de rendu
|
Modalités de rendu
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
@ -13,6 +66,9 @@ et exclusivement à celle-ci que vous devez envoyer vos rendus. Tout rendu
|
|||||||
envoyé à une autre adresse et/ou non signé et/ou reçu après la correction ne
|
envoyé à une autre adresse et/ou non signé et/ou reçu après la correction ne
|
||||||
sera pas pris en compte.
|
sera pas pris en compte.
|
||||||
|
|
||||||
|
Par ailleurs, n'oubliez pas de répondre à
|
||||||
|
[l'évaluation du cours](https://www.epitaf.fr/moodle/mod/quiz/view.php?id=215).
|
||||||
|
|
||||||
|
|
||||||
Tarball
|
Tarball
|
||||||
-------
|
-------
|
||||||
@ -20,20 +76,23 @@ Tarball
|
|||||||
Tous les fichiers identifiés comme étant à rendre pour ce TP sont à
|
Tous les fichiers identifiés comme étant à rendre pour ce TP sont à
|
||||||
placer dans une tarball (pas d'archive ZIP, RAR, ...).
|
placer dans une tarball (pas d'archive ZIP, RAR, ...).
|
||||||
|
|
||||||
Voici une arborescence type (vous pourriez avoir des fichiers supplémentaires,
|
Voici une arborescence type (vous pourriez avoir des fichiers
|
||||||
cela dépendra de votre avancée dans le projet) :
|
supplémentaires) :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```
|
```
|
||||||
login_x-TP1_5/influxdb/Dockerfile
|
login_x-TP2/
|
||||||
login_x-TP1_5/influxdb/influxdb.conf
|
login_x-TP2/youp0m/
|
||||||
login_x-TP1_5/chronograf
|
login_x-TP2/youp0m/Dockerfile
|
||||||
login_x-TP1_5/chronograf/Dockerfile
|
login_x-TP2/youp0m/entrypoint.sh
|
||||||
login_x-TP1_5/chronograf/chronograf.conf
|
login_x-TP2/youp0m/.dockerignore
|
||||||
login_x-TP1_5/mymonitoring
|
login_x-TP2/youp0m/...
|
||||||
login_x-TP1_5/mymonitoring/Dockerfile
|
login_x-TP2/countdown/Dockerfile
|
||||||
login_x-TP1_5/mymonitoring/chronograf.conf
|
(login_x-TP2/countdown/nginx.conf)
|
||||||
login_x-TP1_5/mymonitoring/influxdb.conf
|
(login_x-TP2/countdown/countdown.html)
|
||||||
login_x-TP1_5/mymonitoring/supervisor.conf
|
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
Les deux fichiers `nginx.conf` et `countdown.html` seront écrasés par
|
||||||
|
les fichiers fournis lors de la correction, vous n'êtes pas donc
|
||||||
|
obligés de les embarquer dans votre rendu.
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
\newpage
|
|
||||||
|
|
||||||
Une application par conteneur
|
|
||||||
=============================
|
|
||||||
|
|
||||||
Avec notre conteneur utilisant `supervisor`, nous ne respectons pas
|
|
||||||
cette dernière bonne pratique d'un seul processus par conteneur :-(
|
|
||||||
|
|
||||||
L'intérêt est de permettre à chaque conteneur d'effectuer une tâche
|
|
||||||
simple et générique, de manière à pouvoir être réutilisé pour d'autres
|
|
||||||
projets dans le futur. Par exemple, notre conteneur InfluxDB pourra
|
|
||||||
être utilisé pour stocker des relevés de métriques d'autres systèmes
|
|
||||||
ou des logs. Chronograf peut être connecté à d'autres serveurs afin
|
|
||||||
de corréler les métriques, ...
|
|
||||||
|
|
||||||
|
|
||||||
## Séparer le `Dockerfile`
|
|
||||||
|
|
||||||
Commençons par séparer notre `Dockerfile` en deux : dans une partie
|
|
||||||
nous allons garder la partie InfluxDB, de l'autre la partie Chronograf.
|
|
||||||
|
|
||||||
Il va vous falloir créer deux dossiers distincts, il en faut un par
|
|
||||||
`Dockerfile` : réutilisez l'image `influxdb` créée précédemment et créez le
|
|
||||||
dossier pour l'image `chronograf`.
|
|
||||||
|
|
||||||
\vspace{1em}
|
|
||||||
|
|
||||||
Pour tester la bonne marche de vos conteneurs, vous pouvez le lancer votre
|
|
||||||
conteneur chronograf avec la commande suivante (en considérant que votre
|
|
||||||
conteneur influxdb de la première partie est toujours lancé).
|
|
||||||
|
|
||||||
<div lang="en-US">
|
|
||||||
```shell
|
|
||||||
docker run --rm --link YOUR_INFLUX_CNTR_NAME:influxdb chronograf
|
|
||||||
```
|
|
||||||
</div>
|
|
||||||
|
|
||||||
Remplacez `YOUR_INFLUX_CNTR_NAME` par le nom du conteneur qui fait tourner
|
|
||||||
votre influxdb. En créant ce lien, `chronograf` sera capable de contacter une
|
|
||||||
machine nommée `influxdb` (indiqué par la partie du lien après les `:`).
|
|
||||||
|
|
||||||
|
|
||||||
### Visualiser les données dans `chronograf`
|
|
||||||
|
|
||||||
Avant d'arrêter `telegraf` et nos conteneurs pour passer à une nouvelle étape,
|
|
||||||
prenez le temps d'afficher les données que vous avez collecté depuis le début
|
|
||||||
du TP.
|
|
||||||
|
|
||||||
Après avoir ajouté le serveur (en remplaçant `localhost` proposé par défaut par
|
|
||||||
`influxdb` issue du *link*), ajouter deux visualisations avec les requêtes
|
|
||||||
suivantes :
|
|
||||||
|
|
||||||
<div lang="en-US">
|
|
||||||
```sql
|
|
||||||
SELECT used, available, cached FROM mem WHERE tmpltime()
|
|
||||||
SELECT mean(usage_idle) FROM cpu WHERE tmpltime() GROUP BY time(20s), cpu
|
|
||||||
```
|
|
||||||
</div>
|
|
@ -1,104 +0,0 @@
|
|||||||
\newpage
|
|
||||||
|
|
||||||
Plusieurs daemons dans un conteneur
|
|
||||||
===================================
|
|
||||||
|
|
||||||
Notre système de monitoring commence enfin à ressembler à quelque chose. Mais
|
|
||||||
ce serait tellement plus pratique de voir tous ces tableaux de nombres sous
|
|
||||||
forme de graphiques !
|
|
||||||
|
|
||||||
Nous allons pour cela ajouter `chronograf` dans notre image.
|
|
||||||
|
|
||||||
Avant de modifier votre `Dockerfile`, créez un nouveau dossier de rendu :
|
|
||||||
`mymonitoring`, dans lequel vous recopierez l'état actuel de notre image
|
|
||||||
`influxdb`.
|
|
||||||
|
|
||||||
|
|
||||||
## Chronograf
|
|
||||||
|
|
||||||
Commençons par compléter la commande d'installation existante pour `influxdb`,
|
|
||||||
afin d'installer simultanément `chronograf`.
|
|
||||||
|
|
||||||
La documentation de la procédure est disponible
|
|
||||||
[à cette adresse](https://docs.influxdata.com/chronograf/v1.6/introduction/installation/).
|
|
||||||
|
|
||||||
|
|
||||||
## Script d'init
|
|
||||||
|
|
||||||
Lors du dernier TP, nous avons vu que les conteneurs s'arrêtaient dès que le
|
|
||||||
premier processus du conteneur (celui qui a le PID 1, à la place d'`init`)
|
|
||||||
terminait son exécution, quelque soit le statut de ses éventuels fils.
|
|
||||||
|
|
||||||
Pour lancer tous nos daemons, nous avons donc besoin d'écrire un script qui
|
|
||||||
lance puis attend que les deux deamons aient terminés de s'exécuter.
|
|
||||||
|
|
||||||
Écrivons ce script. Hints : `wait(1)`.
|
|
||||||
|
|
||||||
\vspace{1em}
|
|
||||||
|
|
||||||
Pour vérifier que votre conteneur fonctionne correctement, vous pouvez le
|
|
||||||
lancer :
|
|
||||||
|
|
||||||
<div lang="en-US">
|
|
||||||
```shell
|
|
||||||
docker run --rm -p 10000:10000 mymonitoring
|
|
||||||
```
|
|
||||||
</div>
|
|
||||||
|
|
||||||
Puis accéder à chronograf : <http://localhost:10000/>. Donnez un nom à votre
|
|
||||||
configuration, puis cliquez sur *Add*. Les paramètres préremplis dans le
|
|
||||||
formulaire sont corrects.
|
|
||||||
|
|
||||||
Vous devriez obtenir l'écran suivant (notez la partie `Status: Online, v1.0.0`) :
|
|
||||||
|
|
||||||
![Chronograf configuré](chronograf_setup.png)
|
|
||||||
|
|
||||||
|
|
||||||
## Autorestart
|
|
||||||
|
|
||||||
L'avantage de détruire le conteneur à la mort du père, est que s'il s'agit de
|
|
||||||
notre processus principal et qu'il est seul (par exemple `nginx` pour un
|
|
||||||
conteneur qui délivre des pages web), il va être possible de redémarrer le
|
|
||||||
conteneur automatiquement grâce à la *restart policy* que l'on peut définir au
|
|
||||||
moment du `docker run` :
|
|
||||||
|
|
||||||
<div lang="en-US">
|
|
||||||
```shell
|
|
||||||
docker run -d -p 80:80 --restart=on-failure nginx
|
|
||||||
```
|
|
||||||
</div>
|
|
||||||
|
|
||||||
Il existe trois règles de redémarrage différentes :
|
|
||||||
|
|
||||||
- **`no` :** il s'agit de la règle par défaut. Lorsque l'exécution du conteneur
|
|
||||||
se termine, il n'est pas redémarré.
|
|
||||||
- **`on-failure[:max-retries]` :** redémarre uniquement si le code de sortie du
|
|
||||||
conteneur n'est pas 0. Il est possible de préciser pour cette option le
|
|
||||||
nombre maximum de redémarrage qui sera tenté.
|
|
||||||
- **`always` :** redémarre le conteneur dans tous les cas, quelque soit son
|
|
||||||
code de sortie et indéfiniment.
|
|
||||||
|
|
||||||
Le script d'init que vous avez réalisé ne tient sans doute pas compte de
|
|
||||||
cela. Mais plein de gens ont cette problématique et l'application `supervisor`
|
|
||||||
répond parfaitement à notre problématique !
|
|
||||||
|
|
||||||
|
|
||||||
## `supervisor`
|
|
||||||
|
|
||||||
Première étape : installer `supervisor`, le paquet se trouve dans les dépôts.
|
|
||||||
|
|
||||||
L'étape suivante consiste à remplir puis copier le fichier de configuration
|
|
||||||
dans le conteneur. Vous allez devoir écraser dans votre conteneur le fichier
|
|
||||||
`/etc/supervisord.conf` pour démarrer à la fois `chronograf` et `influxdb`.
|
|
||||||
|
|
||||||
Vous pouvez vous aider de la documentation disponible à :
|
|
||||||
<http://supervisord.org/configuration.html>
|
|
||||||
|
|
||||||
|
|
||||||
La même procédure de test que précédemment peut être suivie.
|
|
||||||
|
|
||||||
|
|
||||||
## Rendu
|
|
||||||
|
|
||||||
Nous ne toucherons plus à cette image, placez-la dans un dossier
|
|
||||||
`mymonitoring`.
|
|
Loading…
Reference in New Issue
Block a user