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éeLe 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 seuleQue 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éeL'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éeIci, 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 populairesAinsi, 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 étapesBien 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écutionDans 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?
