diff --git a/tutorial/3/Makefile b/tutorial/3/Makefile new file mode 100644 index 0000000..7248a16 --- /dev/null +++ b/tutorial/3/Makefile @@ -0,0 +1,19 @@ +SOURCES = tutorial.md installation.md what.md first.md supervisor.md goodpractices.md compose.md project.md +TEMPLATE = ../../template.tex +PANDOCOPTS = --latex-engine=xelatex \ + --standalone \ + --normalize \ + --number-sections \ + -M lang=frenchb \ + -M fontsize=12pt \ + -M papersize=a4paper \ + --template=${TEMPLATE} + + +all: tutorial.pdf + +tutorial.pdf: ${SOURCES} + pandoc ${PANDOCOPTS} -o $@ $+ + +clean:: + rm tutorial.pdf diff --git a/tutorial/3/compose.md b/tutorial/3/compose.md new file mode 100644 index 0000000..93e8c4f --- /dev/null +++ b/tutorial/3/compose.md @@ -0,0 +1,36 @@ +\newpage + +# Compose + +Avec notre conteneur utilisant `supervisor`, nous ne respectons pas +bien 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 +générique, de manière à pouvoir être réutilisé pour d'autres projet +dans le futur. Par exemple, notre conteneur InfluxDB pourra être +utilisé pour stocker des relevés de métriques systèmes ou des logs. +Grafana peut également afficher davantage d'informations ou combiner +les informations de plusieurs bases distinctes. + + +## 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 Grafana. + +Il va vous falloir créer deux dossiers distincts, il en faut un par +`Dockerfile`. + +Profitez en pour rajouter les Data Volume Container. + + +## Automatiser le lancement + +Commencez par lancer tous vos conteneurs à la main pour voir les +étapes que vous allez devoir automatiser. + +Au lieu de faire un script pour construire et lancer tous vos +conteneurs, définissez à la racine de votre projet un fichier +`docker-compose.yml` qui contiendra les méthodes de construction et +les paramètres d'exécution. diff --git a/tutorial/3/entrypoint.md b/tutorial/3/entrypoint.md new file mode 100644 index 0000000..e5d5dc2 --- /dev/null +++ b/tutorial/3/entrypoint.md @@ -0,0 +1,18 @@ +\newpage + +# Entrypoint + +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` : + +Petit indice, les requêtes SQL sont les suivantes : + +``` +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'); +``` + +La base se trouve dans `/var/lib/grafana/grafana.db`. diff --git a/tutorial/3/first.md b/tutorial/3/first.md new file mode 100644 index 0000000..36bdbc2 --- /dev/null +++ b/tutorial/3/first.md @@ -0,0 +1,85 @@ +\newpage + +# Premiers pas + +Dans un premier temps, nous allons créer une image Docker comme si +l'on réalisait l'installation sur une machine classique : en suivant +une recette. La machine (notre première image Docker) contient tout le +nécessaire pour faire fonctionner notre service. + + +## Les caches + +Nous avons vu que chaque instruction de notre `Dockerfile` génère une +couche. Chaque couche sert de cache d'une construction de conteneur à +l'autre. 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 basant principalement sur le contenu de chaque instruction +dans le `Dockerfile` (pour les `COPY` et `ADD`, il va aussi regarder +la date de dernière modification de fichier copié ou ajouté). 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 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 fichier (afin de profiter du cache du système de +fichiers au moment de l'exécution du conteneur). + + +## `apt-get` + +Pour profiter du cache, il faut donc placer les étapes les plus +génériques (qui seraient susceptibles d'apparaître dans plusieurs +conteneur), en haut du `Dockerfile`. + +Commençons donc notre `Dockerfile` : choisissez une image de base pour +votre `FROM`, et indiquez votre nom avec l'instruction `MAINTAINER`, +pour indiquez que c'est vous qui maintenez ce conteneur (si d'autres +gens ont besoin qu'il faut le mettre à jour par exemple). + + +## `RUN` ou script ? + +### InfluxDB + +Ensuite viens l'installation d'InfluxDB. Le paquet n'est pas +disponible dans les dépôts. La +[https://influxdb.com/docs/v0.9/introduction/installation.html](procédure +décrite sur le site) incite à télécharger le paquet mis à disposition +puis à l'installer via `dpkg -i`. + +Deux solutions s'offrent à nous : télécharger le paquet hors du +conteneur, le copier, puis l'installer. Ou faire un `RUN` avec toutes +ces opérations (sans oublier l'installation de `wget`/`curl`). + +La copie étant définitive (supprimer le fichier ne le supprimera pas +des couches où il a pu exister), donc la seconde solution semble +préférable (mais `wget` restera en déchet). + +Écrivez une commande `RUN` qui va télécharger la dernière version +d'InfluxDB, qui va l'installer et supprimer le fichier. + +\vspace{1em} + +À ce stade, nous pouvons déjà terminer le conteneur et tester +qu'InfluxDB est bien utilisable : `EXPOSE`, `CMD`, ... Il est possible +que vous ayez à écraser le fichier de configuration via un +`COPY`. Garder la ligne qui vous permet de lancer votre serveur web +dans un coin, en attendant la partie suivante. + + +### Grafana + +Une fois InfluxDB configuré, nous allons avoir la même réflexion avec +Grafana. + +De la même manière, téléchargez, installez et supprimez le paquet. + +Lors de vos tests, sachez que vous pouvez vous connecter sur grafana avec +l'utilisateur *admin*, mot de passe *admin*. diff --git a/tutorial/3/goodpractices.md b/tutorial/3/goodpractices.md new file mode 100644 index 0000000..ba7194c --- /dev/null +++ b/tutorial/3/goodpractices.md @@ -0,0 +1,214 @@ +\newpage + +# Retour sur les bonnes pratiques + +http://docs.docker.com/articles/dockerfile_best-practices/ + +## Utilisez le fichier `.dockerignore` + +Dans la plupart des cas, vos Dockerfile seront dans des dossiers contenant +beaucoup de fichiers qui ne sont pas nécessaire à la construction de votre +conteneur (par exemple, vous pouvez avoir un `Dockerfile` placé à la racine +d'un dépôt git : il va avoir besoin des binaires compilés, mais pas des +sources). + +Afin d'améliorer les performances lors de la construction, vous pouvez exclure +les fichiers et dossiers inutiles au conteneur en ajoutant un fichier +`.dockerignore` dans le répertoire de votre `Dockerfile`. + +Ce fichier fonctionne de la même manière que le `.gitignore` : vous pouvez +utiliser du globing. + +Pour plus d'informations, vous pouvez consulter la documentation accessible à +. + + +## N'installez rien de superflu + +Afin de réduire la quantité de dépendances à installer, n'installez pas de +paquets dont vous n'avez pas vraiment l'utilité : il n'y a pas de raison par +exemple d'avoir un éditeur de texte dans un environnement qui sera utilisé +comme serveur web. Un autre conteneur pourra contenir cet éditeur de texte dans +les cas où vous avez besoin de modifier des données. + +En plus, cela réduira le temps de build et la taille des images produites ! + + +## Minimisez le nombre de couches + +Vous devez trouver l'équilibre idéal entre la lisibilité de votre `Dockerfile` +(qui assure la maintenabilité sur le long-terme) et le nombre de couches +créées. Le nombre de couches idéal devrait être égal au nombre de branches +distincte partant d'une image de base, afin d'utiliser au mieux le cache du +système de fichiers. + + +## Ordonnez vos lignes de commandes complexes + +### Allez à la ligne pour séparer les longues lignes de commandes complexes + +Aérez vos `Dockerfile` ! + +N'hésitez pas à commenter et séparer les blocs logiques ensemble, comme lorsque +vous codez. + +Lorsqu'une ligne devient complexe, allez à la ligne : + +``` +RUN apt-get update && apt-get install -y \ + nginx \ + php5-fpm +``` + +Notez les backslashs à la fin des lignes, indiquant qu'elle n'est pas terminée. + +### Triez les arguments par ordre alphabétique + +Lorsque c'est possible, ordonnez vos lignes suivant un ordre logique. Par +exemple : + +``` +RUN apt-get update && apt-get install -y \ + bzr \ + cvs \ + git \ + mercurial \ + subversion +``` + + +## Profitez du système de cache + +Le processus de construction de votre image Docker va lire les informations de +votre Dockerfile dans l'ordre. Pour chaque instruction, Docker va essayer de +trouver si une image n'est pas déjà disponible dans le cache (plutôt que de +créer une nouvelle image identique). + +Il y a un certain nombre de règles à connaître pour bien utiliser ce mécanisme : + +- En démarrant d'une image de base déjà présente dans le cache (`docker + images`), l'instruction suivante est comparée avec toutes les autres images + existantes qui en dérivent directement. Si aucune image correspondant n'est + trouvé pour l'instruction, le cache est invalidé pour le reste de cette + construction. +- Dans la plupart des cas, Docker va simplement comparer l'instruction lue avec + le(s) différente(s) image(s) qui dérive(nt) de la commande précédente. Si + aucune commande correspondante n'est trouvé, le cache se retrouve invalidé + pour les instructions suivantes. +- Pour les instructions `ADD` et `COPY`, en plus de la comparaison précédente, + la somme de contrôle du fichier est ajoutée. Si le fichier a été modifié, le + cache se retrouve invalidé. +- Une fois que le cache est invalidé, toutes les commandes restantes à exécuter + dans le `Dockerfile` vont être exécutées. + + +## Concevez des conteneur éphémères + +Les conteneurs que vous générez doivent aussi éphémères que possible : ils +devraient pouvoir être arrêtés, détruits et recréés sans nécessité d'étape de +reconfiguration. La configuration devrait se faire au lancement du conteneur ou +lors de sa construction. + + +## Cas d'`apt-get` et des gestionnaires de paquets + +- N'exécutez pas `apt-get update` seul sur une ligne. Cela risque de poser des + problèmes de cache, car la ligne ne va jamais changer et le cache sera + toujours utilisé. Vous risquez de récupérer des paquets qui ne sont pas à + jour. +- Évitez de mettre à jour le système fourni (via `apt-get upgrade` ou `apt-get + update`). Si l'image n'est pas à jour, contactez son mainteneur. Si vous avez + besoin d'une version à jour d'un paquet distribué avec l'image, préférez + l'utilisation d'`apt-get install -y foo` qui mettra à jour exclusivement le + paquet `foo`, sans altérer le reste du système. +- Pour assurer une bonne gestion du cache, n'hésitez pas à indiquer les + versions des programmes que vous voulez installer sur votre ligne de commande + `apt-get`. + + +## Exposez les ports standards + +La commande `EXPOSE` vous permet d'indiquer les ports sur lesquels votre +conteneur s'attend à recevoir des paquets venant de l'extérieur. Ces ports ne +sont pas partagés avec l'hôte ou les autres conteneur, donc vous n'avez pas de +raison de ne pas utiliser les ports standards. + +Si vous faites cela, il y a de forte chance qu'il n'y ait pas besoin de +modifier la configuration des autres logiciels contenu dans d'autres conteneurs +puis qu'ils sont généralement configurés pour se connecter aux ports standards. + +S'il y a un conflit sur la machine hôte, il sera toujours temps de créer une +redirection à ce moment là. + + +## La bonne utilisation de l'`ENTRYPOINT` + +L'entrypoint peut être utilisé de deux manières différentes : + +- Vous pouvez l'utiliser de telle sorte que la commande passée au `docker run`, + après le nom de l'image, corresponde aux arguments attendu par le programme + indiqué dans l'entrypoint. Par exemple pour nginx : + + ``` + ENTRYPOINT ["nginx"] + CMD ["-g daemon off;"] + ``` + +- Vous pouvez aussi utiliser un script qui servira à faire les initialisations + ou les configurations nécessaire au bon fonctionnement du conteneur + (rappelez-vous, il doit être éphémère !). Par exemple, le `Dockerfile` pour + l'image de PostgreSQL possède cet entrypoint : + + ``` + #!/bin/bash + set -e + + if [ "$1" = 'postgres' ]; then + chown -R postgres "$PGDATA" + + if [ -z "$(ls -A "$PGDATA")" ]; then + gosu postgres initdb + fi + + exec gosu postgres "$@" + fi + + exec "$@" + ``` + + +## `[""]`, `'` et sans `[]` + +Les instructions `ENTRYPOINT` et `CMD` peuvent prendre deux formes : + +- `["cmd", "arg1", "arg2"]` : ici, un simple `exexve` sera effectué avec ces + arguments. Si d'éventuels variables se trouve dans les arguments, elles ne + seront pas remplacées. +- `cmd arg1 arg2` : ici l'exécution se fera au sein d'un `sh -c`, donc les + variables seront remplacés et étendues. + +Les commandes sous forme de tableau étant parsées par un parser JSON, vous ne +pouvez pas utiliser les simple quotes. + + +## Volumes + +L'instruction `VOLUME` doit être utilisée pour exposer tous les espaces de +stockage + + +## Réduisez les privilèges + +Utilisez l'instruction `USER` dès que vous le pouvez, lorsqu'un service ne +réclame pas de privilège particulier. + +Il vous faudra sans doute créer l'utilisateur et son groupe dans le Dockerfile. + + +## Exécutez un seul processus par conteneur + +Dans la majorité des cas, vous ne devriez jamais lancer plus d'un seul +processus par conteneur. Il est préférable de répartir chaque application dans +un conteneur distinct qui n'effectue que le travail pour lequel il est +chargé. Les options de liaison entre conteneur sont à votre disposition pour +vous aider à cette tâche. diff --git a/tutorial/3/installation.md b/tutorial/3/installation.md new file mode 100644 index 0000000..9656375 --- /dev/null +++ b/tutorial/3/installation.md @@ -0,0 +1,31 @@ +\newpage + +# Installation + +## `docker-compose` + +L'équipe en charge de Docker compose met à disposition un binaire contenant +tous les scripts. Nous pouvons l'installer en suivant la procédure suivante : + +``` +curl -L https://github.com/docker/compose/releases/download/1.3.3/docker-compose-Linux-x86_64 > /usr/bin/docker-compose +chmod +x /usr/bin/docker-compose +``` + +Le projet étant écrit en Python, il est également disponible via `pip`, si vous +préférez cette méthode. N'oubliez pas de préciser une version compatible avec +votre version de Docker. + + +### Vérification du fonctionnement + +Comme avec Docker, nous pouvons vérifier le bon fonctionnement de +`docker-compose` en exécutant la commande : + +``` +42sh$ docker-compose --version +docker-compose version: 1.3.3 +``` + +Si vous obtenez une réponse similaire, c'est que vous êtes prêt à commencer le +TP ! Alors n'attendons pas, partons à l'aventure ! diff --git a/tutorial/3/project.md b/tutorial/3/project.md new file mode 100644 index 0000000..acce53b --- /dev/null +++ b/tutorial/3/project.md @@ -0,0 +1,63 @@ +\newpage + +# Rendu + +## TP + +Rendez le contenu de votre dossier à la dernière étape du TP : avec le +`docker-compose.yml`, ainsi que vos `Dockerfile` et les éventuels fichiers +annexes. + + +## Projet + +De la même manière que nous avons réaliser un groupe de conteneurs utilisant +`grafana` et `InfluxDB`, qui permet d'afficher facilement des métriques sous +forme de graphiques, vous allez réaliser, à l'aide des images Docker +présentent sur le hub, une interface web de recherche et de visualisation de +logs, utilisant +[Kibana](https://www.digitalocean.com/community/tutorials/how-to-install-elasticsearch-logstash-and-kibana-4-on-ubuntu-14-04). + +Toutes la chaîne d'image Docker est déjà présente sur le hub : +[logstash](https://hub.docker.com/_/logstash/), +[elasticsearch](https://hub.docker.com/_/kibana/), +[kibana](https://hub.docker.com/_/kibana/). + +Le but du projet est donc de réaliser un `docker-compose.yml` permettant +d'avoir un système de centralisation de logs fonctionnels. Vous aurez sans +doute à faire quelques adaptations au niveau des images Docker, au moins pour +des fichiers de configuration, donc il n'y aura sans doute pas que ce fichier à +rendre. + +Vous pouvez utiliser comme source de logs les conteneurs du TP, grâce aux +options `log-driver=gelf` et `log-opt=gelf-address=udp://host:port`, passées +aux `docker run` (ou dans le `docker-compose`). + +Côté `logstash`, votre configuration devrait ressembler à ça : + +``` +input { + tcp { + port => 4242 + } + udp { + port => 4242 + } +} + +output { + elasticsearch { } +} +``` + +Vous pourrez ainsi envoyez les logs de Docker sur le port 4242. Ou directement +vos logs syslog : + +``` +netcat localhost 4242 < /var/log/syslog +``` + +N'oubliez pas de lire les README associés aux images Docker du hub, ils vous +indiqueront comment utiliser les images et comment leur passer des paramètres. + +Bon courage ! diff --git a/tutorial/3/supervisor.md b/tutorial/3/supervisor.md new file mode 100644 index 0000000..7dbbb30 --- /dev/null +++ b/tutorial/3/supervisor.md @@ -0,0 +1,60 @@ +\newpage + +# Plusieurs daemons dans un conteneur + +## Script d'init + +Lors du dernier TP, nous avons vu que les conteneurs étaient détruits +dès que le premier processus du conteneur (celui qui a le PID 1, à la +place d'`init`) terminer son exécution, quelque soit le statut de ses +éventuels fils. + +Pour lancer tous nos daemon, + + +## 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` : + +``` +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 `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 +`grafana` et `influxdb`. + +Vous pouvez vous aider de la documentation disponible à : + + + +## C'est parti ! + +Votre conteneur doit maintenant être parfaitement fonctionnel : vous +devriez pouvoir lancer votre script de monitoring et voir apparaître +vos données dans Grafana ! diff --git a/tutorial/3/tutorial.md b/tutorial/3/tutorial.md new file mode 100644 index 0000000..7503130 --- /dev/null +++ b/tutorial/3/tutorial.md @@ -0,0 +1,20 @@ +% Virtualisation légère -- TP n^o^3 +% Pierre-Olivier *Nemunaire* Mercier +% Jeudi 29 octobre 2015 + +Durant ce troisième TP, nous allons approfondir l'utilisation de Docker ! + +Tous les éléments de ce TP (exercices et questions) sont à rendre à + au plus tard le jeudi 12 novembre 2015 à 23 h 42. Consultez la +dernière section de chaque partie pour plus d'information sur les éléments à +rendre. Vous pouvez placer les réponses aux questions dans le corps du courriel +ou dans un fichier joint. + +En tant que personnes sensibilisées à la sécurité des échanges électroniques, +vous devriez m'envoyer vos rendus signés avec votre clef PGP. Pensez à +[me](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x842807A84573CC96) faire +signer votre clef et n'hésitez pas à +[faire signer votre clef](http://www.meetup.com/fr/Paris-certification-de-cles-PGP-et-CAcert/). + +\hypersetup{linkcolor=black} +\tableofcontents diff --git a/tutorial/3/what.md b/tutorial/3/what.md new file mode 100644 index 0000000..eb201f6 --- /dev/null +++ b/tutorial/3/what.md @@ -0,0 +1,18 @@ +\newpage + +# But du TP + +Aujourd'hui, nous allons terminer notre système de monitoring commencé lors du +premier TP. + +Le résultat attendu d'ici la fin du TP, est un groupe de conteneurs +indépendants les uns des autres, réutilisables en fonction des besoins. + +TODO image de graphana + +Nous reprendrons le script de monitoring que vous avez rendu au premier TP. Les +données collectées seront envoyés vers [https://influxdb.com/](InfluxDB), puis +elles seront affichées sous forme de graphique dans +[http://grafana.org/](Grafana). L'interface sera servie par un reverse-proxy +qui vous permettra de n'ouvrir que le port 80 ou 443, pour accéder à +l'interface d'administration d'InfluxDB et à l'interface de Grafana.