Vor ein paar Monaten habe ich einen Artikel über die Beschleunigung von Container-Builds innerhalb eines Containers geschrieben. Dieser Artikel konzentrierte sich auf die Geschwindigkeit des Pullens von Container-Images und verschiedene Möglichkeiten, den Image-Speicher vorab zu füllen, indem Volume-Mounts vom Host und das Buildah-Konzept der „zusätzlichen Speicher“ verwendet wurden.
Buildah ist ein Befehlszeilentool zum schnellen und einfachen Erstellen von mit der Open Container Initiative kompatiblen (d. h. auch Docker- und Kubernetes-kompatiblen) Images. Buildah lässt sich einfach in Skripte und Build-Pipelines integrieren und das Beste ist, dass es keinen laufenden Container-Daemon benötigt, um sein Image zu erstellen.
Dieser Artikel behandelt ein zweites Problem mit der Build-Geschwindigkeit bei der Verwendung von dnf
/yum
Befehle in Containern. Beachten Sie, dass ich in diesem Artikel den Namen dnf
verwenden werde (das ist der Upstream-Name) anstelle dessen, was einige Downstreams verwenden (yum
) Diese Kommentare gelten sowohl für dnf
und yum
.
Download-Geschwindigkeit
Ist Ihnen das manchmal aufgefallen, wenn Sie dnf -y update
ausführen oder dnf -y install
Zum ersten Mal seit einiger Zeit pausiert der Befehl für eine lange Zeit, bevor er überhaupt anfängt, die Drehzahl zu senken? Was ist los?
Das erste, was dnf
lädt riesige Cache-Dateien herunter. Diese Dateien sind in XML geschrieben und enthalten jedes einzelne Paket im Remote-Repository, einschließlich vieler Daten über das Paket. Sie enthalten sogar jeden Pfad innerhalb des Pakets. Diese Daten werden benötigt, damit Sie etwas wie dnf -y install /usr/bin/httpd
ausführen können und dann dnf
ermittelt das zu installierende Paket. Viele Pakete enthalten Befehle wie (requires: /usr/bin/sendmail
), die diese Funktion nutzen und dnf
zulassen ein geeignetes Paket zu ziehen, um den Bedarf zu decken.
Das Abrufen dieser riesigen Dateien – und noch wichtiger, das Verarbeiten dieser Dateien – kann über eine Minute dauern. Diese Daten werden von libsolv
verwendet , also muss es in solv
konvertiert werden Format, das langsam ist. Geschwindigkeit ist kein großes Problem, wenn Sie dies nur regelmäßig auf Ihrem Host tun, aber wenn es um das Erstellen von Containern geht, ist dies eine viel größere Sache.
Dockerfile-Syntax
Obwohl Sie mit Buildah Container-Images direkt in der Shell erstellen können, verwenden die meisten Benutzer Dockerfiles und Containerfiles, um Container zu erstellen und reproduzierbare Image-Rezepte zu definieren. Buildah sucht standardmäßig nach einer Containerfile
und ein Dockerfile
jetzt. Alle haben die gleiche Syntax, also werde ich Containerfile
verwenden für den Rest dieses Dokuments.
Eine übliche Vorgehensweise in einer Containerfile
ist eine Syntax wie:
FROM ubi8
RUN dnf -y update; dnf -y install nginx; dnf -y clean all
…
RUN dnf -y install jboss; dnf -y clean all
Untersuchen wir den dnf
Linien. Der erste dnf
Zeile:
(dnf -y update; dnf -y install nginx; dnf -y clean all
):
- Aktualisiert alle Pakete im Container.
- Installiert das ausgewählte Paket
nginx
. - Säubert alles.
Da das ubi8
image wurde wahrscheinlich vor einiger Zeit erstellt und sein /var/cache/dnf
-Verzeichnis existiert wahrscheinlich nicht im Container-Image, dnf
muss die XML-Cache-Datei herunterziehen und verarbeiten. Dann dnf
installiert die eigentlichen Pakete vor dnf -y clean all
entfernt alle überschüssigen Daten, die durch frühere Befehle in das Bild eingefügt wurden, z. B. Protokoll- und Cache-Dateien.
Benutzern wird empfohlen, clean all
auszuführen um das Bild so klein wie möglich zu halten. Jeder RUN
Befehl erstellt eine neue Ebene, und selbst wenn Sie den Inhalt in einem späteren RUN
entfernen Befehl, enthält die erste Ebene den gesamten Inhalt. Dies bedeutet, dass jeder, der jemals Ihr Image abruft, am Ende die Protokolle und Cache-Dateien abruft. Wenn nun Ihre Containerfile
enthält einen oder mehrere dnf
Befehle zahlen Sie immer wieder den Preis. Nicht nur das, sondern jedes Mal, wenn Sie dieses Image neu erstellen, zahlen Sie diesen Preis erneut. Wenn Sie sich auf einem Build-Server befinden, lädt jedes Container-Image, das Sie erstellen, diese XML-Dateien immer wieder herunter, wodurch Unmengen an Ressourcen und Zeit verschwendet werden.
Buildah mit Overlay-Halterungen
Wir haben das oben beschriebene Problem gesehen und dachten, wir könnten es besser lösen. Könnten wir die XML-Daten nicht einfach auf den Host ziehen, auf dem Host verarbeiten und per Volume in die Container einhängen? Vielleicht könnten wir einen Cron-Job oder ein systemd
einrichten Timer, der einen dnf makecache
durchführt einmal für jede Betriebssystemversion, für die Sie Container-Images erstellen werden? Sie könnten diesen Job einmal oder mehrmals am Tag auf dem Host ausführen und dann alle Container-Builder veranlassen, die entsprechenden Caches in die Buildah-Container einzubinden.
Nun, Buildah unterstützt Volume-Mounting-Verzeichnisse vom Host in Container. Das sollte das Problem lösen, und das tut es auch. ABER, die Container wollen oft in dieses Verzeichnis schreiben, wenn sie den Cache aktualisieren müssen, also muss der Cache in den Containern mit Lese-/Schreibzugriff gemountet werden. Dadurch entsteht eine riesige Sicherheitslücke. Stellen Sie sich eine Situation vor, in der ein feindlicher Container-Build Inhalte in diesen Cache geschrieben hat, die ein späterer Container-Builder gelesen hat. Es könnte möglicherweise den zweiten Container dazu verleiten, gehackte Software zu installieren. Wir brauchen eine Lösung, bei der der Inhalt in den Container gemountet wird, nicht vom Container geschrieben werden kann, aber dennoch aus der Perspektive des Containers beschrieben werden kann. Dies ist im Grunde das, was ein Overlay-Mount-Punkt ist.
Das Overlay-Dateisystem hängt einen lower
ein Verzeichnis und hängt dann einen upper
an Verzeichnis zum merged
Einhängepunkt. Wenn ein Prozess in eine neue Datei schreibt, wird merged
Verzeichnis, die neue Datei wird in den upper
geschrieben Verzeichnis. Wenn ein Prozess eine bestehende Datei im lower
ändert Verzeichnis kopiert der Kernel die Datei aus dem lower
Verzeichnis zum upper
Verzeichnis und erlaubt dem Prozess, die Datei im upper
zu ändern Verzeichnis.
Wir haben das Konzept des Overlay-Mounts in Buildah eingeführt. Jetzt können Sie Builds mit
ausführen
buildah bud -v /var/cache/dnf:/var/cache/dnf:O -f /tmp/Containerfile /tmp
Dnf
innerhalb des Containers wird weiterhin prüfen, ob neuere Inhalte in den Repos vorhanden sind, und den Inhalt herunterziehen, falls vorhanden. Wenn der Inhalt auf dem Host jedoch aktuell ist, wird er schnell den Cache des Hosts verwenden. Ich würde empfehlen, dass Sie den Host-Cache mindestens einmal täglich aktualisieren.
Eine zusätzliche Funktion, die wir für das Buildah-Overlay-Reittier hinzugefügt haben, ist das Zerstören des upper
Verzeichnis bei jedem RUN
Richtlinie. Erinnern Sie sich, dass wir in unserem Beispiel mehrere RUN
verwendet haben Befehle, die jeweils ein dnf -y clean all
ausgeführt haben . Der dnf -y clean all
Befehl bewirkt das upper
Verzeichnis, um den gesamten Inhalt des unteren als gelöscht anzuzeigen. Wenn der nächste dnf
Wenn der Befehl den vorherigen oberen Wert geteilt hat, würde er den Cache als leer sehen und den XML-Datenspeicher herunterziehen und verarbeiten müssen. Entfernen des upper
Verzeichnis bedeutet, dass jeder dnf
Befehl sieht wieder den lower
Verzeichnis vom Host und teilen Sie weiterhin den Host-Cache.
Geschwindigkeitsunterschied
Ich werde eine einfache Containerfile
erstellen enthält zwei dnf
Befehle ausführen.
FROM fedora:31
RUN dnf -y install net-utils; dnf -y clean all
RUN dnf -y install iputils; dnf -y clean all
Führen Sie dies lokal auf meiner Fedora 31-Box aus
# 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
Dieser Durchlauf dauerte 2 Minuten und 15 Sekunden, um ein neues Container-Image mit den zwei neuen Paketen zu erstellen.
Versuchen wir es jetzt mit einem Overlay-Mount vom Host.
# 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
Mit dem Overlay-Mount konnten wir mit den beiden Zusatzpaketen in 17 Sekunden statt 2 Minuten und 15 Sekunden ein neues Image aufbauen. Das ist fast 8-mal schneller, um dasselbe Container-Image zu erstellen.
Dies zeigt nun, dass, wenn Sie Images auf einem Host-Betriebssystem erstellen, das über den dnf
verfügt Metadaten vorgecacht können Sie die Installationsgeschwindigkeit um einen RIESIGEN Betrag beschleunigen. Aber was ist, wenn Ihr Build-System Images für andere Versionen des Betriebssystems erstellt? Angenommen, Sie möchten Images sowohl für Fedora 30 als auch für Fedora 31 erstellen. Hinweis:Dies würde auch auf einem RHEL8-System funktionieren, auf dem Sie möglicherweise RHEL7- und vielleicht sogar RHEL6-Images erstellen möchten.Dnf
enthält eine coole Funktion, mit der Sie beim Abrufen von Inhalten mithilfe von --releasever
verschiedene Releases angeben können Möglichkeit. Dnf
erlaubt Ihnen auch, alternative Verzeichnisse zum Platzieren des Cache-Verzeichnisses anzugeben, --setopt=cachedir
.
Im folgenden Beispiel werde ich zwei Caches auf dem Host herunterziehen und dann Buildah im Befehlszeilenmodus verwenden.
# 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
Wie Sie sehen können, konnten wir Buildah-Container mit dnf
ausführen Cache von zwei verschiedenen Versionen von Fedora vom selben Build-Host und die Container für Fedora 31 dauerten 6+ Sekunden und der Fedora 30-Build dauerte 8+ Sekunden.
Hinweis:Ich habe ein Unterverzeichnis von /var/cache/dnf
gewählt für die Cache-Dateien, um sicherzustellen, dass die SELinux-Labels korrekt sind. Führen Sie einfach dnf clean all
aus bereinigt /var/cache/dnf/31
nicht . Sie müssten dnf clean all --setopt=cachedir=/var/cache/dnf/31
ausführen Repo-Cache-Dateien ordnungsgemäß zu bereinigen, aber einige Artefakte bleiben trotzdem erhalten (gpg-Schlüssel, leere Verzeichnisse).
Jetzt nur um zu sehen, wie lange es dauern würde, den Build auf Fedora 31 ohne das Overlay-Mount auszuführen.
# 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
In diesem Fall dauerte es fast 1,5 Minuten, um denselben Container auszuführen. Buildah mit Overlay-Reittieren lief 14-mal schneller.
Rootlose Container
Alle meine bisherigen Beispiele haben die Builds mit Containerfiles
ausgeführt als Wurzel. Aber Sie können dies auch mit Rootless-Containern tun. In den nächsten Beispielen lasse ich Buildah Container mit buildah run
ausführen Syntax, um die Verwendung des Caches zu demonstrieren.
Ausführen
$ buildah run -v /var/cache/dnf/30:/var/cache/dnf:O ${ctr30} dnf -y install iputils
funktioniert gut. Solange der Benutzer den /var/cache/dnf/30
lesen kann Verzeichnis, das lower
Verzeichnis gelesen werden. Aber Sie müssen sich auf etwas auf dem Host verlassen, um den Cache regelmäßig zu aktualisieren.
Wenn Benutzer möchten, können sie sogar dnf
verwenden um den Cache in ihrem Home-Verzeichnis zu erstellen.
$ 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
Beachten Sie, dass ich das SELinux-Label von $HOME/dnfcache
ändern musste Verzeichnis, damit SELinux den Containern erlauben würde, den lower
zu lesen Verzeichnis für das Overlay-Mount.
Fazit
Um den Bau von Containern zu beschleunigen, müssen Sie verstehen, was passiert, wenn Sie Pakete installieren. dnf
vorab zwischenspeichern Daten auf dem Host und die Verwendung von Overlay-Mounts zum Mounten des Caches in den Container mit Buildah kann die Geschwindigkeit von Builds erheblich erhöhen und die Anzahl der Ressourcen reduzieren, die zur Unterstützung einer Build-Farm erforderlich sind.
Buildah steht für Einfachheit, hat aber auch einige großartige Funktionen wie Overlay mounts
und additional stores
was Ihnen helfen kann, Ihre Container-Image-Builds zu beschleunigen.
[ Neu bei Containern? Laden Sie den Containers Primer herunter und lernen Sie die Grundlagen von Linux-Containern kennen. ]