Dockerfile part done

This commit is contained in:
nemunaire 2018-10-18 07:05:36 +02:00
parent 02db9cc19c
commit 55817a2073
12 changed files with 381 additions and 279 deletions

View File

@ -1 +0,0 @@
../dockerfiles/chronograf_setup.png

Before

Width:  |  Height:  |  Size: 35 B

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 B

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -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 \
--standalone \
--normalize \

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

View File

@ -1,13 +1,11 @@
\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
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
de *commits*. Docker dispose d'un mécanisme permettant d'automatiser la
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 :
<div lang="en-US">
@ -19,7 +17,7 @@ que l'on a réussi à faire précédemment en utilisant le `Dockerfile` suivant
```
</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
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
but de le construire.
Pour lancer la construction de la nouvelle image, créer un nouveau dossier ne
contenant que votre fichier `Dockerfile`, placez-vous dedans, puis utilisez la
commande `build` :
Pour lancer la construction de la nouvelle image, créons un nouveau dossier ne
contenant que votre fichier `Dockerfile`, plaçons-nous ensuite dedans, puis
lançons la commande `build` :
<div lang="en-US">
```
@ -37,7 +35,7 @@ commande `build` :
```
</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 :
<div lang="en-US">
@ -62,9 +60,21 @@ Cela signifie que l'exemple suivant **ne fonctionne pas** :
```
</div>
Cet exemple ne fonctionne pas car le serveur MySQL qui est lancé dans le
premier `RUN`, n'est plus lancé au moment du deuxième `RUN`. En effet, chaque
commande du `Dockerfile` a pour but de modifier le système de fichiers.
Cet exemple ne fonctionne pas car le serveur MySQL est bien lancé dans le
premier `RUN`, mais il se trouve brûtalement arrêté dès lors que la commande
`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 :
@ -75,13 +85,17 @@ Pour avoir le résultat escompté, il faut exécuter les commandes ensemble :
```
</div>
Après le `RUN`, MySQL sera de nouveau arrêté, si on veut l'utiliser dans le
conteneur, il ne faudra pas oublier de lancer le processus.
Après le `RUN`, MySQL sera de nouveau tué.
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
Construisons maintenant un conteneur avec un serveur web :
Construisons maintenant un conteneur avec un service web :
<div lang="en-US">
```
@ -95,30 +109,90 @@ Construisons maintenant un conteneur avec un serveur web :
</div>
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
image écoute.
à l'argument `--expose`). Il s'agit d'une métadonnée qui sera attachée à
l'image (et à toutes ses images filles).
En utilisant l'option `-P` du `run`, vous allez pouvoir assigner une
redirection de port aléatoire sur la machine hôte vers votre conteneur :
En précisant tous les ports qu'exposent une image dans ses métadonnées, ces
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">
```
docker image build --tag=my_webserver .
docker container run -it -P my_webserver /bin/bash
service nginx start
42sh$ docker image build --tag=my_webserver .
42sh$ docker container run -it -P my_webserver /bin/bash
(cntnr)# service nginx start
```
</div>
Dans un autre terminal, lancer un `docker ps` et consulter la colonne *PORTS*
pour connaître le port choisi par Docker pour effectuer la redirection.
Dans un autre terminal, lancer un `docker container ls` et consulter la colonne
*PORTS* pour connaître le port choisi par Docker pour effectuer la redirection.
Rendez-vous ensuite dans votre navigateur sur <http://localhost:49153/>.
*À 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
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">
```
docker image build --tag=my_nginx .
docker container run -d -P my_nginx
42sh$ docker image build --tag=my_nginx .
42sh$ docker container run -d -P my_nginx
```
</div>
@ -142,16 +216,104 @@ retirez cette option pour voir ce qui ne va pas, ou utilisez la commande
`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 ?
Consultez <https://docs.docker.com/engine/reference/builder/> pour la liste
complète des instructions reconnues.
## Rendu
## Exercice
Rendez le fichier `Dockerfile` et son contexte (`index.html`, fichiers de conf
éventuels, ...) que vous avez utilisé pour réaliser votre image `my_webserver`.
Pour mettre en application tout ce que nous venons de voir, réalisons le
`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
pratiques vues en cours pour l'écriture du `Dockerfile`.
Pour réaliser ce genre de contribution, on ajoute généralement un `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>

View File

@ -1,21 +1,114 @@
\newpage
Entrypoint
==========
Personnalisation du point d'entrée du conteneur
===============================================
Jusque là, à chaque redémarrage d'InfluxDB, il est nécessaire de reconfigurer
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` :
## Point d'entrée basique
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">
```
DELETE FROM "data_source";
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');
42sh$ docker run -d -p 80:80 youp0m -bind 80
```
</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>

View File

@ -11,40 +11,6 @@ La machine (notre première image Docker) contiendra tout le nécessaire pour
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 ?

View File

@ -4,7 +4,7 @@ Retour sur les bonnes pratiques
===============================
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`

View File

@ -38,23 +38,11 @@ Installons maintenant un programme :
```
</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
`exit`.
Sauvegardez votre image modifiée avec la commande `commit` pour pouvoir
commencer directement de votre image avec `nano` :
Sauvegardez vos modifications en tant que nouvelle image Docker, avec
la commande `commit` :
<div lang="en-US">
```
@ -62,9 +50,11 @@ commencer directement de votre image avec `nano` :
```
</div>
en remplaçant `CONTAINER` par le nom ou l'identifiant de votre
container. `my_nano` est le nom que vous voudrez utiliser à la place
d'`ubuntu` :
en remplaçant `CONTAINER` 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`.
Testons sans plus attendre notre nouvelle image :
<div lang="en-US">
```

View File

@ -1,6 +0,0 @@
\newpage
Multi-stage build
=================
TODO

View File

@ -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
------------------
@ -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
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
-------
@ -20,20 +76,23 @@ Tarball
Tous les fichiers identifiés comme étant à rendre pour ce TP sont à
placer dans une tarball (pas d'archive ZIP, RAR, ...).
Voici une arborescence type (vous pourriez avoir des fichiers supplémentaires,
cela dépendra de votre avancée dans le projet) :
Voici une arborescence type (vous pourriez avoir des fichiers
supplémentaires) :
<div lang="en-US">
```
login_x-TP1_5/influxdb/Dockerfile
login_x-TP1_5/influxdb/influxdb.conf
login_x-TP1_5/chronograf
login_x-TP1_5/chronograf/Dockerfile
login_x-TP1_5/chronograf/chronograf.conf
login_x-TP1_5/mymonitoring
login_x-TP1_5/mymonitoring/Dockerfile
login_x-TP1_5/mymonitoring/chronograf.conf
login_x-TP1_5/mymonitoring/influxdb.conf
login_x-TP1_5/mymonitoring/supervisor.conf
login_x-TP2/
login_x-TP2/youp0m/
login_x-TP2/youp0m/Dockerfile
login_x-TP2/youp0m/entrypoint.sh
login_x-TP2/youp0m/.dockerignore
login_x-TP2/youp0m/...
login_x-TP2/countdown/Dockerfile
(login_x-TP2/countdown/nginx.conf)
(login_x-TP2/countdown/countdown.html)
```
</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.

View File

@ -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>

View File

@ -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`.