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 \
|
||||
--standalone \
|
||||
--normalize \
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 23 KiB |
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 ?
|
||||
|
|
|
@ -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`
|
||||
|
||||
|
|
|
@ -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">
|
||||
```
|
||||
|
|
|
@ -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
|
||||
------------------
|
||||
|
||||
|
@ -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.
|
||||
|
|
|
@ -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