Was sind Docker-Bildebenen?

165

Ich bin brandneu bei Docker und versuche genau zu verstehen, was ein Docker- Image ist. Jede einzelne Definition eines Docker-Bildes verwendet den Begriff "Ebene", scheint jedoch nicht zu definieren, was unter Ebene zu verstehen ist .

Aus den offiziellen Docker-Dokumenten :

Wir haben bereits gesehen, dass Docker-Images schreibgeschützte Vorlagen sind, aus denen Docker-Container gestartet werden. Jedes Bild besteht aus einer Reihe von Ebenen. Docker verwendet Union-Dateisysteme, um diese Ebenen zu einem einzigen Bild zu kombinieren. Mit Union-Dateisystemen können Dateien und Verzeichnisse separater Dateisysteme, sogenannte Zweige, transparent überlagert werden und bilden ein einziges zusammenhängendes Dateisystem.

Also frage ich, was ist eine Schicht (genau); Kann jemand ein paar konkrete Beispiele dafür nennen? Und wie "schnappen" diese Ebenen zusammen, um ein Bild zu bilden?

smeeb
quelle

Antworten:

132

Ich könnte zu spät kommen, aber hier sind meine 10 Cent (als Ergänzung zu Ashishjains Antwort):

Grundsätzlich ist eine Ebene oder Bildebene eine Änderung an einem Bild oder einem Zwischenbild . Jeder Befehl , der Du angeben ( FROM, RUN, COPY, usw.) in Ihrem Dockerfile bewirkt , dass das vorherige Bild zu ändern, damit eine neue Ebene zu schaffen. Sie können sich das als Staging von Änderungen vorstellen, wenn Sie git verwenden: Sie fügen die Änderung einer Datei hinzu, dann eine weitere, dann eine weitere ...

Betrachten Sie die folgende Docker-Datei:

FROM rails:onbuild
ENV RAILS_ENV production
ENTRYPOINT ["bundle", "exec", "puma"]

Zuerst wählen wir ein Startbild:, rails:onbuilddas wiederum viele Ebenen hat . Wir fügen eine weitere Ebene über unserem Startbild hinzu und setzen die Umgebungsvariable RAILS_ENVmit dem ENVBefehl. Dann weisen wir Docker an, ausgeführt zu werden bundle exec puma(wodurch der Rails-Server hochgefahren wird). Das ist eine andere Schicht.

Das Konzept der Ebenen ist zum Zeitpunkt der Erstellung von Bildern nützlich. Da Ebenen Zwischenbilder sind, erstellt Docker bei einer Änderung an Ihrer Docker-Datei nur die Ebene, die geändert wurde, und die danach. Dies wird als Layer-Caching bezeichnet.

Sie können mehr darüber lesen Sie hier .

