Split Docker tutorials into basis, orchestration and dockerfiles
This commit is contained in:
parent
2d364556a2
commit
2c48fa7942
36 changed files with 477 additions and 188 deletions
22
tutorial/dockerfiles/Makefile
Normal file
22
tutorial/dockerfiles/Makefile
Normal file
|
@ -0,0 +1,22 @@
|
|||
SOURCES = tutorial.md interactive.md dockerfile.md first.md supervisor.md goodpractices.md split.md entrypoint.md multistaged.md
|
||||
PANDOCOPTS = --latex-engine=xelatex \
|
||||
--standalone \
|
||||
--normalize \
|
||||
--number-sections \
|
||||
--smart \
|
||||
-M lang=french \
|
||||
-M fontsize=12pt \
|
||||
-M papersize=a4paper \
|
||||
-M mainfont="Linux Libertine O" \
|
||||
-M monofont="FantasqueSansMono-Regular" \
|
||||
-M sansfont="Linux Biolinum O" \
|
||||
--include-in-header=../header.tex
|
||||
|
||||
|
||||
all: tutorial.pdf
|
||||
|
||||
tutorial.pdf: ${SOURCES}
|
||||
pandoc ${PANDOCOPTS} -o $@ $+
|
||||
|
||||
clean::
|
||||
rm tutorial.pdf
|
BIN
tutorial/dockerfiles/chronograf_setup.png
Normal file
BIN
tutorial/dockerfiles/chronograf_setup.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
142
tutorial/dockerfiles/dockerfile.md
Normal file
142
tutorial/dockerfiles/dockerfile.md
Normal file
|
@ -0,0 +1,142 @@
|
|||
\newpage
|
||||
|
||||
`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
|
||||
que l'on a réussi à faire précédemment en utilisant le `Dockerfile` suivant :
|
||||
|
||||
```
|
||||
FROM ubuntu:latest
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y nano
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
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` :
|
||||
|
||||
```
|
||||
docker image build --tag=my_editor .
|
||||
```
|
||||
|
||||
Une fois la construction de l'image terminée, vous pouvez la lancer et
|
||||
constater l'existence de notre éditeur favori :
|
||||
|
||||
```
|
||||
docker container run -it my_editor /bin/bash
|
||||
```
|
||||
|
||||
|
||||
## `RUN` dans le `Dockerfile`
|
||||
|
||||
Dans un `Dockerfile`, chaque ligne est exécutée indépendamment des
|
||||
autres et correspondra à une nouvelle couche de notre image.
|
||||
|
||||
Cela signifie que l'exemple suivant **ne fonctionne pas** :
|
||||
|
||||
```
|
||||
COPY db.sql /db.sql
|
||||
RUN service mysqld start
|
||||
RUN mysql -u root -p toor virli < /db.sql
|
||||
```
|
||||
|
||||
Cet exemple ne fonctionne pas car le serveur MySQL 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.
|
||||
|
||||
Pour avoir le résultat escompté, il faut exécuter les commandes ensemble :
|
||||
|
||||
```
|
||||
COPY db.sql /db.sql
|
||||
RUN service mysqld start && mysql -u root -p toor virli < /db.sql
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
|
||||
## Exposer des ports
|
||||
|
||||
Construisons maintenant un conteneur avec un serveur web :
|
||||
|
||||
```
|
||||
FROM my_editor
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y nginx
|
||||
|
||||
EXPOSE 80
|
||||
```
|
||||
|
||||
L'instruction `EXPOSE` sera traité plus tard par le client Docker
|
||||
(équivalent à l'argument `--expose`). Il s'agit de préciser les ports
|
||||
sur lesquels votre image écoute.
|
||||
|
||||
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 :
|
||||
|
||||
```
|
||||
docker image build --tag=my_webserver .
|
||||
docker container run -it -P my_webserver /bin/bash
|
||||
service nginx start
|
||||
```
|
||||
|
||||
Dans un autre terminal, lancer un `docker ps` et consulter la colonne
|
||||
*PORTS* pour connaître le port choisit 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.
|
||||
|
||||
|
||||
## Lancement de commande automatique
|
||||
|
||||
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 :
|
||||
|
||||
```
|
||||
CMD nginx -g "daemon off;"
|
||||
```
|
||||
|
||||
```
|
||||
docker image build --tag=my_nginx .
|
||||
docker container run -d -P my_nginx
|
||||
```
|
||||
|
||||
L'option `-d` passée au `run` lance le conteneur en tâche de
|
||||
fond. Si vous constatez via un `docker ps` que le conteneur s'arrête
|
||||
directement, retirer cette option pour voir ce qui ne va pas, ou
|
||||
utilisez la commande `docker logs`.
|
||||
|
||||
|
||||
## D'autres instructions ?
|
||||
|
||||
Consultez <https://docs.docker.com/engine/reference/builder/> pour la liste complète
|
||||
des instructions reconnues.
|
||||
|
||||
|
||||
## Rendu
|
||||
|
||||
Rendez le fichier `Dockerfile` et son contexte (`index.html`, fichiers de conf
|
||||
éventuels, ...) que vous avez utilisé pour réaliser votre image
|
||||
`my_webserver`.
|
||||
|
||||
Une attention particulière sera apporté au respect des différentes bonnes
|
||||
pratiques vues en cours pour l'écriture de `Dockerfile`.
|
19
tutorial/dockerfiles/entrypoint.md
Normal file
19
tutorial/dockerfiles/entrypoint.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
\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`.
|
137
tutorial/dockerfiles/first.md
Normal file
137
tutorial/dockerfiles/first.md
Normal file
|
@ -0,0 +1,137 @@
|
|||
\newpage
|
||||
|
||||
Premières étapes
|
||||
================
|
||||
|
||||
Dans un premier temps, nous allons créer une image Docker comme si l'on
|
||||
réalisait une installation sur une machine classique : en suivant une recette,
|
||||
sans trop se préoccuper des fonctionnalités que propose Docker.
|
||||
|
||||
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 ?
|
||||
|
||||
### InfluxDB
|
||||
|
||||
Ensuite vient la suite d'instructions pour installer d'InfluxDB. Le paquet
|
||||
n'est pas disponible dans les dépôts[^debrepos]. La
|
||||
[procédure décrite du site](https://portal.influxdata.com/downloads) incite à
|
||||
télécharger le paquet mis à disposition puis à l'installer via `dpkg -i`.
|
||||
|
||||
[^debrepos]: Le projet met à disposition des dépôts, si vous préférez cette
|
||||
méthode, consultez la
|
||||
[documentation d'installation](https://docs.influxdata.com/influxdb/v1.3/introduction/installation/#ubuntu-debian).
|
||||
|
||||
Deux solutions s'offrent à nous :
|
||||
|
||||
* télécharger le paquet hors du conteneur, le copier, puis l'installer.
|
||||
* faire un `RUN` avec toutes ces opérations (sans oublier l'installation
|
||||
préalable de `wget`/`curl`).
|
||||
|
||||
La copie étant définitive (supprimer le fichier ne le supprimera pas des
|
||||
couches où il a pu exister), on préférera la seconde méthode, malgré que `wget`
|
||||
restera en déchet. La première méthode aura plus sa place dans un dépôt de
|
||||
projet où les binaires auront été préalablement compilés, il ne restera plus
|
||||
qu'à les copier dans le conteneur au bon emplacement.
|
||||
|
||||
Écrivons 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 (`EXPOSE`, `CMD`, ...) et
|
||||
[tester](http://localhost:8083) qu'InfluxDB est bien utilisable.
|
||||
|
||||
Il est possible que vous ayez à écraser le fichier de configuration via un
|
||||
`COPY` (ou de manière plus maligne en utilisant `--volume` au moment du `docker
|
||||
run`, cela ne fonctionne pas qu'avec les dossiers !). Ou peut-être ferez-vous
|
||||
un `ENTRYPOINT` ?
|
||||
|
||||
|
||||
### `telegraf`
|
||||
|
||||
`telegraf` est un programme qui permet de collecter des métriques systèmes. Il
|
||||
travaille de paire avec InfluxDB, qu'il utilise pour stocker les valeurs
|
||||
relevées.
|
||||
|
||||
Vous pouvez monitorer les métriques de n'importe quelle machine, simplement en
|
||||
installant `telegraf` et en lui indiquant l'emplacement de son serveur
|
||||
InfluxDB. Nous allons installer `telegraf` sur notre machine à l'aide de la
|
||||
[documentation](https://docs.influxdata.com/telegraf/v1.0/introduction/installation/).
|
||||
|
||||
Ces quelques lignes devraient suffir à lancer la collecte, à condition que
|
||||
votre InfluxDB écoute sur le port 8086 local :
|
||||
|
||||
```bash
|
||||
TELEGRAF_VERSION=1.0.0
|
||||
wget https://dl.influxdata.com/telegraf/releases/telegraf-${TELEGRAF_VERSION}_linux_amd64.tar.gz
|
||||
tar xf telegraf-${TELEGRAF_VERSION}_linux_amd64.tar.gz
|
||||
TELEGRAF_CONFIG_PATH=./telegraf/etc/telegraf/telegraf.conf ./telegraf/usr/bin/telegraf
|
||||
```
|
||||
|
||||
Rendez-vous ensuite dans [l'interface d'InfluxDB](http://localhost:8083/) pour
|
||||
voir si la collecte se passe bien.
|
||||
|
||||
Dans l'interface sélectionnez la base `telegraf` puis explorez les valeurs :
|
||||
|
||||
```sql
|
||||
SHOW MEASUREMENTS
|
||||
SHOW FIELD KEYS
|
||||
SELECT usage_idle FROM cpu WHERE cpu = 'cpu-total' ORDER BY time DESC LIMIT 5
|
||||
```
|
||||
|
||||
Laissons tourner `telegraf` afin de constituer un petit historique de valeurs.
|
||||
|
||||
|
||||
## Rendu
|
||||
|
||||
### Questions
|
||||
|
||||
1. Dans quel langage est écrit `telegraf` ?
|
||||
|
||||
1. Quelle(s) particularité(s) de ce langage permet de se passer de la variable
|
||||
`LD_LIBRARY_PATH` au lancement de `telegraf`, alors qu'on ne l'a pas
|
||||
installé ?
|
||||
|
||||
### Éléments à rendre
|
||||
|
||||
Avant de passer à la suite, placez votre `Dockerfile` et les éventuels fichiers
|
||||
annexes dans un dossier `influxdb`.
|
226
tutorial/dockerfiles/goodpractices.md
Normal file
226
tutorial/dockerfiles/goodpractices.md
Normal file
|
@ -0,0 +1,226 @@
|
|||
\newpage
|
||||
|
||||
Retour sur les bonnes pratiques
|
||||
===============================
|
||||
|
||||
Pour chaque bonne pratique ci-dessous, vérifiez que vous la respectez
|
||||
bien, faites les modifications nécessaires.
|
||||
|
||||
## 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 à
|
||||
<http://docs.docker.com/reference/builder/#dockerignore-file>.
|
||||
|
||||
|
||||
## 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.
|
||||
|
||||
|
||||
## 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 être aussi éphémères que possible : ils
|
||||
devraient pouvoir être arrêtés, détruits et recréés sans nécessiter 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 :
|
||||
|
||||
```shell
|
||||
#!/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 de données, configuration, ...
|
||||
|
||||
|
||||
## 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.
|
||||
|
||||
|
||||
## Profitez du système de liaison et de résolution de nom
|
||||
|
||||
Dès lors que vous effectuez un lien avec un autre conteneur, son nom (ou son
|
||||
alias) est ajouté au fichier `/etc/hosts`. Cela signifie que lorsqu'un nom de
|
||||
domaine correspondant au nom du conteneur (ou son alias) sera recherché, l'IP
|
||||
sera bien celle du conteneur. Lorsque vous configurez un conteneur, utilisez de
|
||||
préférence un nom plutôt qu'une IP, qui changera à coup sûr.
|
||||
|
||||
Au moment du `docker run`, vous pouvez préciser d'autres noms d'ĥôtes
|
||||
particuliers en utilisant l'option `--add-host`.
|
||||
|
||||
|
||||
## 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.
|
59
tutorial/dockerfiles/interactive.md
Normal file
59
tutorial/dockerfiles/interactive.md
Normal file
|
@ -0,0 +1,59 @@
|
|||
\newpage
|
||||
|
||||
Modification interactive
|
||||
========================
|
||||
|
||||
Nous voilà maintenant dans le conteneur ! Il est assez épuré, il n'y a rien de
|
||||
superflu : même pas d'éditeur de texte : ni vim, ni emacs, même pas `vi` !
|
||||
|
||||
La première chose à faire est de télécharger la liste des paquets. En effet,
|
||||
afin de ne pas livrer de superflu, la liste des paquets et son cache ne sont
|
||||
pas inclues dans le conteneur.
|
||||
|
||||
```
|
||||
apt-get update
|
||||
```
|
||||
|
||||
Il peut arriver que des paquets présents dans l'image ne soient pas à
|
||||
jour. Afin de garder un environnement cohérent, il est recommandé de ne pas
|
||||
utiliser le gestionnaire de paquets pour mettre à jour les paquets présent de
|
||||
base, mais plutôt de contacter le mainteneur de l'image pour qu'il la mette à
|
||||
jour.
|
||||
|
||||
Installons maintenant un programme :
|
||||
|
||||
```
|
||||
apt-get install nano
|
||||
```
|
||||
|
||||
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 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` :
|
||||
|
||||
```
|
||||
docker container commit CONTAINER my_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` :
|
||||
|
||||
```
|
||||
docker container run -it my_nano /bin/bash
|
||||
```
|
||||
|
||||
Vous constatez cette fois que vous pouvez lancer `nano`, alors que vous ne
|
||||
pouvez toujours pas le faire dans un conteneur issue d'une image `ubuntu` !
|
6
tutorial/dockerfiles/multistaged.md
Normal file
6
tutorial/dockerfiles/multistaged.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
\newpage
|
||||
|
||||
Multi-stage build
|
||||
=================
|
||||
|
||||
TODO
|
54
tutorial/dockerfiles/split.md
Normal file
54
tutorial/dockerfiles/split.md
Normal file
|
@ -0,0 +1,54 @@
|
|||
\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
|
||||
```
|
103
tutorial/dockerfiles/supervisor.md
Normal file
103
tutorial/dockerfiles/supervisor.md
Normal file
|
@ -0,0 +1,103 @@
|
|||
\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.0/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 : <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`) :
|
||||
|
||||

|
||||
|
||||
|
||||
## 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 !
|
||||
|
||||
|
||||
## `HEALTHCHECK`
|
||||
|
||||
|
||||
## `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`.
|
23
tutorial/dockerfiles/tutorial.md
Normal file
23
tutorial/dockerfiles/tutorial.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
title: Virtualisation légère -- TP n^o^ 3
|
||||
subtitle: Construire des images Docker
|
||||
author: Pierre-Olivier *Nemunaire* Mercier
|
||||
institute: EPITA
|
||||
date: Jeudi 11 octobre 2018
|
||||
...
|
||||
|
||||
Durant ce troisième TP, nous allons voir comment créer nos propres images !
|
||||
|
||||
Tous les éléments de ce TP (exercices et projet) sont à rendre à
|
||||
<virli@nemunai.re> au plus tard le jeudi 18 octobre 2018 à 8 h 42. Consultez la
|
||||
dernière section de chaque partie pour plus d'information sur les éléments à
|
||||
rendre.
|
||||
|
||||
En tant que personnes sensibilisées à la sécurité des échanges électroniques,
|
||||
vous devrez 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
|
Loading…
Add table
Add a link
Reference in a new issue