Es gibt eine Vielzahl von Techniken, für die es keine einzige Lösung gibt. Sie werden wahrscheinlich mehrere der folgenden Aktionen ausführen wollen:
Optimieren Sie zunächst Ihre Bildebenen für die Wiederverwendung. Fügen Sie häufig geänderte Schritte später in das Dockerfile ein, um die Wahrscheinlichkeit zu erhöhen, dass frühe Ebenen aus früheren Builds zwischengespeichert werden. Eine wiederverwendete Ebene wird als mehr Speicherplatz auf einer Festplatte angezeigt. docker image ls
Wenn Sie jedoch das zugrunde liegende Dateisystem untersuchen, wird immer nur eine Kopie jeder Ebene auf der Festplatte gespeichert. Das bedeutet, dass 3 Images mit jeweils 2 GB, die in den letzten Ebenen des Builds nur 50 MB Speicherplatz haben, nur 2,1 GB Speicherplatz belegen, obwohl die Auflistung den Anschein erweckt, dass sie seitdem 6 GB belegen Doppelzählung jeder der wiederverwendeten Schichten.
Bei der Wiederverwendung von Ebenen werden Bilder mit sich selten ändernden Build-Abhängigkeiten zuerst installiert, bevor sie in den Code kopiert werden. Sehen Sie sich ein beliebiges Python-Beispiel an, das ein Muster wie das folgende aufweist:
FROM python
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
# note how the code is copied only after the pip install
# since code changes but requirements.txt doesn't
COPY . .
CMD ["gunicorn", "app:app"]
Wählen Sie ein minimales Basisbild. Dies ist der Grund, warum Leute von zu ubuntu
zu gehen debian:slim
(die schlanken Varianten sind kleiner, werden mit weniger Werkzeugen geliefert) oder sogar alpine
. Dies verringert die Größe Ihres Startpunkts und ist sehr hilfreich, wenn Sie ständig neue Versionen des Basis-Images abrufen. Wenn sich Ihr Basis-Image jedoch nur selten ändert, wird durch die Wiederverwendung von Ebenen der Vorteil eines minimalen Basis-Images weitgehend aufgehoben.
Das kleinste Basisimage, das Sie auswählen können, ist scratch
, dass es sich um nichts, keine Shell oder Bibliotheken handelt und nur bei statisch kompilierten Binärdateien nützlich ist. Wählen Sie andernfalls ein Basisimage mit den benötigten Werkzeugen aus, ohne viele Werkzeuge, die Sie nicht benötigen.
Als nächstes sollte jeder Schritt, der eine Datei ändert oder löscht, mit den vorherigen Schritten kombiniert werden, mit denen diese Datei erstellt wird. Andernfalls hat das Dateisystem mit mehreren Ebenen, das auch bei Änderungen der Dateiberechtigung Copy-on-Write verwendet, die ursprüngliche Datei in einer vorherigen Ebene, und die Bildgröße wird beim Entfernen von Dateien nicht kleiner. Aus diesem Grund haben Ihre rm
Befehle keine Auswirkung auf den resultierenden Speicherplatz. Stattdessen können Sie die Befehle wie folgt verketten:
RUN apt-get update \
&& apt-get install -y \
a-package \
wget \
&& ... \
&& apt-get purge -y wget \
&& rm -r a-build-dir \
&& apt-get purge -y a-package
Beachten Sie, dass eine übermäßige Verwendung der Befehlskettung Ihre Builds verlangsamen kann, da Sie das gleiche Toolset jedes Mal neu installieren müssen, wenn sich eine Voraussetzung ändert (z. B. wenn der Code mit wget abgerufen wird). Eine bessere Alternative finden Sie weiter unten unter Mehrstufen.
Jede Datei, die Sie erstellen, die Sie in Ihrem resultierenden Bild nicht benötigen, sollte in dem Schritt, in dem Sie sie erstellen, gelöscht werden. Dies schließt Paket-Caches, Protokolle, Manpages usw. ein. Um herauszufinden, welche Dateien in den einzelnen Ebenen erstellt werden, können Sie ein Tool wie wagoodman / dive verwenden (das ich nicht persönlich überprüft habe und das Vorsicht walten lassen würde, da es mit vollem Root-Zugriff ausgeführt wird auf Ihrem Host), oder Sie können Ihre Docker-Images erstellen, ohne die Zwischencontainer zu beschneiden, und dann den Unterschied anzeigen mit:
# first create and leave containers from any RUN step using options on build
docker image build --rm=false --no-cache -t image_name .
# review which layers use an unexpectedly large amount of space
docker image history image_name
# list all containers, particularly the exited ones from above
docker container ps -a
# examine any of those containers
docker container diff ${container_id}
# ... repeat the diff for other build steps
# then cleanup exited containers
docker container prune
Mit jedem dieses Zwischenbehälter wird das diff zeigen , welche Dateien hinzugefügt, geändert oder gelöscht, dass der Schritt (diese mit einem angedeutet sind A
, C
oder D
vor jedem Dateinamen). Was sich unterscheidet, ist das container-spezifische Lese- / Schreib-Dateisystem. Hierbei handelt es sich um alle Dateien, die vom Container mithilfe von Copy-on-Write aus dem Image-Status geändert wurden.
Die beste Möglichkeit, die Bildgröße zu reduzieren, besteht darin, nicht benötigte Komponenten, wie z. B. Compiler, aus dem ausgelieferten Bild zu entfernen. Mit mehrstufigen Builds können Sie in einer Stufe kompilieren und dann nur die resultierenden Artefakte aus der Buildstufe in ein Laufzeitimage kopieren, das nur das Minimum aufweist, das zum Ausführen der Anwendung erforderlich ist. Auf diese Weise müssen Sie die Erstellungsschritte nicht optimieren, da sie nicht mit dem resultierenden Image geliefert werden.
FROM debian:9 as build
# still chain update with install to prevent stale cache issues
RUN apt-get update \
&& apt-get install -y \
a-package \
wget \
RUN ... # perform any download/compile steps
FROM debian:9-slim as release
COPY --from=build /usr/local/bin/app /usr/local/bin/app
CMD [ "/usr/local/bin/app" ]
Multi-Stage ist ideal für statisch kompilierte Binärdateien, die Sie mit Scratch als Basis-Image ausführen oder von einer Kompilierungsumgebung wie JDK zu einer Laufzeitumgebung wie JRE wechseln können. Dies ist der einfachste Weg, um Ihre Bildgröße drastisch zu reduzieren, während Sie noch schnelle Builds haben. Sie können die Verkettung von Schritten in Ihrer Freigabephase weiterhin durchführen, wenn Sie Schritte zum Ändern oder Löschen von Dateien haben, die in vorherigen Schritten erstellt wurden. In den meisten COPY
Fällen wird die Freigabephase jedoch durch die von einer anderen Phase ausgehende Aufblähung von allen Ebenen isoliert, die in früheren Buildphasen aufgetreten sind.
Hinweis: Ich empfehle nicht, Bilder zusammenzudrücken, da dadurch die Größe eines Bildes auf Kosten der Vermeidung der Wiederverwendung von Ebenen verringert wird. Das bedeutet, dass zukünftige Builds desselben Images mehr Festplatten- und Netzwerkverkehr erfordern, um Aktualisierungen zu senden. Wenn Sie zum ersten Beispiel zurückkehren, wird Ihr Image durch Squashing möglicherweise von 2 GB auf 1 GB verkleinert, nicht jedoch 3 GB anstelle von 2,1 GB.
2.37
vs.1.47 GB