David Castillo
quelle
13
Wenn Sie eine Ebene ändern oder hinzufügen, erstellt Docker auch alle nachfolgenden Ebenen, da diese möglicherweise von der Änderung betroffen sind.
Adam
Vielen Dank, dass Sie den Grund für das Konzept der Ebenen erläutert haben, das in den anderen Antworten fehlt.
Seeta Somagani
@ David, im obigen Beispiel, wie viele Ebenen werden hinzugefügt? 2? oder 1?
Gourav Singla
1
@GouravSingla Es sollte 2 sein. Änderung ENV ist auch eine Änderung. Es sieht so aus, als ob die Ebene das Commit von Git ist.
PokerFace
Der letzte Weblink ( https://labs.ctl.io/caching-docker-images/) ist kaputt. Hat jemand Vorschläge für einen Ersatz?
Johnny Utahh
72

Ein Docker-Container-Image wird mithilfe einer Docker-Datei erstellt . Jede Zeile in einer Docker-Datei erstellt eine Ebene. Betrachten Sie das folgende Dummy-Beispiel:

FROM ubuntu             #This has its own number of layers say "X"
MAINTAINER FOO          #This is one layer 
RUN mkdir /tmp/foo      #This is one layer 
RUN apt-get install vim #This is one layer 

Dadurch wird ein endgültiges Bild erstellt, in dem die Gesamtzahl der Ebenen X + 3 beträgt

Ashishjain
quelle
32
Während ich nicht downvote, wäre meine Vermutung , dass dies erklärt , wie schafft die Schichten, aber auf gar keine Antwort Art und Weise die Frage nach , was eine Schicht ist.
Lasse V. Karlsen
2
Ich stimme @ LasseV.Karlsen, ashishjain, zu. Ich habe dich nicht herabgestimmt und habe dich tatsächlich dafür gestimmt, dass du versucht hast, mir zu helfen (also +1) - aber damit ich dir den grünen Scheck geben kann, muss ich verstehen, was eine Schicht eigentlich ist! Nochmals vielen Dank, mach weiter!
Smeeb
3
beste Antwort imo. Für viele von uns, die "Docker verwenden", gibt es einen Überblick darüber, wie die Ebenen funktionieren.
dtc
6
"Jede Zeile in einer Docker-Datei wird eine Ebene erstellen" - das war sehr hilfreich für mich zu wissen
Akirekadu
2
@akirekadu Das ist nicht die ganze Geschichte. Die meisten Zeilen erstellen eine Ebene, aber nur die Anweisungen HINZUFÜGEN, KOPIEREN oder AUSFÜHREN erstellen Ebenen, die das resultierende Containerbild vergrößern. Ich sagte die meisten Zeilen, denn wenn Sie Befehle verketten oder Zeilenumbrüche mit einem Backslash maskieren, bildet die Folge verketteter Befehle / Zeilenumbrüche einen einzelnen Befehl.
Scott Simontis
41

Sie machen für mich mit einem Beispiel am meisten Sinn ...

Untersuchen von Ebenen Ihres eigenen Builds mit Docker Diff

Nehmen wir ein erfundenes Beispiel Dockerfile:

FROM busybox

RUN mkdir /data
# imagine this is downloading source code
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/one 
RUN chmod -R 0777 /data
# imagine this is compiling the app
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/two 
RUN chmod -R 0777 /data
# and now this cleans up that downloaded source code
RUN rm /data/one 

CMD ls -alh /data

Jeder dieser ddBefehle gibt eine 1M-Datei auf der Festplatte aus. Erstellen Sie das Image mit einem zusätzlichen Flag, um die temporären Container zu speichern:

docker image build --rm=false .

In der Ausgabe sehen Sie, dass jeder der ausgeführten Befehle in einem temporären Container ausgeführt wird, den wir jetzt behalten, anstatt ihn automatisch zu löschen:

...
Step 2/7 : RUN mkdir /data
 ---> Running in 04c5fa1360b0
 ---> 9b4368667b8c
Step 3/7 : RUN dd if=/dev/zero bs=1024 count=1024 of=/data/one
 ---> Running in f1b72db3bfaa
1024+0 records in
1024+0 records out
1048576 bytes (1.0MB) copied, 0.006002 seconds, 166.6MB/s
 ---> ea2506fc6e11

Wenn Sie docker difffür jede dieser Container-IDs eine ausführen , sehen Sie, welche Dateien in diesen Containern erstellt wurden:

$ docker diff 04c5fa1360b0  # mkdir /data
A /data
$ docker diff f1b72db3bfaa  # dd if=/dev/zero bs=1024 count=1024 of=/data/one
C /data
A /data/one
$ docker diff 81c607555a7d  # chmod -R 0777 /data
C /data
C /data/one
$ docker diff 1bd249e1a47b  # dd if=/dev/zero bs=1024 count=1024 of=/data/two
C /data
A /data/two
$ docker diff 038bd2bc5aea  # chmod -R 0777 /data
C /data/one
C /data/two
$ docker diff 504c6e9b6637  # rm /data/one
C /data
D /data/one

Jede Zeile mit dem Präfix a Afügt die Datei hinzu, das Czeigt eine Änderung an einer vorhandenen Datei an und das Dzeigt ein Löschen an.

Hier ist der TL; DR-Teil

Jedes der oben genannten Unterschiede in diesem Container-Dateisystem besteht aus einer "Ebene", die zusammengestellt wird, wenn Sie das Image als Container ausführen. Die gesamte Datei befindet sich in jeder Ebene, wenn hinzugefügt oder geändert wird. Daher führt jeder dieser chmodBefehle, obwohl nur ein Berechtigungsbit geändert wird, dazu, dass die gesamte Datei in die nächste Ebene kopiert wird. Die gelöschte / data / one-Datei befindet sich noch dreimal in den vorherigen Ebenen und wird über das Netzwerk kopiert und beim Abrufen des Bildes auf der Festplatte gespeichert.

Bestehende Bilder untersuchen

Sie können die Befehle sehen, mit denen die Ebenen eines vorhandenen Bildes mit dem docker historyBefehl erstellt werden. Sie können auch docker image inspectein Bild ausführen und die Liste der Ebenen im Abschnitt RootFS anzeigen.

Hier ist die Geschichte für das obige Bild:

IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
a81cfb93008c        4 seconds ago       /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "ls -…   0B
f36265598aef        5 seconds ago       /bin/sh -c rm /data/one                         0B
c79aff033b1c        7 seconds ago       /bin/sh -c chmod -R 0777 /data                  2.1MB
b821dfe9ea38        10 seconds ago      /bin/sh -c dd if=/dev/zero bs=1024 count=102…   1.05MB
a5602b8e8c69        13 seconds ago      /bin/sh -c chmod -R 0777 /data                  1.05MB
08ec3c707b11        15 seconds ago      /bin/sh -c dd if=/dev/zero bs=1024 count=102…   1.05MB
ed27832cb6c7        18 seconds ago      /bin/sh -c mkdir /data                          0B
22c2dd5ee85d        2 weeks ago         /bin/sh -c #(nop)  CMD ["sh"]                   0B
<missing>           2 weeks ago         /bin/sh -c #(nop) ADD file:2a4c44bdcb743a52f…   1.16MB

Die neuesten Ebenen sind oben aufgeführt. Bemerkenswert ist, dass es unten zwei Schichten gibt, die ziemlich alt sind. Sie stammen aus dem Busybox-Image selbst. Wenn Sie ein Bild erstellen, erben Sie alle Ebenen des Bildes, die Sie in der FROMZeile angeben . Es werden auch Ebenen für Änderungen an den Bild-Metadaten hinzugefügt, z. B. die CMDLinie. Sie nehmen kaum Platz ein und dienen eher dazu, aufzuzeichnen, welche Einstellungen für das von Ihnen ausgeführte Bild gelten.

Warum Schichten?

Die Schichten haben einige Vorteile. Erstens sind sie unveränderlich. Einmal erstellt, wird sich diese durch einen sha256-Hash identifizierte Ebene niemals ändern. Diese Unveränderlichkeit ermöglicht es Bildern, sich sicher aufzubauen und voneinander zu trennen. Wenn zwei Docker-Dateien denselben Anfangssatz von Zeilen haben und auf demselben Server erstellt werden, teilen sie denselben Satz von Anfangsebenen, wodurch Speicherplatz gespart wird. Das bedeutet auch, dass beim erneuten Erstellen eines Images, bei dem nur die letzten Zeilen der Docker-Datei Änderungen erfahren, nur diese Ebenen neu erstellt werden müssen und der Rest aus dem Ebenen-Cache wiederverwendet werden kann. Dadurch können Docker-Images sehr schnell neu erstellt werden.

In einem Container sehen Sie das Image-Dateisystem, aber dieses Dateisystem wird nicht kopiert. Über diesen Bildebenen stellt der Container seine eigene Lese- / Schreib-Dateisystemebene bereit. Jeder Lesevorgang einer Datei durchläuft die Ebenen, bis sie auf eine Ebene trifft, die die Datei zum Löschen markiert hat, eine Kopie der Datei in dieser Ebene hat oder dem Lesevorgang die Ebenen zum Durchsuchen ausgehen. Bei jedem Schreibvorgang wird eine Änderung in der container-spezifischen Lese- / Schreibschicht vorgenommen.

Reduzierung des Aufblähens der Schicht

Ein Nachteil der Ebenen ist das Erstellen von Bildern, die Dateien duplizieren oder Dateien versenden, die in einer späteren Ebene gelöscht werden. Die Lösung besteht häufig darin, mehrere Befehle zu einem einzigen RUNBefehl zusammenzuführen. Insbesondere wenn Sie vorhandene Dateien ändern oder Dateien löschen, möchten Sie, dass diese Schritte in demselben Befehl ausgeführt werden, in dem sie zuerst erstellt wurden. Eine Neufassung der obigen Docker-Datei würde folgendermaßen aussehen:

FROM busybox

RUN mkdir /data \
 && dd if=/dev/zero bs=1024 count=1024 of=/data/one \
 && chmod -R 0777 /data \
 && dd if=/dev/zero bs=1024 count=1024 of=/data/two \
 && chmod -R 0777 /data \
 && rm /data/one

CMD ls -alh /data

Und wenn Sie die resultierenden Bilder vergleichen:

  • Busybox: ~ 1 MB
  • erstes Bild: ~ 6MB
  • zweites Bild: ~ 2 MB

Durch einfaches Zusammenführen einiger Zeilen im erfundenen Beispiel haben wir den gleichen resultierenden Inhalt in unserem Bild erhalten und unser Bild von 5 MB auf nur die 1 MB-Datei verkleinert, die Sie im endgültigen Bild sehen.

BMitch
quelle
Das Durchlaufen der Ebenen beim Lesen von Dateien ist mit einem gewissen Aufwand verbunden, oder? Ist es sinnvoll, mehrere Befehle (die sowieso zusammen ausgeführt werden müssen) in einem RUN zu kombinieren, um diesen Overhead zu sparen?
SergiyKolesnikov
@SergiyKolesnikov hängt davon ab, wie viel Zeit Sie für die vorzeitige Optimierung verwenden möchten. Das Risiko besteht darin, Stunden Entwicklerzeit, Giganten zusätzlicher Bandbreite und Speicherplatz aufzuwenden, um Millisekunden Laufzeit zu sparen. Wie bei vielen leistungsbezogenen Dingen gibt es Extreme, und es ist erforderlich, das Problem zu messen, bevor Anstrengungen unternommen werden, um es zu beheben.
BMitch
19

Seit Docker v1.10, mit der Einführung des inhaltsadressierbaren Speichers, wurde der Begriff "Ebene" ganz anders. Ebenen haben keine Vorstellung von einem Bild oder gehören zu einem Bild. Sie werden lediglich zu Sammlungen von Dateien und Verzeichnissen, die von mehreren Bildern gemeinsam genutzt werden können. Ebenen und Bilder wurden getrennt.

Beispiel: Bei einem lokal erstellten Image aus einem Basis-Image gibt ubuntu:14.04der docker historyBefehl beispielsweise die Image-Kette aus. Einige der Image-IDs werden jedoch als "fehlend" angezeigt, da der Build-Verlauf nicht mehr geladen wird. Und die Ebenen, aus denen diese Bilder bestehen, können über gefunden werden

docker inspect <image_id> | jq -r '.[].RootFS'

Der Ebeneninhalt wird gespeichert, /var/lib/docker/aufs/diffwenn die Auswahl des Speichertreibers lautet aufs. Die Layer werden jedoch mit einer zufällig generierten Cache-ID benannt. Die Verbindung zwischen einem Layer und seiner Cache-ID scheint Docker Engine nur aus Sicherheitsgründen bekannt zu sein. Ich bin immer noch auf der Suche nach einem Weg, es herauszufinden

  1. Die entsprechende Beziehung zwischen einem Bild und seinen zusammensetzenden Schichten.
  2. Tatsächliche Position und Größe einer Ebene auf der Festplatte

Dieser Blog lieferte viele Einblicke.

Ruifeng Ma
quelle
In diesem SO-Eintrag habe ich eine ziemlich naive Art der Beantwortung der beiden Fragen gepostet, die ich gepostet habe.
Ruifeng Ma
13

Per Docker- Bildspezifikation über The Moby Project :

Bilder bestehen aus Ebenen. Jede Ebene besteht aus einer Reihe von Dateisystemänderungen. Ebenen haben keine Konfigurationsmetadaten wie Umgebungsvariablen oder Standardargumente. Dies sind Eigenschaften des gesamten Bildes und keine bestimmte Ebene.

Eine Ebene besteht also im Wesentlichen nur aus einer Reihe von Änderungen, die am Dateisystem vorgenommen wurden.

Aditya Patawari
quelle
Ich habe nur ein paar Stunden gebraucht, um es zu finden, aber mit dieser elegant-einfachen Antwort verstehe ich endlich, was eine Ebene ist: "Each [Docker] layer is a set of filesystem changes."(Vorausgesetzt, dies ist wahr.) Aus irgendeinem Grund habe ich diesen grundlegenden Punkt beim Lesen zahlreicher anderer Dokumente nicht verstanden. Blogs / Q + A's / etc, und ich vermute, die Einschränkung lag bei ihnen und nicht bei mir. Unabhängig davon, Bravo Aditya, um die Sache auf den Punkt zu bringen.
Johnny Utahh
12

Ich denke, das offizielle Dokument enthält eine ziemlich detaillierte Erklärung: https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/ .


(Quelle: docker.com )

Ein Bild besteht aus vielen Ebenen, die normalerweise aus Dockerfile generiert werden. Jede Zeile in Dockerfile erstellt eine neue Ebene, und das Ergebnis ist ein Bild, das durch das Formular repo:tagwie gekennzeichnet ist ubuntu:15.04.

Weitere Informationen finden Sie in den offiziellen Dokumenten oben.

Cizixs
quelle
2

Vielen Dank an David Castillo für die nützlichen Informationen . Ich denke, die Ebene ist eine binäre Änderung oder Anweisung eines Bildes, die leicht durchgeführt oder rückgängig gemacht werden kann. Sie werden Schritt für Schritt ausgeführt, was einer Ebene auf einer Ebene entspricht, daher haben wir "Ebene" genannt.

Weitere Informationen finden Sie in der "Docker-Historie" wie folgt:

Docker-Bilder - Baum
Warnung: '--tree' ist veraltet und wird bald entfernt. Siehe Verwendung.
└─511136ea3c5a Virtuelle Größe: 0 B Tags: Scratch: Neueste
  └─59e359cb35ef Virtuelle Größe: 85,18 MB
    └─e8d37d9e3476 Virtuelle Größe: 85,18 MB Tags: debian: wheezy
      └─c58b36b8f285 Virtuelle Größe: 85,18 MB
        Ea─90ea6e05b074 Virtuelle Größe: 118,6 MB
          └─5dc74cffc471 Virtuelle Größe: 118,6 MB Tags: vim: latest

hiproz
quelle
5
Es wurden neue Informationen zu Layern gefunden : Wenn Docker die Rootfs bereitstellt, wird es schreibgeschützt gestartet, wie bei einem herkömmlichen Linux-Start. Statt das Dateisystem in den Lese- / Schreibmodus zu ändern, wird ein Union Mount zum Hinzufügen verwendet ein Lese- / Schreib-Dateisystem über das schreibgeschützte Dateisystem. Tatsächlich können mehrere schreibgeschützte Dateisysteme übereinander gestapelt sein. Wir betrachten jedes dieser Dateisysteme als eine Schicht .
Hiproz
1

Mein persönliches Verständnis ist, dass wir die Docker-Ebene mit dem Github-Commit vergleichen können. Für Ihr Basis-Image (Ihr neues Master-Repo) führen Sie mehrere Commits durch, jedes Commit ändert Ihren Master-Status, es ist im Docker gleich, jede Ebene führt eine Operation basierend auf der vorherigen Zwischenebene aus. Und dann wird diese Schicht eine neue Zwischenschicht zur nächsten Schicht.

KevinZhou
quelle
0

Früher dachte ich, sie sind wie Unterschiede in früheren Schichten. Nachdem ich einige der Antworten hier gelesen hatte, war ich mir nicht so sicher; Sie werden als Änderungssätze am Dateisystem beschrieben . Ich habe einige Dockerfiles geschrieben, um zu zeigen, dass sie eher Diffs sind, dh sie hängen wirklich von vorherigen Ebenen ab.

Angesichts dieser beiden Dockerfiles

FROM bash
RUN mkdir /data
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/one
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/two
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/three

und

FROM bash
RUN mkdir /data
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/three
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/two
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/one

Man würde den gleichen Satz von Ebenen erwarten, wenn es nur um Änderungen am Dateisystem geht, aber dies ist nicht der Fall:

$ docker history img_1
IMAGE               CREATED             CREATED BY                                      SIZE
30daa166a9c5        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102…   1.05MB
4467d16e79f5        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102…   1.05MB
c299561fd031        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102…   1.05MB
646feb178431        6 minutes ago       /bin/sh -c mkdir /data                          0B
78664daf24f4        2 weeks ago         /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B
<more missing...>

und

$ docker history img_2
IMAGE               CREATED             CREATED BY                                      SIZE
f55c91305f8c        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102…   1.05MB
29b3b627c76f        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102…   1.05MB
18360be603aa        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102…   1.05MB
646feb178431        6 minutes ago       /bin/sh -c mkdir /data                          0B
78664daf24f4        2 weeks ago         /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B
<more missing...>

Sie können sehen, wie wichtig die Reihenfolge ist , auch wenn die Änderungen am Dateisystem in beiden Fällen gleich sind.

Olepinto
quelle