Wie erstellt man jedes Mal das kleinste funktionierende Docker-Image?

19

Ziel: jedes Mal die kleinsten funktionierenden Docker-Bilder erstellen

Aktuell

REPOSITORY          TAG       IMAGE ID            CREATED             SIZE
a-docker-image      latest    x                   42 minutes ago       1.92 GB

Versuch

Hinzufügen eines Bereinigungsschritts am Ende der Docker-Datei:

#clean
RUN apt-get purge -y wget
RUN rm -r a-build-dir
RUN apt-get purge -y a-package

Bildgröße etwas verkleinert:

REPOSITORY          TAG       IMAGE ID            CREATED             SIZE
a-docker-image      latest    y                   2 minutes ago       1.86 GB

Diskussion

Ich habe verschiedene Docker-Images erstellt. Jedes Mal, wenn ich versuche, das erstellte Bild zu verkleinern, habe ich das Gefühl, dass es zu groß ist. Ich suche ein Skript, das bereits von jemandem auf Github erstellt wurde, der alle überflüssigen Pakete aus dem Image entfernt, damit das erstellte Image so klein wie möglich wird.

Wie gesagt, ich versuche immer, das Bild zu verkleinern, aber ich möchte dies konsistent anwenden, damit jedes Bild, das ich von nun an erstelle, so klein wie möglich wird.

Frage

Wie erstellt man jedes Mal das kleinste funktionierende Docker-Image?

030
quelle

Antworten:

1

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 lsWenn 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 ubuntuzu 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 rmBefehle 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, Coder Dvor 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 COPYFä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.

BMitch
quelle
25

A Dockerfileerstellt für jeden Befehl in der Datei eine neue Ebene. Da Schichten gut sind, geschichtet auf dem jeweils anderen - Sie können Dateien nicht entfernen , dass eine vorherige Schicht hinzugefügt. Wenn Sie Pakete installieren, Dateien herunterladen oder Builds in einem separaten Befehl erstellen, sind diese immer noch im Image enthalten, auch wenn Sie sie in einer zukünftigen Ebene entfernt haben.

Also, wenn Sie dies nur ändern:

RUN apt-get update -y
RUN apt-get install -y wget a-package
# ...
RUN apt-get purge -y wget
RUN rm -r a-build-dir
RUN apt-get purge -y a-package

Dazu:

RUN apt-get update -y \
    && apt-get install -y wget a-package \
    && mkdir a-build-dir \
    && wget http://some-site/very-big-source-code.tar.gz \
    && tar xzvf very-big-source-code.tar.gz \
    && do-some-compilation \
    && apt-get purge -y wget \
    && cd .. \
    && rm -rf a-build-dir \
    && apt-get purge -y a-package

Sie erhalten ein viel kleineres Bild.


Eine andere Option ist das Quetschen des Bildes, nachdem Sie es erstellt haben. F: Wie funktioniert das neue docker --squash?


Eine weitere Option ist die Auswahl eines schlanken Basisimages. Zum Beispiel benötigen Images, die Alpine Linux anstelle von Debian verwenden, nur 10-15 MB anstelle von 180-250 MB. Und das ist, bevor Sie Ihre eigenen Anwendungen und Daten hinzufügen. Viele offizielle Basisbilder auf Docker Hub haben eine alpine Version.

Evgeny
quelle
3
2.37vs.1.47 GB
030
4

Wahrscheinlich keine genaue Antwort, aber es lohnt sich, Alternativen anzugeben.

Der Lebensraum des Küchenchefs wurde mit diesem Gedanken geschaffen, indem ein Paket mit allen erforderlichen Abhängigkeiten erstellt wurde, ohne dass Sie die unerwünschte externe Distribution / Basis-Image-Ladung benötigen.

Auszüge daraus, worauf es hier ankommt, die Containergröße aus diesem Blogbeitrag mit einer einfachen nodejs-App:

michael@ricardo-2:plans_pkg_part_2$ docker images
REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
mfdii/node-example   latest              36c6568c606b        40 minutes ago      655.9 MB
node                 latest              04c0ca2a8dad        16 hours ago        654.6 MB
mfdii/mytutorialapp  latest              534afd80d74d        2 minutes ago       182.1 MB

mdfii/node-exampleist ein Docker-Image aus einer klassischen Docker-Datei, während mfdii/mytutorialappdas Docker-Image mit Habitat erstellt wird.

Wenn die Größe Ihr Hauptanliegen ist und Sie die Lernkurve für Habitat-Pläne erstellen möchten, ist dies möglicherweise eine Lösung für Sie.

Tensibai
quelle
0

Man könnte auch tauchen

docker run --rm -it \
    -v /var/run/docker.sock:/var/run/docker.sock \
    wagoodman/dive:latest <dive arguments...>

um einen Bericht darüber zu erhalten, welche Abfälle aus einem Docker-Image entfernt werden könnten, um die Größe zu verringern.

030
quelle
0

Wenn Sie wiederverwendbare Entwicklungsebenen haben möchten, aber die Festplattennutzung für die Zustellung reduzieren möchten, können Sie eine zusammengeführte "Zustellungsebene" wie die folgende erstellen:

  1. Stellen Sie sicher, dass Sie einen Container haben, der Ihr Bild verwendet (wenn Sie keinen haben, verwenden Sie möglicherweise etwas docker run IMAGE echo, wenn der Befehl echo verfügbar ist).
  2. Finde die Container ID (vielleicht mit docker container ls -l)
  3. Rohr docker exportauf docker importdie zusammengefasste Ebene (so etwas wie zu erstellen docker export 20f192c6530a | docker import - project:merged)

Dadurch bleiben Ihre Entwicklungsebenen erhalten, Sie erhalten jedoch ein kleineres, zusammengeführtes Bild, das Sie liefern können.

Bill Burdick
quelle
0

Mehrstufig aufgebaut. Verwenden Sie das Image mit all Ihren Build-Komponenten, um Ihre Anwendung zu erstellen, und ein einfacheres Laufzeitimage. Kopieren Sie nur Ihr Build-Artefakt in das Laufzeit-Image. Sie müssen nichts löschen.

https://docs.docker.com/develop/develop-images/multistage-build/

frankd
quelle
0

simple .. docker ps prüft die aktuell laufenden Bilder..nachstehend finden Sie ein einfaches Dateibeispiel.

FROM ubuntu16

MAINTAINER sreeni (E-Mail / Domain)

RUN apt-get update

Führen Sie apt-get install -y nginx aus

ENTRYPOINT ["/ usr / sbin / nginx", "- g", "Daemon aus;"]

EXPOSE 80 (Port)

einfache Docker-Datei ...

Verwenden Sie den folgenden Docker-Befehl

docker run -d -p 80:80 --name web server ubuntu16 (image name) danach überprüfe localhost oder ip address: 80 (öffne browser und überprüfe)

sreeniwl
quelle
1
Bitte
korrigieren