Il y a quelques mois, j'ai écrit un article sur l'accélération de la construction de conteneurs à l'intérieur d'un conteneur. Cet article s'est concentré sur la vitesse d'extraction des images de conteneurs et sur les différentes manières de préremplir le magasin d'images, en utilisant les montages de volume de l'hôte et le concept Buildah de "magasins supplémentaires".
Buildah est un outil de ligne de commande permettant de créer rapidement et facilement des images compatibles avec l'Open Container Initiative (c'est-à-dire compatibles avec Docker et Kubernetes). Buildah est facile à intégrer dans des scripts et à créer des pipelines, et surtout, il ne nécessite pas de démon de conteneur en cours d'exécution pour créer son image.
Cet article abordera un deuxième problème avec la vitesse de construction lors de l'utilisation de dnf
/yum
commandes à l'intérieur des conteneurs. Notez que dans cet article j'utiliserai le nom dnf
(qui est le nom en amont) au lieu de ce que certains utilisateurs en aval utilisent (yum
) Ces commentaires s'appliquent à la fois à dnf
et yum
.
Vitesse de téléchargement
Avez-vous déjà remarqué que, parfois, lorsque vous exécutez dnf -y update
ou dnf -y install
pour la première fois depuis longtemps, la commande s'interrompt longtemps avant même de commencer à réduire les RPM ? Que se passe-t-il ?
La première chose que dnf
est de télécharger d'énormes fichiers de cache. Ces fichiers sont écrits en XML et contiennent chaque package du référentiel distant, y compris de nombreuses données sur le package. Ils contiennent même tous les chemins du package. Ces données sont nécessaires pour que vous puissiez exécuter quelque chose comme dnf -y install /usr/bin/httpd
puis dnf
détermine le paquet à installer. De nombreux packages contiennent des commandes telles que (requires: /usr/bin/sendmail
) qui tirent parti de cette fonctionnalité, en autorisant dnf
pour tirer un package approprié pour satisfaire le besoin.
L'extraction de ces fichiers volumineux, et plus important encore, leur traitement, peut prendre plus d'une minute. Ces données sont utilisées par libsolv
, il doit donc être converti en solv
format, qui est lent. La vitesse n'est pas un gros problème lorsque vous ne le faites que périodiquement sur votre hôte, mais lorsqu'il s'agit de créer des conteneurs, c'est beaucoup plus important.
Syntaxe Dockerfile
Bien que Buildah vous permette de créer des images de conteneurs directement dans le shell, la plupart des gens utilisent Dockerfiles et Containerfiles pour créer des conteneurs et définir des recettes d'images reproductibles. Buildah recherche par défaut un Containerfile
et un Dockerfile
à présent. Chacun partage la même syntaxe, je vais donc utiliser Containerfile
pour le reste de ce document.
Une chose courante à faire dans un Containerfile
est d'utiliser une syntaxe du type :
FROM ubi8
RUN dnf -y update; dnf -y install nginx; dnf -y clean all
…
RUN dnf -y install jboss; dnf -y clean all
Examinons le dnf
lignes. Le premier dnf
ligne :
(dnf -y update; dnf -y install nginx; dnf -y clean all
):
- Met à jour tous les packages du conteneur.
- Installe le package sélectionné
nginx
. - Nettoie tout.
Depuis ubi8
l'image a probablement été créée il y a quelque temps et son /var/cache/dnf
le répertoire n'existe probablement pas dans l'image du conteneur, dnf
doit extraire le fichier cache XML et le traiter. Ensuite, dnf
installe les packages réels avant dnf -y clean all
supprime toutes les données en excès que les commandes précédentes ont placées dans l'image, comme les fichiers journaux et de cache.
Il est recommandé aux utilisateurs d'exécuter clean all
pour garder l'image aussi petite que possible. Chaque RUN
La commande crée un nouveau calque, et même si vous supprimez le contenu dans un RUN
ultérieur commande, la couche initiale contiendra tout le contenu. Cela signifie que tous ceux qui extraient votre image finiront par extraire les journaux et les fichiers de cache. Maintenant, si votre Containerfile
contient un ou plusieurs dnf
commandes, vous en paierez le prix encore et encore. Non seulement cela, mais chaque fois que vous reconstruisez cette image, vous paierez à nouveau ce prix. Si vous êtes sur un serveur de compilation, chaque image de conteneur que vous créez téléchargera ces fichiers XML encore et encore, ce qui vous fera perdre des tonnes de ressources et de temps.
Buildah avec des supports superposés
Nous avons vu le problème décrit ci-dessus et avons pensé que nous pourrions mieux le gérer. Ne pourrions-nous pas simplement extraire les données XML vers l'hôte, les traiter sur l'hôte et les monter en volume dans les conteneurs ? Peut-être pourrions-nous mettre en place une tâche cron ou un systemd
timer qui fait un dnf makecache
une fois pour chaque version des systèmes d'exploitation pour lesquels vous allez créer des images de conteneur ? Vous pouvez exécuter cette tâche une ou plusieurs fois par jour sur l'hôte, puis demander à tous les volumes de création de conteneurs de monter les caches appropriés dans les conteneurs buildah.
Eh bien, Buildah prend en charge les répertoires de montage de volume de l'hôte dans les conteneurs. Cela devrait résoudre le problème, et c'est le cas. MAIS, les conteneurs veulent souvent écrire dans ce répertoire, s'ils doivent mettre à jour le cache, donc le cache doit être monté dans les conteneurs en lecture/écriture. Cela provoque un énorme trou de sécurité. Imaginez une situation dans laquelle une construction de conteneur hostile écrit du contenu dans ce cache qu'un constructeur de conteneur ultérieur lit. Cela pourrait éventuellement inciter le deuxième conteneur à installer un logiciel piraté. Nous avons besoin d'une solution où le contenu est monté dans le conteneur, ne peut pas être écrit par le conteneur, mais toujours écrit du point de vue du conteneur. C'est fondamentalement ce qu'est un point de montage de superposition.
Le système de fichiers de superposition monte un lower
répertoire, puis attache un upper
répertoire vers le merged
point de montage. Lorsqu'un processus écrit dans un nouveau fichier dans le merged
répertoire, le nouveau fichier est écrit dans le upper
annuaire. Lorsqu'un processus modifie un fichier existant dans le lower
répertoire, le noyau copie le fichier depuis le répertoire lower
répertoire vers le upper
répertoire et permet au processus de modifier le fichier dans le upper
répertoire.
Nous avons introduit le concept de la monture Overlay dans Buildah. Vous pouvez maintenant exécuter des builds avec
buildah bud -v /var/cache/dnf:/var/cache/dnf:O -f /tmp/Containerfile /tmp
Dnf
à l'intérieur du conteneur vérifiera toujours s'il y a du contenu plus récent dans les dépôts et extraira le contenu s'il existe. Mais si le contenu de l'hôte est à jour, il utilisera rapidement le cache de l'hôte. Je vous recommande de mettre à jour le cache des hôtes au moins une fois par jour.
Une fonctionnalité supplémentaire que nous avons ajoutée pour le montage Buildah Overlay est de détruire le upper
répertoire à chaque RUN
directif. Rappelons dans notre exemple, nous avons utilisé plusieurs RUN
commandes qui exécutaient chacune un dnf -y clean all
. Le dnf -y clean all
commande provoque le upper
répertoire pour afficher tout le contenu du bas comme supprimé. Si le prochain dnf
La commande partageait la partie supérieure précédente, elle verrait le cache comme vide et devrait extraire le magasin de données XML et le traiter. Suppression du upper
répertoire signifie que chaque dnf
la commande verra à nouveau le lower
répertoire de l'hôte et continuez à partager le cache des hôtes.
Différence de vitesse
Je vais créer un simple Containerfile
contenant deux dnf
exécuter des commandes.
FROM fedora:31
RUN dnf -y install net-utils; dnf -y clean all
RUN dnf -y install iputils; dnf -y clean all
Exécuter ceci localement sur ma boîte Fedora 31
# time -f "Elapsed Time: %E" buildah bud -f Containerfile .
STEP 1: FROM fedora:31
STEP 2: RUN dnf -y install procps-ng; dnf -y clean all
Fedora Modular 31 - x86_64 2.0 MB/s | 5.2 MB 00:02
Fedora Modular 31 - x86_64 - Updates 1.6 MB/s | 4.0 MB 00:02
Fedora 31 - x86_64 - Updates 4.2 MB/s | 19 MB 00:04
Fedora 31 - x86_64 1.8 MB/s | 71 MB 00:39
Last metadata expiration check: 0:00:01 ago on Wed Feb 5 13:55:54 2020.
Dependencies resolved.
================================================================================
Package Architecture Version Repository Size
================================================================================
Installing:
procps-ng x86_64 3.3.15-6.fc31 fedora 326 k
Transaction Summary
================================================================================
Install 1 Package
Total download size: 326 k
Installed size: 966 k
Downloading Packages:
procps-ng-3.3.15-6.fc31.x86_64.rpm 375 kB/s | 326 kB 00:00
--------------------------------------------------------------------------------
Total 218 kB/s | 326 kB 00:01
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
Preparing : 1/1
Installing : procps-ng-3.3.15-6.fc31.x86_64 1/1
Running scriptlet: procps-ng-3.3.15-6.fc31.x86_64 1/1
Verifying : procps-ng-3.3.15-6.fc31.x86_64 1/1
Installed:
procps-ng-3.3.15-6.fc31.x86_64
Complete!
33 files removed
STEP 3: RUN dnf -y install iputils; dnf -y clean all
Fedora Modular 31 - x86_64 741 kB/s | 5.2 MB 00:07
Fedora Modular 31 - x86_64 - Updates 928 kB/s | 4.0 MB 00:04
Fedora 31 - x86_64 - Updates 3.8 MB/s | 19 MB 00:05
Fedora 31 - x86_64 7.9 MB/s | 71 MB 00:08
Last metadata expiration check: 0:00:01 ago on Wed Feb 5 13:57:13 2020.
Dependencies resolved.
================================================================================
Package Architecture Version Repository Size
================================================================================
Installing:
iputils x86_64 20190515-3.fc31 fedora 141 k
Transaction Summary
================================================================================
Install 1 Package
Total download size: 141 k
Installed size: 387 k
Downloading Packages:
iputils-20190515-3.fc31.x86_64.rpm 252 kB/s | 141 kB 00:00
--------------------------------------------------------------------------------
Total 141 kB/s | 141 kB 00:01
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
Preparing : 1/1
Installing : iputils-20190515-3.fc31.x86_64 1/1
Running scriptlet: iputils-20190515-3.fc31.x86_64 1/1
Verifying : iputils-20190515-3.fc31.x86_64 1/1
Installed:
iputils-20190515-3.fc31.x86_64
Complete!
33 files removed
STEP 4: COMMIT
Getting image source signatures
Copying blob ac0b803c5612 skipped: already exists
Copying blob 922380d685bc done
Copying config 566e2afbb4 done
Writing manifest to image destination
Storing signatures
566e2afbb417f0119109578a87950250b566a3b4908868627975a4c7428accfb
566e2afbb417f0119109578a87950250b566a3b4908868627975a4c7428accfb
Elapsed Time: 2:15.00
Cette exécution a pris 2 minutes et 15 secondes pour créer une nouvelle image de conteneur avec les deux nouveaux packages.
Essayons maintenant avec un montage Overlay de l'hôte.
# dnf -y makecache
# time -f "Elapsed Time: %E" buildah bud -v /var/cache/dnf:/var/cache/dnf:O -f Containerfile .
STEP 1: FROM fedora:31
STEP 2: RUN dnf -y install procps-ng; dnf -y clean all
Last metadata expiration check: 0:02:34 ago on Wed Feb 5 13:51:54 2020.
Dependencies resolved.
================================================================================
Package Architecture Version Repository Size
================================================================================
Installing:
procps-ng x86_64 3.3.15-6.fc31 fedora 326 k
Transaction Summary
================================================================================
Install 1 Package
Total download size: 326 k
Installed size: 966 k
Downloading Packages:
procps-ng-3.3.15-6.fc31.x86_64.rpm 496 kB/s | 326 kB 00:00
--------------------------------------------------------------------------------
Total 245 kB/s | 326 kB 00:01
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
Preparing : 1/1
Installing : procps-ng-3.3.15-6.fc31.x86_64 1/1
Running scriptlet: procps-ng-3.3.15-6.fc31.x86_64 1/1
Verifying : procps-ng-3.3.15-6.fc31.x86_64 1/1
Installed:
procps-ng-3.3.15-6.fc31.x86_64
Complete!
285 files removed
STEP 3: RUN dnf -y install iputils; dnf -y clean all
Last metadata expiration check: 0:02:41 ago on Wed Feb 5 13:51:54 2020.
Dependencies resolved.
================================================================================
Package Architecture Version Repository Size
================================================================================
Installing:
iputils x86_64 20190515-3.fc31 fedora 141 k
Transaction Summary
================================================================================
Install 1 Package
Total download size: 141 k
Installed size: 387 k
Downloading Packages:
iputils-20190515-3.fc31.x86_64.rpm 556 kB/s | 141 kB 00:00
--------------------------------------------------------------------------------
Total 222 kB/s | 141 kB 00:00
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
Preparing : 1/1
Installing : iputils-20190515-3.fc31.x86_64 1/1
Running scriptlet: iputils-20190515-3.fc31.x86_64 1/1
Verifying : iputils-20190515-3.fc31.x86_64 1/1
Installed:
iputils-20190515-3.fc31.x86_64
Complete!
285 files removed
STEP 4: COMMIT
Getting image source signatures
Copying blob ac0b803c5612 skipped: already exists
Copying blob 524bb3b83d61 done
Copying config 0f82aa6064 done
Writing manifest to image destination
Storing signatures
0f82aa6064814ff3dcb603c34c75e516e00817811681b83b8632f3e9b694e518
0f82aa6064814ff3dcb603c34c75e516e00817811681b83b8632f3e9b694e518
Elapsed Time: 0.17.44
Avec le montage Overlay, nous avons pu construire une nouvelle image avec les deux packages supplémentaires en 17 secondes au lieu de 2 minutes et 15 secondes. C'est presque 8 fois plus rapide pour créer la même image de conteneur.
Maintenant, cela montre que si vous créez des images sur un système d'exploitation hôte qui a le dnf
métadonnées pré-cachées, vous pouvez accélérer considérablement la vitesse d'installation. Mais que se passe-t-il si votre système de build crée des images pour d'autres versions du système d'exploitation ? Supposons que vous souhaitiez créer des images pour Fedora 30 ainsi que Fedora 31. Remarque :cela fonctionnerait également sur un système RHEL8, où vous pourriez vouloir créer des images RHEL7 et peut-être même RHEL6.Dnf
inclut une fonctionnalité intéressante où vous pouvez spécifier différentes versions lors de l'extraction de contenu, en utilisant le --releasever
option. Dnf
vous permet également de spécifier des répertoires alternatifs pour placer le cachedir, --setopt=cachedir
.
Dans l'exemple suivant, je vais extraire deux caches sur l'hôte, puis utiliser Buildah en mode ligne de commande.
# dnf -y makecache --releasever=31 --setopt=cachedir=/var/cache/dnf/31
# dnf -y makecache --releasever=30 --setopt=cachedir=/var/cache/dnf/30
# ctr31=$(buildah from fedora:31)
# time -f 'Elapsed Time: %E' buildah run -v /var/cache/dnf/31:/var/cache/dnf:O ${ctr31} dnf -y install iputils
Last metadata expiration check: 0:00:15 ago on Wed Feb 5 14:17:41 2020.
Dependencies resolved.
================================================================================
Package Architecture Version Repository Size
================================================================================
Installing:
iputils x86_64 20190515-3.fc31 fedora 141 k
Transaction Summary
================================================================================
Install 1 Package
Total download size: 141 k
Installed size: 387 k
Downloading Packages:
iputils-20190515-3.fc31.x86_64.rpm 192 kB/s | 141 kB 00:00
--------------------------------------------------------------------------------
Total 107 kB/s | 141 kB 00:01
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
Preparing : 1/1
Installing : iputils-20190515-3.fc31.x86_64 1/1
Running scriptlet: iputils-20190515-3.fc31.x86_64 1/1
Verifying : iputils-20190515-3.fc31.x86_64 1/1
Installed:
iputils-20190515-3.fc31.x86_64
Complete!
Elapsed Time: 0:06.85
# ctr30=$(buildah from fedora:30)
# time -f 'Elapsed Time: %E' buildah run -v /var/cache/dnf/30:/var/cache/dnf:O ${ctr30} dnf -y install iputils
Last metadata expiration check: 0:00:15 ago on Wed Feb 5 14:17:47 2020.
Dependencies resolved.
================================================================================
Package Architecture Version Repository Size
================================================================================
Installing:
iputils x86_64 20180629-4.fc30 fedora 123 k
Transaction Summary
================================================================================
Install 1 Package
Total download size: 123 k
Installed size: 351 k
Downloading Packages:
iputils-20180629-4.fc30.x86_64.rpm 370 kB/s | 123 kB 00:00
--------------------------------------------------------------------------------
Total 138 kB/s | 123 kB 00:00
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
Preparing : 1/1
Installing : iputils-20180629-4.fc30.x86_64 1/1
Running scriptlet: iputils-20180629-4.fc30.x86_64 1/1
Verifying : iputils-20180629-4.fc30.x86_64 1/1
Installed:
iputils-20180629-4.fc30.x86_64
Complete!
Elapsed Time: 0:08.88
Comme vous pouvez le voir, nous avons pu exécuter des conteneurs Buildah en utilisant le dnf
cache de deux versions différentes de Fedora à partir du même hôte de construction et les conteneurs pour Fedora 31 ont pris plus de 6 secondes et la construction de Fedora 30 a pris plus de 8 secondes.
Remarque :J'ai choisi un sous-répertoire de /var/cache/dnf
pour les fichiers de cache, pour s'assurer que les étiquettes SELinux étaient correctes. Exécutez simplement dnf clean all
ne nettoiera pas /var/cache/dnf/31
. Vous devrez exécuter dnf clean all --setopt=cachedir=/var/cache/dnf/31
pour nettoyer correctement les fichiers mis en cache du référentiel, mais certains artefacts resteront quand même (clés gpg, répertoires vides).
Maintenant, juste pour voir combien de temps cela prendrait pour exécuter la version sur Fedora 31 sans le montage Overlay.
# ctr31=$(buildah from fedora:31)
# time -f 'Elapsed Time: %E' buildah run ${ctr31} dnf -y install iputils
Fedora Modular 31 - x86_64 1.2 MB/s | 5.2 MB 00:04
Fedora Modular 31 - x86_64 - Updates 875 kB/s | 4.0 MB 00:04
Fedora 31 - x86_64 - Updates 2.4 MB/s | 19 MB 00:07
Fedora 31 - x86_64 1.7 MB/s | 71 MB 00:41
Dependencies resolved.
================================================================================
Package Architecture Version Repository Size
================================================================================
Installing:
iputils x86_64 20190515-3.fc31 fedora 141 k
Transaction Summary
================================================================================
Install 1 Package
Total download size: 141 k
Installed size: 387 k
Downloading Packages:
iputils-20190515-3.fc31.x86_64.rpm 279 kB/s | 141 kB 00:00
--------------------------------------------------------------------------------
Total 129 kB/s | 141 kB 00:01
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
Preparing : 1/1
Installing : iputils-20190515-3.fc31.x86_64 1/1
Running scriptlet: iputils-20190515-3.fc31.x86_64 1/1
Verifying : iputils-20190515-3.fc31.x86_64 1/1
Installed:
iputils-20190515-3.fc31.x86_64
Complete!
Elapsed Time: 1:29.85
Dans ce cas, il a fallu près d'une minute et demie pour exécuter le même conteneur. Buildah avec des montures superposées a fonctionné 14 fois plus vite.
Conteneurs sans racine
Jusqu'à présent, tous mes exemples ont exécuté les builds à l'aide de Containerfiles
en tant que racine. Mais vous pouvez également le faire avec des conteneurs sans racine. Dans les deux prochains exemples, j'aurai des conteneurs d'exécution Buildah en utilisant le buildah run
syntaxe pour démontrer l'utilisation du cache.
Exécution
$ buildah run -v /var/cache/dnf/30:/var/cache/dnf:O ${ctr30} dnf -y install iputils
fonctionne bien. Tant que l'utilisateur peut lire le /var/cache/dnf/30
répertoire, le lower
répertoire peut être lu. Mais vous devez compter sur quelque chose sur l'hôte pour mettre à jour le cache périodiquement.
Si les utilisateurs le souhaitent, ils peuvent même utiliser dnf
pour créer le cache dans leur répertoire personnel.
$ dnf -y makecache --releasever=30 --setopt=cachedir=$HOME/dnfcache
$ chcon --reference /var/cache/dnf -R $HOME/dnfcache
$ ctr30=$(buildah from fedora:30)
$ buildah run -v $HOME/dnfcache:/var/cache/dnf:O ${ctr30} dnf -y install iputils
Remarquez que j'ai dû changer l'étiquette SELinux du $HOME/dnfcache
répertoire afin que SELinux autorise les conteneurs à lire le lower
répertoire pour le montage Overlay.
Conclusion
Accélérer la construction de conteneurs nécessite de comprendre ce qui se passe lorsque vous installez des packages. Pré-cache dnf
les données sur l'hôte et l'utilisation de montages superposés pour monter le cache dans le conteneur avec Buildah peuvent considérablement augmenter la vitesse des builds et réduire le nombre de ressources nécessaires pour prendre en charge une batterie de builds.
Buildah est synonyme de simplicité, mais il possède également des fonctionnalités intéressantes telles que les Overlay mounts
et additional stores
qui peut vous aider à accélérer la création de vos images de conteneur.
[ Nouveau dans les conteneurs ? Téléchargez le Containers Primer et apprenez les bases des conteneurs Linux. ]