Dockerfile.1
exécute plusieurs RUN
:
FROM busybox
RUN echo This is the A > a
RUN echo This is the B > b
RUN echo This is the C > c
Dockerfile.2
les rejoint:
FROM busybox
RUN echo This is the A > a &&\
echo This is the B > b &&\
echo This is the C > c
Chacun RUN
crée un calque, donc j'ai toujours supposé que moins de calques était mieux et donc Dockerfile.2
mieux.
Cela est évidemment vrai lorsqu'un RUN
supprime quelque chose d'ajouté par un précédent RUN
(c'est-à-dire yum install nano && yum clean all
), mais dans les cas où tout RUN
ajoute quelque chose, il y a quelques points à considérer:
Les couches sont censées simplement ajouter une différence au-dessus de la précédente, donc si la dernière couche ne supprime pas quelque chose d'ajouté dans une précédente, il ne devrait pas y avoir beaucoup d'avantage d'économie d'espace disque entre les deux méthodes ...
Les couches sont extraites en parallèle de Docker Hub, donc
Dockerfile.1
, bien que probablement légèrement plus grandes, elles seraient théoriquement téléchargées plus rapidement.Si l'ajout d'une 4ème phrase (ie
echo This is the D > d
) et la reconstruction locale,Dockerfile.1
construirait plus rapidement grâce au cache, maisDockerfile.2
devrait exécuter à nouveau les 4 commandes.
Alors, la question: quelle est la meilleure façon de faire un Dockerfile?
la source
Réponses:
Lorsque cela est possible, je fusionne toujours les commandes qui créent des fichiers avec des commandes qui suppriment ces mêmes fichiers en une seule
RUN
ligne. En effet, chaqueRUN
ligne ajoute un calque à l'image, la sortie est littéralement les modifications du système de fichiers que vous pouvez afficherdocker diff
sur le conteneur temporaire qu'il crée. Si vous supprimez un fichier qui a été créé dans une couche différente, tout ce que le système de fichiers d'union fait est d'enregistrer le changement de système de fichiers dans une nouvelle couche, le fichier existe toujours dans la couche précédente et est expédié sur le réseau et stocké sur le disque. Donc, si vous téléchargez le code source, extrayez-le, compilez-le dans un binaire, puis supprimez les fichiers tgz et source à la fin, vous voulez vraiment que tout cela soit fait en une seule couche pour réduire la taille de l'image.Ensuite, je divise personnellement les couches en fonction de leur potentiel de réutilisation dans d'autres images et de l'utilisation prévue de la mise en cache. Si j'ai 4 images, toutes avec la même image de base (par exemple debian), je peux extraire une collection d'utilitaires communs à la plupart de ces images dans la première commande d'exécution afin que les autres images bénéficient de la mise en cache.
L'ordre dans le Dockerfile est important lors de l'examen de la réutilisation du cache d'images. Je regarde tous les composants qui se mettront à jour très rarement, peut-être uniquement lorsque l'image de base se met à jour et les met en haut dans le Dockerfile. Vers la fin du Dockerfile, j'inclus toutes les commandes qui s'exécuteront rapidement et peuvent changer fréquemment, par exemple en ajoutant un utilisateur avec un UID spécifique à l'hôte ou en créant des dossiers et en modifiant les autorisations. Si le conteneur comprend du code interprété (par exemple JavaScript) en cours de développement actif, celui-ci est ajouté le plus tard possible afin qu'une reconstruction n'exécute que cette seule modification.
Dans chacun de ces groupes de changements, je consolide du mieux que je peux pour minimiser les couches. Donc, s'il y a 4 dossiers de code source différents, ceux-ci sont placés dans un seul dossier afin qu'il puisse être ajouté avec une seule commande. Toutes les installations de paquet à partir de quelque chose comme apt-get sont fusionnées en un seul RUN lorsque cela est possible pour minimiser la charge du gestionnaire de paquets (mise à jour et nettoyage).
Mise à jour pour les versions en plusieurs étapes:
Je m'inquiète beaucoup moins de la réduction de la taille de l'image dans les étapes non finales d'une construction en plusieurs étapes. Lorsque ces étapes ne sont pas balisées et expédiées à d'autres nœuds, vous pouvez maximiser la probabilité d'une réutilisation du cache en fractionnant chaque commande sur une
RUN
ligne distincte .Cependant, ce n'est pas une solution parfaite pour écraser les couches car tout ce que vous copiez entre les étapes sont les fichiers, et non le reste des métadonnées d'image telles que les paramètres de variable d'environnement, le point d'entrée et la commande. Et lorsque vous installez des packages dans une distribution Linux, les bibliothèques et autres dépendances peuvent être dispersées dans le système de fichiers, ce qui rend difficile une copie de toutes les dépendances.
Pour cette raison, j'utilise des versions en plusieurs étapes en remplacement de la création de binaires sur un serveur CI / CD, de sorte que mon serveur CI / CD n'a besoin que de l'outillage pour fonctionner
docker build
, et non d'un jdk, nodejs, go, et tout autre outil de compilation installé.la source
Réponse officielle répertoriée dans leurs meilleures pratiques (les images officielles DOIVENT y adhérer)
Depuis le docker 1.10
COPY
, les instructions ,ADD
etRUN
ajoutent un nouveau calque à votre image. Soyez prudent lorsque vous utilisez ces déclarations. Essayez de combiner les commandes en une seuleRUN
instruction. Séparez-le uniquement si cela est nécessaire pour la lisibilité.Plus d'infos: https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#/minimize-the-number-of-layers
Mise à jour: Multi stage dans le docker> 17.05
Avec les versions en plusieurs étapes, vous pouvez utiliser plusieurs
FROM
instructions dans votre Dockerfile. ChaqueFROM
instruction est une étape et peut avoir sa propre image de base. Dans la dernière étape, vous utilisez une image de base minimale comme alpine, copiez les artefacts de construction des étapes précédentes et installez les exigences d'exécution. Le résultat final de cette étape est votre image. C'est donc là que vous vous souciez des couches comme décrit précédemment.Comme d'habitude, docker a d' excellents documents sur les versions en plusieurs étapes. Voici un extrait rapide:
Un excellent article de blog à ce sujet peut être trouvé ici: https://blog.alexellis.io/mutli-stage-docker-builds/
Pour répondre à vos points:
Oui, les calques sont un peu comme des diffs. Je ne pense pas qu'il y ait des couches ajoutées s'il n'y a absolument aucun changement. Le problème est qu'une fois que vous avez installé / téléchargé quelque chose dans la couche # 2, vous ne pouvez pas le supprimer dans la couche # 3. Ainsi, une fois que quelque chose est écrit dans un calque, la taille de l'image ne peut plus être diminuée en supprimant cela.
Bien que les couches puissent être tirées en parallèle, ce qui le rend potentiellement plus rapide, chaque couche augmente sans aucun doute la taille de l'image, même si elles suppriment des fichiers.
Oui, la mise en cache est utile si vous mettez à jour votre fichier docker. Mais cela fonctionne dans un sens. Si vous avez 10 calques et que vous changez le calque n ° 6, vous devrez toujours tout reconstruire à partir du calque n ° 6 - n ° 10. Ce n'est donc pas trop souvent que cela accélérera le processus de création, mais il est garanti d'augmenter inutilement la taille de votre image.
Merci à @Mohan de m'avoir rappelé de mettre à jour cette réponse.
la source
Il semble que les réponses ci-dessus soient dépassées. La documentation note:
https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#minimize-the-number-of-layers
et
https://docs.docker.com/engine/userguide/eng-image/multistage-build/
Les meilleures pratiques semblent avoir changé pour utiliser des builds à plusieurs niveaux et garder les
Dockerfile
s lisibles.la source
docker image build --squash
option sortira de l'expérimentation.squash
expérimentation. Il a de nombreux gadgets et n'a de sens qu'avant les constructions en plusieurs étapes. Avec les versions en plusieurs étapes, il vous suffit d'optimiser la dernière étape, ce qui est très facile.Cela dépend de ce que vous incluez dans vos calques d'image.
Le point clé est de partager autant de couches que possible:
Mauvais exemple:
Dockerfile.1
Dockerfile.2
Bon exemple:
Dockerfile.1
Dockerfile.2
Une autre suggestion est que la suppression n'est pas si utile que si elle se produit sur la même couche que l'action d'ajout / d'installation.
la source
RUN yum install big-package
cache?