250 lines
9.0 KiB
Markdown
250 lines
9.0 KiB
Markdown
\newpage
|
||
|
||
Les bonnes pratiques
|
||
--------------------
|
||
|
||
Pour chaque bonne pratique ci-dessous, vérifiez que vous la respectez
|
||
bien, faites les modifications nécessaire dans votre `Dockerfile`.
|
||
|
||
### 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).
|
||
|
||
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`.
|
||
|
||
Vous pouvez exclure les produits intermédiaires de compilation (`*.o`, ...) si
|
||
vous utilisez un langage compilé, excluez également les produits de compilation
|
||
si votre image construit le binaire. Dans le cas de NodeJS, vous allez sans
|
||
doute vouloir exclure le dossier `node_modules` et faire un `npm install` dans
|
||
votre `Dockerfile`. Cela permettra au passage de s'assurer que toutes les
|
||
dépendances ont bien été enregistrée.
|
||
|
||
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 à\
|
||
<https://docs.docker.com/engine/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 construction et la taille des images
|
||
produites !
|
||
|
||
Avec `apt` par exemple, vous pouvez ajouter l'option `--no-install-recommends`
|
||
lors vous installer un paquet qui vient avec de nombreuses recommandations
|
||
inutiles. C'est le cas par exemple de `ffmpeg` ou de `gstreamer`, qui viennent
|
||
tous deux avec de nombreux *codecs*, mais peut-être que vous savez exactement
|
||
de quels *codecs* vous avez besoin.
|
||
|
||
|
||
### 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.
|
||
|
||
Utilisez les constructions en plusieurs étapes pour n'en recopier que les
|
||
éléments utiles dans l'image finale. C'est le meilleur moyen de gagner de la
|
||
place.
|
||
|
||
|
||
### 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 :
|
||
|
||
<div lang="en-US">
|
||
```dockerfile
|
||
RUN apt-get update && apt-get install -y \
|
||
nginx \
|
||
php5-fpm
|
||
```
|
||
</div>
|
||
|
||
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 :
|
||
|
||
<div lang="en-US">
|
||
```dockerfile
|
||
RUN apt-get update && apt-get install -y \
|
||
bzr \
|
||
cvs \
|
||
git \
|
||
mercurial \
|
||
subversion
|
||
```
|
||
</div>
|
||
|
||
|
||
### 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ée 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`{.dockerfile} et `COPY`{.dockerfile}, 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.
|
||
- 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`. Lors d'un changement de version, vous changerez la ligne, le cache
|
||
ne sera donc pas utilisé.
|
||
|
||
|
||
### Exposez les ports standards
|
||
|
||
La commande `EXPOSE`{.dockerfile} 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 conteneurs, 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
|
||
puisqu'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* (on le verra plus en détail dans la partie suivante) 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 :
|
||
|
||
<div lang="en-US">
|
||
```dockerfile
|
||
ENTRYPOINT ["nginx"]
|
||
CMD ["-g", "daemon", "off;"]
|
||
```
|
||
</div>
|
||
|
||
- 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 :
|
||
|
||
<div lang="en-US">
|
||
```bash
|
||
#!/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 "$@"
|
||
```
|
||
</div>
|
||
|
||
|
||
### `[""]`, `'` et sans `[]`
|
||
|
||
Les instructions `ENTRYPOINT`{.dockerfile} et `CMD`{.dockerfile} peuvent
|
||
prendre deux formes :
|
||
|
||
- `["cmd", "arg1", "arg2"]` : ici, un simple `execve` 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 *simples quotes*.
|
||
|
||
|
||
### Volumes
|
||
|
||
L'instruction `VOLUME`{.dockerfile} doit être utilisée pour exposer tous les
|
||
espaces de stockage de données, configuration, ...
|
||
|
||
|
||
### Réduisez les privilèges
|
||
|
||
Utilisez l'instruction `USER`{.dockerfile} 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.
|