From 55817a20739ebfa44c458757e37008b0024c9298 Mon Sep 17 00:00:00 2001 From: nemunaire Date: Thu, 18 Oct 2018 07:05:36 +0200 Subject: [PATCH] Dockerfile part done --- tutorial/docker-advanced/chronograf_setup.png | Bin 35 -> 23649 bytes tutorial/dockerfiles/Makefile | 2 +- tutorial/dockerfiles/chronograf_setup.png | Bin 23649 -> 0 bytes tutorial/dockerfiles/dockerfile.md | 232 +++++++++++++++--- tutorial/dockerfiles/entrypoint.md | 115 ++++++++- tutorial/dockerfiles/first.md | 34 --- tutorial/dockerfiles/goodpractices.md | 2 +- tutorial/dockerfiles/interactive.md | 24 +- tutorial/dockerfiles/multistaged.md | 6 - tutorial/dockerfiles/rendu.md | 83 ++++++- tutorial/dockerfiles/split.md | 58 ----- tutorial/dockerfiles/supervisor.md | 104 -------- 12 files changed, 381 insertions(+), 279 deletions(-) mode change 120000 => 100644 tutorial/docker-advanced/chronograf_setup.png delete mode 100644 tutorial/dockerfiles/chronograf_setup.png delete mode 100644 tutorial/dockerfiles/multistaged.md delete mode 100644 tutorial/dockerfiles/split.md delete mode 100644 tutorial/dockerfiles/supervisor.md diff --git a/tutorial/docker-advanced/chronograf_setup.png b/tutorial/docker-advanced/chronograf_setup.png deleted file mode 120000 index dcbd7d3..0000000 --- a/tutorial/docker-advanced/chronograf_setup.png +++ /dev/null @@ -1 +0,0 @@ -../dockerfiles/chronograf_setup.png \ No newline at end of file diff --git a/tutorial/docker-advanced/chronograf_setup.png b/tutorial/docker-advanced/chronograf_setup.png new file mode 100644 index 0000000000000000000000000000000000000000..25d005ad8c79131e86f90582ccc0275e850cce32 GIT binary patch literal 23649 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 cIyO-(_UB6}*5|(hx @@ -19,7 +17,7 @@ que l'on a réussi à faire précédemment en utilisant le `Dockerfile` suivant ``` -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` :
``` @@ -37,7 +35,7 @@ commande `build` : ```
-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 :
@@ -62,9 +60,21 @@ Cela signifie que l'exemple suivant **ne fonctionne pas** : ```
-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` : + +
+``` + docker container run tinysql service mysqld start +``` +
+ +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 : ``` -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 :
``` @@ -95,30 +109,90 @@ Construisons maintenant un conteneur avec un serveur web :
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 :
``` - 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 ```
-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 . *À 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 : + +
+``` + LABEL maintainer="Pierre-Olivier Mercier " +``` +
+ +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 :
``` - 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 ```
@@ -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 : + +
+``` + 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"] +``` +
+ +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 : + +
+``` + 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"] +``` +
+ +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` : + +
+``` + 42sh$ docker build --target builder -t hello-builder . +``` +
+ +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 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` à : + +
+``` + https://git.nemunai.re/youp0m.git +``` +
diff --git a/tutorial/dockerfiles/entrypoint.md b/tutorial/dockerfiles/entrypoint.md index c1e560c..28288dc 100644 --- a/tutorial/dockerfiles/entrypoint.md +++ b/tutorial/dockerfiles/entrypoint.md @@ -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 :
``` - 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 ```
-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 : + +
+``` + 42sh$ docker run -d -p 80:80 youp0m /srv/youp0m -bind 80 +``` +
+ +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 ) : + +
+``` + 42sh$ docker run -i --rm -p 8080:8080 -e YOUP0M_PASSWORD=admin youp0m +``` +
+ +### 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)` : + +
+```shell + exec /srv/youp0m $@ +``` +
+ +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 : + +
+```shell + ( + echo -n "$YOUP0M_USERNAME" + echo -n ":" + openssl passwd -crypt "$YOUP0M_PASSWORD" + ) > myhtpasswd +``` +
+ +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 : + +
+``` + 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/ + +``` +
diff --git a/tutorial/dockerfiles/first.md b/tutorial/dockerfiles/first.md index cbe3a62..cc389ed 100644 --- a/tutorial/dockerfiles/first.md +++ b/tutorial/dockerfiles/first.md @@ -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 ? diff --git a/tutorial/dockerfiles/goodpractices.md b/tutorial/dockerfiles/goodpractices.md index 482a39c..33cb584 100644 --- a/tutorial/dockerfiles/goodpractices.md +++ b/tutorial/dockerfiles/goodpractices.md @@ -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` diff --git a/tutorial/dockerfiles/interactive.md b/tutorial/dockerfiles/interactive.md index e290485..886bfc9 100644 --- a/tutorial/dockerfiles/interactive.md +++ b/tutorial/dockerfiles/interactive.md @@ -38,23 +38,11 @@ Installons maintenant un programme : ``` -En attendant la fin de l'installation, jetons un œil à la commande dans un -autre terminal : - -
-``` - docker container ls -``` -
- -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` :
``` @@ -62,9 +50,11 @@ commencer directement de votre image avec `nano` : ```
-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 :
``` diff --git a/tutorial/dockerfiles/multistaged.md b/tutorial/dockerfiles/multistaged.md deleted file mode 100644 index 55d144e..0000000 --- a/tutorial/dockerfiles/multistaged.md +++ /dev/null @@ -1,6 +0,0 @@ -\newpage - -Multi-stage build -================= - -TODO diff --git a/tutorial/dockerfiles/rendu.md b/tutorial/dockerfiles/rendu.md index 89c375b..5ee8b2b 100644 --- a/tutorial/dockerfiles/rendu.md +++ b/tutorial/dockerfiles/rendu.md @@ -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 : ). + +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 + +
+``` + 42sh$ docker build -t countdown countdown + 42sh$ docker run -d -P countdown + 42sh$ firefox http://localhost:32198/42:23 +``` +
+ + 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) :
``` - 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) ```
+ +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. diff --git a/tutorial/dockerfiles/split.md b/tutorial/dockerfiles/split.md deleted file mode 100644 index 93648f9..0000000 --- a/tutorial/dockerfiles/split.md +++ /dev/null @@ -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é). - -
-```shell - docker run --rm --link YOUR_INFLUX_CNTR_NAME:influxdb chronograf -``` -
- -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 : - -
-```sql - SELECT used, available, cached FROM mem WHERE tmpltime() - SELECT mean(usage_idle) FROM cpu WHERE tmpltime() GROUP BY time(20s), cpu -``` -
diff --git a/tutorial/dockerfiles/supervisor.md b/tutorial/dockerfiles/supervisor.md deleted file mode 100644 index f0d6b68..0000000 --- a/tutorial/dockerfiles/supervisor.md +++ /dev/null @@ -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 : - -
-```shell -docker run --rm -p 10000:10000 mymonitoring -``` -
- -Puis accéder à chronograf : . 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` : - -
-```shell -docker run -d -p 80:80 --restart=on-failure nginx -``` -
- -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 à : - - - -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`.