virli/tutorial/dockerfiles/goodpractices.md

273 lines
9.9 KiB
Markdown
Raw Normal View History

2021-09-23 00:55:18 +00:00
Les bonnes pratiques
--------------------
2015-10-29 04:45:40 +00:00
2016-09-15 02:27:59 +00:00
Pour chaque bonne pratique ci-dessous, vérifiez que vous la respectez
bien, faites les modifications nécessaires dans votre `Dockerfile`.
2015-10-29 04:45:40 +00:00
2021-09-23 00:55:18 +00:00
### Utilisez le fichier `.dockerignore`
2015-10-29 04:45:40 +00:00
2016-09-15 02:27:59 +00:00
Dans la plupart des cas, vos `Dockerfile` seront dans des dossiers contenant
beaucoup de fichiers qui ne sont pas nécessaires à la construction de votre
2015-10-29 04:45:40 +00:00
conteneur (par exemple, vous pouvez avoir un `Dockerfile` placé à la racine
2021-09-24 15:12:07 +00:00
d'un dépôt git).
2015-10-29 04:45:40 +00:00
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`.
2021-09-24 15:12:07 +00:00
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ées.
2021-09-24 15:12:07 +00:00
Ce fichier fonctionne de la même manière que le `.gitignore` : vous pouvez
2015-10-29 04:45:40 +00:00
utiliser du globing.
2022-02-24 19:43:43 +00:00
Pour plus d'informations, vous pouvez consulter la documentation accessible à\
<https://docs.docker.com/engine/reference/builder/#dockerignore-file>
2015-10-29 04:45:40 +00:00
2021-09-23 00:55:18 +00:00
### N'installez rien de superflu
2015-10-29 04:45:40 +00:00
Afin de réduire la quantité de dépendances à installer, n'installez pas de
2021-09-24 15:12:07 +00:00
paquets dont vous n'avez pas vraiment l'utilité : il n'y a pas de raison par
2015-10-29 04:45:40 +00:00
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.
2021-09-23 00:55:18 +00:00
En plus, cela réduira le temps de construction et la taille des images
2022-02-24 19:43:43 +00:00
produites !
2015-10-29 04:45:40 +00:00
2021-09-23 00:55:18 +00:00
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.
2015-10-29 04:45:40 +00:00
2021-09-23 00:55:18 +00:00
### Minimisez le nombre de couches
2015-10-29 04:45:40 +00:00
Vous devez trouver l'équilibre idéal entre la lisibilité de votre `Dockerfile`
2021-09-23 00:55:18 +00:00
(qui assure la maintenabilité sur le long terme) et le nombre de couches
2016-09-15 00:46:46 +00:00
créées.
2015-10-29 04:45:40 +00:00
2021-09-23 00:55:18 +00:00
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.
2015-10-29 04:45:40 +00:00
2021-09-23 00:55:18 +00:00
### Ordonnez vos lignes de commandes complexes
2015-10-29 04:45:40 +00:00
2022-04-09 00:50:14 +00:00
#### Allez à la ligne pour séparer les longues lignes de commandes complexes
2015-10-29 04:45:40 +00:00
2022-02-24 19:43:43 +00:00
Aérez vos `Dockerfile` !
2015-10-29 04:45:40 +00:00
N'hésitez pas à commenter et séparer les blocs logiques ensemble, comme lorsque
vous codez.
2021-09-24 15:12:07 +00:00
Lorsqu'une ligne devient complexe, allez à la ligne :
2015-10-29 04:45:40 +00:00
2017-10-17 06:29:07 +00:00
<div lang="en-US">
```dockerfile
RUN apt-get update && apt-get install -y \
nginx \
php5-fpm
2015-10-29 04:45:40 +00:00
```
2017-10-17 06:29:07 +00:00
</div>
2015-10-29 04:45:40 +00:00
Notez les backslashs à la fin des lignes, indiquant qu'elle n'est pas terminée.
2022-04-09 00:50:14 +00:00
#### Triez les arguments par ordre alphabétique
2015-10-29 04:45:40 +00:00
Lorsque c'est possible, ordonnez vos lignes suivant un ordre logique. Par
2021-09-24 15:12:07 +00:00
exemple :
2015-10-29 04:45:40 +00:00
2017-10-17 06:29:07 +00:00
<div lang="en-US">
```dockerfile
RUN apt-get update && apt-get install -y \
bzr \
cvs \
git \
mercurial \
subversion
2015-10-29 04:45:40 +00:00
```
2017-10-17 06:29:07 +00:00
</div>
2015-10-29 04:45:40 +00:00
2021-09-23 00:55:18 +00:00
### Profitez du système de cache
2015-10-29 04:45:40 +00:00
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).
2021-09-24 15:12:07 +00:00
Il y a un certain nombre de règles à connaître pour bien utiliser ce mécanisme :
2015-10-29 04:45:40 +00:00
- 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
2021-09-23 00:55:18 +00:00
trouvée pour l'instruction, le cache est invalidé pour le reste de cette
2015-10-29 04:45:40 +00:00
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é.
2015-10-29 04:45:40 +00:00
- Une fois que le cache est invalidé, toutes les commandes restantes à exécuter
dans le `Dockerfile` vont être exécutées.
### Concevez des conteneurs éphémères
2015-10-29 04:45:40 +00:00
2021-09-24 15:12:07 +00:00
Les conteneurs que vous générez doivent être aussi éphémères que possible : ils
2016-09-14 20:51:14 +00:00
devraient pouvoir être arrêtés, détruits et recréés sans nécessiter d'étape de
2015-10-29 04:45:40 +00:00
reconfiguration. La configuration devrait se faire au lancement du conteneur ou
lors de sa construction.
2021-09-23 00:55:18 +00:00
### Cas d'`apt-get` et des gestionnaires de paquets
2015-10-29 04:45:40 +00:00
- 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
2021-09-23 00:55:18 +00:00
`apt-get`. Lors d'un changement de version, vous changerez la ligne, le cache
ne sera donc pas utilisé.
2015-10-29 04:45:40 +00:00
2021-09-23 00:55:18 +00:00
### Exposez les ports standards
2015-10-29 04:45:40 +00:00
2021-09-24 15:12:07 +00:00
La commande `EXPOSE`{.dockerfile} permet d'indiquer les ports sur lesquels
votre conteneur s'attend à recevoir des paquets venant de l'extérieur. Ces
2021-09-24 15:12:07 +00:00
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.
2015-10-29 04:45:40 +00:00
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
2021-09-24 15:12:07 +00:00
puisqu'ils sont généralement configurés pour se connecter aux ports standards.
2015-10-29 04:45:40 +00:00
S'il y a un conflit sur la machine hôte, il sera toujours temps de créer une
redirection à ce moment-là.
2015-10-29 04:45:40 +00:00
2021-09-23 00:55:18 +00:00
### La bonne utilisation de l'`ENTRYPOINT`
2015-10-29 04:45:40 +00:00
2021-09-24 15:12:07 +00:00
L'*entrypoint* (on le verra plus en détail dans la partie suivante) peut être
utilisé de deux manières différentes :
2015-10-29 04:45:40 +00:00
- 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` :
2015-10-29 04:45:40 +00:00
2017-10-17 06:29:07 +00:00
<div lang="en-US">
```dockerfile
ENTRYPOINT ["nginx"]
2020-09-14 13:46:13 +00:00
CMD ["-g", "daemon", "off;"]
2016-09-14 20:51:14 +00:00
```
2017-10-17 06:29:07 +00:00
</div>
2015-10-29 04:45:40 +00:00
- Vous pouvez aussi utiliser un script qui servira à faire les initialisations
ou les configurations nécessaire au bon fonctionnement du conteneur
2022-02-24 19:43:43 +00:00
(rappelez-vous, il doit être éphémère !). Par exemple, le `Dockerfile` pour
2021-09-24 15:12:07 +00:00
l'image de PostgreSQL possède cet entrypoint :
2015-10-29 04:45:40 +00:00
2017-10-17 06:29:07 +00:00
<div lang="en-US">
```bash
#!/bin/bash
set -e
2015-10-29 04:45:40 +00:00
if [ "$1" = 'postgres' ]; then
chown -R postgres "$PGDATA"
2015-10-29 04:45:40 +00:00
if [ -z "$(ls -A "$PGDATA")" ]; then
gosu postgres initdb
fi
2015-10-29 04:45:40 +00:00
exec gosu postgres "$@"
fi
2015-10-29 04:45:40 +00:00
exec "$@"
2016-09-14 20:51:14 +00:00
```
2017-10-17 06:29:07 +00:00
</div>
2015-10-29 04:45:40 +00:00
2021-09-23 00:55:18 +00:00
### `[""]`, `'` et sans `[]`
2015-10-29 04:45:40 +00:00
Les instructions `ENTRYPOINT`{.dockerfile} et `CMD`{.dockerfile} peuvent
2021-09-24 15:12:07 +00:00
prendre deux formes :
2015-10-29 04:45:40 +00:00
2021-09-24 15:12:07 +00:00
- `["cmd", "arg1", "arg2"]` : ici, un simple `execve` sera effectué avec ces
2015-10-29 04:45:40 +00:00
arguments. Si d'éventuels variables se trouve dans les arguments, elles ne
seront pas remplacées.
2021-09-24 15:12:07 +00:00
- `cmd arg1 arg2` : ici l'exécution se fera au sein d'un `sh -c`, donc les
variables seront remplacées et étendues.
2015-10-29 04:45:40 +00:00
Les commandes sous forme de tableau étant parsées par un parser JSON, vous ne
2021-09-24 15:12:07 +00:00
pouvez pas utiliser les *simples quotes*.
2015-10-29 04:45:40 +00:00
2021-09-23 00:55:18 +00:00
### Volumes
2015-10-29 04:45:40 +00:00
L'instruction `VOLUME`{.dockerfile} doit être utilisée pour exposer tous les
2021-09-24 15:12:07 +00:00
espaces de stockage de données, configuration, ...
2015-10-29 04:45:40 +00:00
2021-09-23 00:55:18 +00:00
### Réduisez les privilèges
2015-10-29 04:45:40 +00:00
Utilisez l'instruction `USER`{.dockerfile} dès que vous le pouvez, lorsqu'un
service ne réclame pas de privilège particulier.
2015-10-29 04:45:40 +00:00
2021-09-24 15:12:07 +00:00
Il vous faudra sans doute créer l'utilisateur et son groupe dans le `Dockerfile`.
2015-10-29 04:45:40 +00:00
2021-09-23 00:55:18 +00:00
### Profitez du système de liaison et de résolution de nom
2016-09-14 20:51:14 +00:00
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`.
2021-09-23 00:55:18 +00:00
### Exécutez un seul processus par conteneur
2015-10-29 04:45:40 +00:00
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.
2022-09-20 04:02:53 +00:00
## De l'intérêt de faire des images minimales
À l'inverse de langages comme Javascript, Python, Java et bien
d'autres, le langage Go compile, comme le C, vers du code directement
exécutable par le processeur. Tandis que les langages interprétés ont
besoin de leur interpréteur et de leur environnement d'exécution, les
langages compilés n'ont pas besoin d'être distribués avec leur
compilateur.
Prenons le temps de regarder les tailles des images :
<div lang="en-US">
```
42sh$ docker image ls -f reference=golang -f reference=youp0m
REPOSITORY TAG IMAGE ID CREATED SIZE
golang 1-alpine 155ead2e66ca 3 months ago 328MB
nemunaire/youp0m latest 2c06880e48aa 2 days ago 25MB
```
</div>
L'image contenant le compilateur Go est bien plus lourde que l'image
minimale que l'on a construite avec le binaire compilé. C'est autant
d'espace et de performances gagnées.