Docker Image Reduction Techniques

Vous êtes-vous déjà demandé pourquoi la taille d'un conteneur Docker contenant une seule application peut être d'environ 400 Mo? Ou peut-être étiez-vous préoccupé par la taille assez grande de l'image Docker contenant un seul binaire de plusieurs dizaines de Mo?



L'auteur de l'article, dont nous publions aujourd'hui la traduction, souhaite analyser les principaux facteurs affectant la taille des conteneurs Docker. Il va, en outre, partager des recommandations sur la réduction de la taille des conteneurs.

Couches d'images Docker


Une image d'un conteneur Docker est essentiellement une collection de fichiers empilés les uns sur les autres en plusieurs couches. Un conteneur de travail est assemblé à partir de ces fichiers. Docker utilise le système de fichiers UnionFS , dans lequel les fichiers sont regroupés en couches. Un calque peut contenir un ou plusieurs fichiers, les calques se chevauchent. Lors de l'exécution du conteneur, le contenu des couches est combiné, de sorte que l'utilisateur final du conteneur perçoit les matériaux «disposés» dans les couches comme un système de fichiers unique.


Vue UnionFS simplifiée

Le système de fichiers résultant est présenté à l'utilisateur final à l'aide d'une implémentation UnionFS (Docker prend en charge de nombreuses implémentations similaires via des pilotes de stockage de plug-in). La taille totale des fichiers reçus par l'utilisateur final est égale à la somme des tailles des fichiers dans les couches. Lorsque Docker crée un conteneur basé sur l'image, il utilise toutes les couches en lecture seule de l'image, en ajoutant une couche mince au-dessus de ces couches qui prend en charge la lecture et l'écriture. C'est cette couche qui vous permet de modifier des fichiers dans un conteneur en cours d'exécution.


Le conteneur en cours d'exécution contient une couche en lecture-écriture située au-dessus des couches en lecture seule

Que se passe-t-il si un fichier est supprimé dans la Layer 4 conteneur schématisé ci-dessus? Bien que ce fichier ne soit pas disponible dans le système de fichiers que l'utilisateur voit, en fait, la taille de ce fichier sera toujours l'un des composants de la taille du conteneur, car ce fichier restera dans l'une des couches en lecture seule.

Il est assez simple de commencer à créer l'image avec un petit fichier exécutable d'application et d'accéder à la très grande image. Ci-dessous, nous examinerons différentes méthodes pour rendre les conteneurs aussi petits que possible.

Faites attention au chemin d'accès au dossier, en fonction des matériaux dont les images sont collectées


Quelle est la méthode la plus courante pour assembler des images Docker? Apparemment - comme ça:

 docker build . 

Le point de cette commande indique à Docker que nous considérons que le répertoire de travail actuel est la racine du système de fichiers utilisé dans le processus d'assemblage d'image.

Afin de mieux comprendre ce qui se passe après l'exécution de la commande ci-dessus, il convient de se rappeler que la construction d'une image Docker est un processus client-serveur. L'interface de ligne de commande Docker (client), à laquelle nous donnons la docker build , utilise le moteur Docker (serveur) pour créer l'image du conteneur. Pour limiter l'accès au système de fichiers de base du client, le système d'assemblage d'images doit savoir où se trouve la racine du système de fichiers virtuel. C'est là que les instructions du fichier Dockerfile recherchent les ressources de fichiers qui peuvent éventuellement se retrouver dans l'image en cours d'assemblage.

Imaginez un endroit où un Dockerfile généralement placé. C'est probablement le répertoire racine du projet? S'il y a un Dockerfile à la racine du projet, qui est utilisé par la docker build pour construire l'image, il s'avère que tous les fichiers du projet peuvent entrer dans l'image. Cela peut conduire au fait que des milliers de fichiers indésirables de plusieurs mégaoctets peuvent entrer dans le contexte de l'assemblage d'image. Si vous utilisez légèrement les commandes ADD et COPY dans le Dockerfile , tous les fichiers de projet peuvent bien faire partie de l'image finale. Le plus souvent, ceux qui collectent des images n'en ont pas besoin, car l'image finale ne doit généralement inclure que certains fichiers sélectionnés.

Assurez-vous toujours que la commande docker build chemin d'accès correct et qu'il n'y a aucune commande dans le Dockerfile qui ajoute des fichiers inutiles à l'image. Si, pour une raison ou une autre, vous devez faire du root du projet le contexte de génération, vous pouvez y inclure des fichiers de manière sélective et les en exclure à l'aide de .dockerignore .

Optimiser les calques d'image


