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, chaque RUN
ligne ajoute un calque à l'image, la sortie est littéralement les modifications du système de fichiers que vous pouvez afficher docker 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é.