Le nombre maximal de couches qu'une image peut avoir est de 127 (compte tenu de la prise en charge d'un tel nombre de couches utilisées par le pilote d'entrepôt de données). Cette limitation, si elle est absolument nécessaire, peut être assouplie, mais avec cette approche, la gamme de systèmes sur lesquels ces images peuvent être collectées est réduite. Le fait est que le moteur Docker doit fonctionner sur un système dont le noyau est modifié en conséquence.

Comme mentionné dans la section précédente, en raison du fait que UnionFS est utilisé lors de l'assemblage d'images, les fichiers qui tombent dans une certaine couche y restent même s'ils ont été supprimés des couches sus-jacentes. Voyons cela en utilisant le Dockerfile expérimental:

 FROM alpine RUN wget http://xcal1.vodafone.co.uk/10MB.zip -P /tmp RUN rm /tmp/10MB.zip 

Assemblons l'image:


Assemblage d'une image expérimentale dans laquelle il y a un espace irrationnellement utilisé

Explorez l'image à l'aide de la plongée :


L'indicateur de performance d'image est de 34%

L'indicateur d'efficacité d'image de 34% indique qu'une quantité considérable d'espace d'image est utilisée de manière irrationnelle. Cela conduit à une augmentation du temps de démarrage de l'image, à un gaspillage inutile des ressources réseau, à un temps de démarrage plus lent du conteneur.

Comment se débarrasser de ce problème? Examinons plusieurs options.

▍ Fusion des résultats du travail d'équipe


Avez-vous déjà vu des Dockerfile contenant de très longues directives RUN dans lesquelles de nombreuses commandes shell sont combinées à l'aide de && ? Il s'agit de la fusion des résultats des équipes.

En utilisant cette méthode, nous créons, sur la base des résultats d'une seule longue équipe, une seule couche. Puisqu'il n'y aura aucun calque dans l'image contenant des fichiers supprimés dans les calques suivants, l'image finale n'inclura pas de tels «fichiers fantômes». Considérez ceci comme un exemple, en amenant le Dockerfile ci-dessus à cet état:

 FROM alpine RUN wget http://xcal1.vodafone.co.uk/10MB.zip -P /tmp && rm /tmp/10MB.zip 

Après cela, nous analysons l'image:


La fusion des équipes vous a permis de créer une image 100% optimisée

L'application de cette technique pour optimiser la taille des images dans la pratique est qu'après avoir fini de travailler sur le fichier Dockerfile , vous devez l'analyser et savoir si vous pouvez utiliser la fusion de commandes pour réduire la quantité d'espace gaspillé.

▍Application de l'option --squash


Dans les cas où vous utilisez les Dockerfile autres personnes que vous ne souhaitez pas ou ne pouvez pas modifier, une alternative à la fusion des commandes peut être l'assemblage d'une image à l'aide de l'option --squash .

Les versions modernes de Docker (à partir de la version 1.13) vous permettent de regrouper toutes les couches en une seule, éliminant ainsi les "ressources fantômes". Dans ce cas, vous pouvez utiliser le Dockerfile origine non Dockerfile , contenant de nombreuses commandes distinctes. Mais vous devez créer l'image en utilisant l'option --squash :

 docker build --squash . 

L'image résultante s'avère également 100% optimisée:


L'utilisation de l'option --squash lors de l'assemblage a permis de créer une image 100% optimisée

Ici, vous pouvez prêter attention à un détail intéressant. À savoir, dans Dockerfile une couche a été créée pour ajouter un fichier et une autre couche pour supprimer ce fichier. L'option --squash est suffisamment intelligente pour comprendre que dans ce scénario, vous n'avez pas du tout besoin de créer de couches supplémentaires (dans l'image finale, il n'y a que la couche 9ccd9… partir de l'image de base que nous utilisons). En général, pour cela, nous pouvons mettre --squash un plus supplémentaire. Certes, en utilisant --squash , vous devez considérer que cela peut interférer avec l'utilisation des couches mises en cache.

Par conséquent, il est recommandé de prendre en compte le fait que lorsque vous travaillez avec le Dockerfile quelqu'un d'autre que vous ne souhaitez pas modifier, vous pouvez réduire la quantité d'espace d'image utilisé de manière irrationnelle en collectant des images à l'aide de l'option --squash . Pour analyser l'image finie, vous pouvez utiliser l'outil de plongée .

Supprimer les caches et les fichiers temporaires


Lors de la conteneurisation d'applications, une situation se produit souvent lorsque vous devez placer des outils, des bibliothèques et des utilitaires supplémentaires dans l'image avec eux. Cela se fait en utilisant des gestionnaires de paquets comme apk , yum , apt .

Les gestionnaires de packages s'efforcent de gagner du temps pour l'utilisateur et de ne pas charger à nouveau sa connexion réseau lors de l'installation des packages. Par conséquent, ils mettent en cache les données téléchargées. Pour que la taille de l'image Docker finale soit aussi petite que possible, nous n'avons pas besoin de stocker les caches du gestionnaire de packages dans cette image. Après tout, si nous avons besoin d'une autre image, nous pouvons toujours la reconstruire en utilisant le Dockerfile mis à jour.

Afin de supprimer les caches créés par les trois gestionnaires de packages populaires susmentionnés, à la fin d'une commande agrégée (c'est-à-dire celle qui s'exécute pour créer une couche), vous pouvez ajouter ce qui suit:

 APK: ... && rm -rf /etc/apk/cache YUM: ... && rm -rf /var/cache/yum APT: ... && rm -rf /var/cache/apt 

Par conséquent, il est recommandé qu'avant de terminer le travail sur le Dockerfile ajouter des Dockerfile qui suppriment les caches des gestionnaires de packages utilisés pour créer l'image. La même chose s'applique à tous les fichiers temporaires qui n'affectent pas le bon fonctionnement du conteneur.

Choisissez soigneusement votre image de base


Chaque Dockerfile commence par une directive FROM . C'est là que nous définissons l'image de base sur la base de laquelle notre image sera créée.

Voici ce que la documentation Docker en dit: «L'instruction FROM initialise une nouvelle phase de construction et configure l'image de base pour les instructions qui suivent. Par conséquent, un Dockerfile correctement composé doit commencer par une instruction FROM . Une image peut être n'importe quelle image réalisable. Il est plus facile de commencer à assembler votre propre image, en prenant comme base une image d'un référentiel public. "

De toute évidence, il existe de nombreuses images de base, chacune ayant ses propres caractéristiques et capacités. La sélection correcte d'une image de base qui contient exactement ce dont l'application a besoin, ni plus ni moins, a un impact énorme sur la taille de l'image finale.

Comme vous pouvez vous y attendre, les tailles des images de base populaires varient énormément:


Tailles d'images de docker de base populaires

Ainsi, la conteneurisation de l'application en utilisant l'image de base d' Ubuntu 19.10 conduira au fait que la taille de l'image, en plus de la taille de l'application, sera ajoutée à 73 Mo supplémentaires. Si nous collectons la même image sur la base de l'image d' Alpine 3.10.3 , nous n'obtiendrons un «additif» que d'un montant de 6 Mo. Étant donné que Docker met en cache les couches d'images, les ressources réseau sont consacrées au chargement d'une image uniquement lorsque le conteneur est lancé pour la première fois de la manière appropriée (en d'autres termes, la première fois que l'image est chargée). Mais la taille de l'image elle-même n'en diminue pas.

Ici, vous pouvez arriver à la conclusion (tout à fait logique) suivante: "Donc - j'utiliserai toujours Alpine!". Mais, malheureusement, dans le monde du développement logiciel, tout n'est pas si simple.

Peut-être que les développeurs d'Alpine Linux ont découvert un ingrédient secret qu'Ubuntu ou Debian ne peuvent toujours pas trouver? Non. Le fait est que pour créer une image Docker, dont la taille est d'un ordre de grandeur inférieur à la taille de l'image du même Debian, les développeurs Alpine ont dû prendre des décisions sur ce qui doit être inclus dans l'image et ce qui n'est pas nécessaire. Avant d'appeler Alpine l'image de base que vous utiliserez toujours, vous devez lui demander si elle contient tout ce dont vous avez besoin. De plus, même si Alpine possède un gestionnaire de packages, il se peut que le package spécifique utilisé dans votre environnement de travail basé, par exemple, sur Ubuntu, ne soit pas disponible dans Alpine. Ou - pas un package, mais la version souhaitée du package. Ce sont les compromis que vous devez connaître avant de choisir et de tester l'image de base la mieux adaptée à votre projet.

Et enfin, si vous avez vraiment besoin de l'une des plus grandes images de base, vous pouvez utiliser l'outil pour réduire la taille de l'image. Par exemple - un outil open source gratuit DockerSlim . Cela réduira la taille de l'image finie.

En fin de compte, nous pouvons dire que l'utilisation d'une image de base soigneusement sélectionnée est extrêmement importante pour créer vos propres images compactes. Évaluez les besoins de votre projet et sélectionnez une image qui contient ce dont vous avez besoin tout en ayant des dimensions qui vous conviennent.

Pensez à créer une image qui n'a pas d'image de base.


Si votre application peut s'exécuter sans un environnement supplémentaire fourni de manière basique, vous pouvez décider de ne pas utiliser une image de base. Bien sûr, puisque l'instruction FROM doit être présente dans le Dockerfile , vous ne pouvez pas vous en passer. Elle doit en outre pointer vers une sorte d'image. Quelle image utiliser dans une telle situation?

Un look Scratch pourrait être utile ici. D'après sa description, vous pouvez découvrir qu'il est spécialement rendu vide et conçu pour créer des images, si vous parlez le langage Dockerfile , FROM scratch , c'est-à-dire à partir de zéro. Cette image est particulièrement utile lors de la création d'images de base (telles que des images debian et busybox) ou des images extrêmement minimales (celles qui contiennent un seul fichier binaire et ce qui est requis pour son fonctionnement, par exemple, est quelque chose comme hello-world). L'utilisation de cette image comme base de l'image décrite par le Dockerfile est similaire à l'utilisation d'une «opération vide» dans certains programmes. L'application d'une image de travail ne créera pas de couche supplémentaire dans l'image finie.

Par conséquent, si votre application est un exécutable autonome qui peut fonctionner seul, le choix de l'image de base de base vous permettra de réduire la taille du conteneur.

Utiliser des builds en plusieurs étapes


Les builds en plusieurs étapes ont été au centre de l'attention depuis Docker 05/17. C'était une opportunité attendue depuis longtemps. Il permet aux créateurs d'images d'abandonner leurs propres scripts pour créer des images et implémenter tout ce dont ils ont besoin en utilisant le format Dockerfile bien connu.

En termes généraux, un assemblage à plusieurs étapes peut être considéré comme combinant plusieurs Dockerfile , ou comme un Dockerfile , qui contient plusieurs instructions FROM .

Avant l'émergence d'assemblages à plusieurs étapes, si vous deviez créer un assemblage de votre projet et le distribuer dans un conteneur à l'aide du Dockerfile , vous auriez probablement besoin de terminer le processus d'assemblage, ce qui conduirait à l'apparition d'un conteneur, comme celui illustré ci-dessous:


Créez et distribuez une application sans utiliser la technologie de construction en plusieurs étapes

Bien que d'un point de vue technique, tout ait été fait correctement, l'image finale et le conteneur résultant sont remplis de couches créées dans le processus de préparation des matériaux du projet. Et ces couches ne sont pas nécessaires pour former l'environnement d'exécution du projet.

Les assemblages en plusieurs étapes vous permettent de séparer les phases de création et de préparation des matériaux du projet de l'environnement dans lequel le code du projet est exécuté.


Assemblage en plusieurs étapes, séparation du processus de création et de préparation des matériaux du projet de l'environnement d'exécution

Dans le même temps, un seul Dockerfile suffit pour décrire le processus complet de construction du projet. Mais maintenant, vous pouvez copier du matériel d'une étape à l'autre et vous débarrasser des données inutiles.

Les assemblys à plusieurs étages vous permettent de créer des assemblys multiplateformes qui peuvent être utilisés à plusieurs reprises sans utiliser vos propres scripts d'assemblage écrits pour un système d'exploitation spécifique. La taille finale de l'image peut être minimisée en raison de la possibilité d'inclusion sélective de matériaux générés aux étapes précédentes du processus d'assemblage d'image.

Résumé


La création d'images de conteneur Docker est un processus auquel les programmeurs modernes doivent souvent faire face. Il existe de nombreuses ressources pour créer des Dockerfile , et vous pouvez trouver de nombreux exemples de ces fichiers sur Internet. Mais peu importe ce que vous utilisez, lors de la création de votre propre Dockerfile toujours utile de considérer la taille des images résultantes.

Ici, nous avons examiné plusieurs techniques pour minimiser la taille des images Docker. Dockerfile au contenu du Dockerfile , y compris uniquement ce dont vous avez vraiment besoin, choisir la bonne image de base, utiliser la technologie de construction en plusieurs étapes - tout cela peut aider à réduire sérieusement la taille des images Docker que vous créez.

PS Nous avons lancé le marché sur le site Web de RUVDS. Sur le marché, l'image Docker est installée en un clic, vous pouvez vérifier le fonctionnement des conteneurs sur VPS , 3 jours pour les tests sont fournis gratuitement pour tous les nouveaux clients.

Chers lecteurs! Comment optimisez-vous la taille de vos images Docker?

Source: https://habr.com/ru/post/fr485650/


All Articles