Co to są „warstwy” obrazu platformy Docker?

165

Jestem zupełnie nowy w Dockerze i próbuję dokładnie zrozumieć, czym jest obraz Dockera . Każda definicja obrazu Dockera używa terminu „warstwa”, ale wydaje się, że nie definiuje, co oznacza warstwa .

Z oficjalnej dokumentacji Dockera :

Widzieliśmy już, że obrazy platformy Docker są szablonami tylko do odczytu, z których uruchamiane są kontenery platformy Docker. Każdy obraz składa się z serii warstw. Docker wykorzystuje systemy plików unii do łączenia tych warstw w jeden obraz. Unijne systemy plików umożliwiają przezroczyste nakładanie plików i katalogów oddzielnych systemów plików, zwanych gałęziami, tworząc jeden spójny system plików.

Więc pytam, czym jest warstwa (dokładnie); czy ktoś może podać kilka konkretnych ich przykładów? Jak te warstwy „łączą się ze sobą”, tworząc obraz?

smeeb
źródło

Odpowiedzi:

133

Mogę się spóźnić, ale oto moje 10 centów (uzupełnienie odpowiedzi Ashishjaina):

Zasadniczo warstwa lub warstwa obrazu to zmiana na obrazie lub obraz pośredni . Każda komenda określić ( FROM, RUN, COPY, itd.) W swojej Dockerfile powoduje poprzedni obraz zmian, tworząc nową warstwę. Możesz o tym myśleć jak o zmianach przejściowych, gdy używasz git: dodajesz zmianę pliku, potem kolejny, potem kolejny ...

Rozważmy następujący plik Dockerfile:

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

Najpierw wybieramy obraz początkowy:, rails:onbuildktóry z kolei ma wiele warstw . Dodajemy kolejną warstwę na górze naszego obrazu początkowego, ustawiając zmienną środowiskową RAILS_ENVza pomocą ENVpolecenia. Następnie każemy dockerowi uruchomić bundle exec puma(co uruchamia serwer railsowy). To kolejna warstwa.

Koncepcja warstw przydaje się podczas tworzenia obrazów. Ponieważ warstwy są obrazami pośrednimi, jeśli dokonasz zmiany w pliku Dockerfile, docker utworzy tylko warstwę, która została zmieniona, i te późniejsze . Nazywa się to buforowaniem warstw.

Więcej na ten temat przeczytasz tutaj .

David Castillo
źródło
13
Jeśli zmienisz lub dodasz warstwę, Docker utworzy również warstwy, które pojawią się później, ponieważ zmiana może na nie wpłynąć.
Adam,
Dziękuję za wyjaśnienie powodu koncepcji warstw, której brakuje w innych odpowiedziach.
Seeta Somagani
@David, w powyższym przykładzie, ile warstw zostanie dodanych? 2? czy 1?
Gourav Singla
1
@GouravSingla Powinno być 2. Zmiana ENV to także zmiana. Wygląda na to, że warstwa jest zatwierdzona przez git.
PokerFace
Ostatni link ( https://labs.ctl.io/caching-docker-images/) jest uszkodzony. Czy ktoś ma sugestie dotyczące wymiany?
Johnny Utahh
72

Obraz kontenera Dockera jest tworzony przy użyciu pliku dockerfile . Każda linia w pliku dokowanym utworzy warstwę. Rozważmy następujący przykład fikcyjny:

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 

Spowoduje to utworzenie ostatecznego obrazu, w którym całkowita liczba warstw będzie wynosić X + 3

ashishjain
źródło
32
Chociaż nie głosowałem przeciw, przypuszczam, że to wyjaśnia, jak tworzyć warstwy, ale w żaden sposób nie odpowiada na pytanie, czym jest warstwa.
Lasse V. Karlsen
2
Zgadzam się z @ LasseV.Karlsen, ashishjain. Nie przegłosowałem Cię i tak naprawdę popieram Cię za to, że próbowałeś mi pomóc (więc +1) - ale aby móc dać Ci zielony czek, muszę zrozumieć, czym właściwie jest warstwa! Jeszcze raz dziękuję, kontynuuj!
smeeb
3
najlepsza odpowiedź imo. dla wielu z nas, którzy przechodzą do „korzystania z dockera”, daje nam to w skrócie sposób działania warstw.
dtc,
6
„Każda linia w pliku dokowanym utworzy warstwę” - bardzo mi się to
przydało
2
@akirekadu To nie jest cała historia. Większość linii utworzy warstwę, ale tylko instrukcje ADD, COPY lub RUN utworzą warstwy, które zwiększą rozmiar wynikowego obrazu kontenera. Powiedziałem większość wierszy, ponieważ jeśli połączysz polecenia w łańcuchy lub zmienisz znaki nowej linii za pomocą odwrotnego ukośnika, sekwencja połączonych poleceń / znaków nowej linii utworzy pojedyncze polecenie.
Scott Simontis
41

Mają dla mnie największy sens na przykładzie ...

Badanie warstw własnej kompilacji za pomocą narzędzia Docker diff

Weźmy wymyślony przykład 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

Każde z tych ddpoleceń wyprowadza plik 1M na dysk. Zbudujmy obraz z dodatkową flagą, aby zapisać tymczasowe kontenery:

docker image build --rm=false .

Na wyjściu zobaczysz, że każde z uruchomionych poleceń odbywa się w tymczasowym kontenerze, który teraz przechowujemy, zamiast automatycznie usuwać:

...
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

Jeśli uruchomisz docker diffna każdym z tych identyfikatorów kontenerów, zobaczysz, jakie pliki zostały utworzone w tych kontenerach:

$ 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

Każda linia z prefiksem Aoznacza dodanie pliku, Csymbol oznacza zmianę w istniejącym pliku, a Dsymbol oznacza usunięcie.

Oto część TL; DR

Każda z powyższych różnic w systemie plików kontenera trafia do jednej „warstwy”, która jest składana, gdy obraz jest uruchamiany jako kontener. Cały plik znajduje się w każdej warstwie, gdy następuje dodanie lub zmiana, więc każde z tych chmodpoleceń, pomimo zmiany tylko bitu uprawnień, powoduje skopiowanie całego pliku do następnej warstwy. Usunięty / dane / jeden plik nadal znajduje się na poprzednich warstwach, w rzeczywistości 3 razy, i zostanie skopiowany przez sieć i zapisany na dysku po pobraniu obrazu.

Badanie istniejących obrazów

Za pomocą polecenia można zobaczyć polecenia związane z tworzeniem warstw istniejącego obrazu docker history. Możesz także uruchomić docker image inspectna obrazie i zobaczyć listę warstw w sekcji RootFS.

Oto historia powyższego obrazu:

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

Najnowsze warstwy są wymienione u góry. Warto zauważyć, że na dole znajdują się dwie warstwy, które są dość stare. Pochodzą z samego obrazu busybox. Tworząc jeden obraz, dziedziczysz wszystkie warstwy obrazu określone w FROMlinii. Dodawane są również warstwy w celu zmiany metadanych obrazu, takich jak CMDlinia. Ledwo zajmują miejsce i służą raczej do rejestrowania ustawień dotyczących uruchomionego obrazu.

Dlaczego warstwy?

Warstwy mają kilka zalet. Po pierwsze, są niezmienne. Po utworzeniu ta warstwa zidentyfikowana przez skrót sha256 nigdy się nie zmieni. Ta niezmienność pozwala obrazom na bezpieczne budowanie i rozwidlenie się. Jeśli dwa pliki dokowane mają ten sam początkowy zestaw wierszy i są zbudowane na tym samym serwerze, będą współdzielić ten sam zestaw początkowych warstw, oszczędzając miejsce na dysku. Oznacza to również, że jeśli przebudujesz obraz, a tylko kilka ostatnich wierszy pliku Dockerfile ulegnie zmianom, tylko te warstwy będą musiały zostać odbudowane, a reszta może zostać ponownie wykorzystana z pamięci podręcznej warstw. Może to bardzo przyspieszyć odbudowę obrazów Dockera.

Wewnątrz kontenera widzisz system plików obrazu, ale ten system plików nie jest kopiowany. Oprócz tych warstw obrazu kontener montuje własną warstwę systemu plików do odczytu i zapisu. Każde odczytanie pliku przechodzi przez warstwy, aż trafi na warstwę, która oznaczyła plik do usunięcia, ma kopię pliku w tej warstwie lub podczas odczytu skończą się warstwy do przeszukania. Każdy zapis powoduje zmianę w warstwie odczytu i zapisu specyficznej dla kontenera.

Zmniejszenie wzdęcia warstwy

Jedną z wad warstw jest tworzenie obrazów, które powielają pliki lub wysyłają pliki, które są usuwane w późniejszej warstwie. Rozwiązaniem jest często połączenie wielu poleceń w jedno RUNpolecenie. Szczególnie podczas modyfikowania istniejących plików lub usuwania plików chcesz, aby te kroki były wykonywane w tym samym poleceniu, w którym zostały utworzone po raz pierwszy. Przepisanie powyższego pliku Dockerfile wyglądałoby tak:

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

A jeśli porównasz otrzymane obrazy:

  • busybox: ~ 1 MB
  • pierwszy obraz: ~ 6 MB
  • drugi obraz: ~ 2 MB

Po prostu scalając razem kilka linii w wymyślonym przykładzie, otrzymaliśmy tę samą wynikową zawartość w naszym obrazie i zmniejszyliśmy nasz obraz z 5 MB do tylko 1 MB pliku, który widzisz na ostatecznym obrazie.

BMitch
źródło
Przechodzenie przez warstwy podczas odczytywania plików wiąże się z pewnym obciążeniem, prawda? Aby zaoszczędzić na tym narzutu, czy ma sens łączenie wielu poleceń (które i tak muszą być wykonywane razem) w jednym RUN?
SergiyKolesnikov
@SergiyKolesnikov zależy od tego, ile czasu chcesz poświęcić na przedwczesną optymalizację. Ryzyko polega na spędzaniu godzin czasu programisty, gigantach dodatkowej przepustowości i pamięci masowej, aby zaoszczędzić milisekundy czasu działania. Podobnie jak w przypadku wielu rzeczy związanych z wydajnością, istnieją skrajności i istnieje potrzeba zmierzenia problemu przed włożeniem wysiłku w jego naprawę.
BMitch
19

Od czasu Dockera v1.10, wraz z wprowadzeniem magazynu adresowalnego treści, pojęcie „warstwy” stało się zupełnie inne. Warstwy nie mają pojęcia obrazu ani przynależności do obrazu, stają się jedynie zbiorami plików i katalogów, które można udostępniać między obrazami. Warstwy i obrazy zostały rozdzielone.

Na przykład, na lokalnie zbudowany wizerunek z obrazu bazowego, powiedzmy, ubuntu:14.04The docker historykomenda daje łańcuch obrazu, ale niektóre z identyfikatorami obraz zostanie pokazany jako „brakujący”, ponieważ historia build nie jest już załadowany. Warstwy, które składają się na te obrazy, można znaleźć pod adresem

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

Zawartość warstwy jest przechowywana, /var/lib/docker/aufs/diffjeśli wybrano sterownik pamięci masowej aufs. Ale warstwy są nazywane z losowo generowanym identyfikatorem pamięci podręcznej, wydaje się, że połączenie między warstwą a jej identyfikatorem pamięci podręcznej jest znane tylko silnikowi Docker ze względów bezpieczeństwa. Wciąż szukam sposobu, aby się tego dowiedzieć

  1. Odpowiednia relacja między obrazem a jego warstwą (warstwami) tworzącymi
  2. Rzeczywista lokalizacja i rozmiar warstwy na dysku

Ten blog dostarczył wielu informacji.

Ruifeng Ma
źródło
W tym wpisie SO zamieściłem raczej naiwny sposób odpowiedzi na dwa pytania, które zamieściłem.
Ruifeng Ma
13

Zgodnie ze specyfikacją obrazu Dockera za pośrednictwem The Moby Project :

Obrazy składają się z warstw. Każda warstwa to zbiór zmian w systemie plików. Warstwy nie mają metadanych konfiguracyjnych, takich jak zmienne środowiskowe lub domyślne argumenty - są to właściwości obrazu jako całości, a nie określonej warstwy.

Zasadniczo warstwa to po prostu zestaw zmian wprowadzonych w systemie plików.

Aditya Patawari
źródło
Znalezienie go zajęło mi tylko kilka godzin, ale dzięki tej elegancko-prostej odpowiedzi w końcu rozumiem, czym jest warstwa: "Each [Docker] layer is a set of filesystem changes."(Zakładając, że to prawda). Z jakiegoś powodu nie zrozumiałem tego fundamentalnego punktu, czytając wiele innych dokumentów / blogi / pytania + odpowiedzi / itp. i podejrzewam, że ograniczenie było ich, a nie moje. Niezależnie od tego, brawo Aditya za dotarcie do sedna sprawy.
Johnny Utahh
12

Myślę, że oficjalny dokument zawiera dość szczegółowe wyjaśnienie: https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/ .


(źródło: docker.com )

Obraz składa się z wielu warstw, które zwykle są generowane z Dockerfile, każda linia w Dockerfile tworzy nową warstwę, a wynikiem jest obraz, który jest oznaczony przez formularz repo:tag, np ubuntu:15.04.

Aby uzyskać więcej informacji, przeczytaj powyższe oficjalne dokumenty.

cizixs
źródło
2

Dziękuję @David Castillo za przydatne informacje . Myślę, że warstwa jest binarną zmianą lub instrukcją obrazu, którą można łatwo wykonać lub cofnąć. Są wykonywane krok po kroku, czyli tak samo, jak warstwa na warstwie, więc nazwaliśmy „warstwą”.

Aby uzyskać więcej informacji, możesz zobaczyć „historię Dockera” w następujący sposób:

obrazy docker - drzewo
Ostrzeżenie: „--tree” jest przestarzałe i wkrótce zostanie usunięte. Zobacz użycie.
└─511136ea3c5a Rozmiar wirtualny: 0 B Tagi: scratch: najnowsze
  └─59e359cb35ef Rozmiar wirtualny: 85,18 MB
    └─e8d37d9e3476 Rozmiar wirtualny: 85,18 MB Tagi: debian: wheezy
      └─c58b36b8f285 Rozmiar wirtualny: 85,18 MB
        └─90ea6e05b074 Rozmiar wirtualny: 118,6 MB
          └─5dc74cffc471 Rozmiar wirtualny: 118,6 MB Tagi: vim: najnowsze

hiproz
źródło
5
znalazł nowe informacje o warstwach : kiedy Docker montuje rootfs, uruchamia się w trybie tylko do odczytu, tak jak w tradycyjnym rozruchu Linuksa, ale potem zamiast zmienić system plików na tryb do odczytu i zapisu, korzysta z połączenia montowania do dodania system plików do odczytu i zapisu w systemie plików tylko do odczytu. W rzeczywistości może istnieć wiele systemów plików tylko do odczytu ułożonych jeden na drugim. Myślimy o każdym z tych systemów plików jako o warstwie .
hiproz
1

Osobiście rozumiem, że możemy porównać warstwę dockera z zatwierdzeniem na githubie. Dla twojego podstawowego obrazu (twojego nowego głównego repozytorium), wykonujesz kilka zatwierdzeń, każde zatwierdzenie zmienia twój status główny, to samo w dockerze, każda warstwa wykonuje jakąś operację w oparciu o poprzednią warstwę pośrednią. A potem ta warstwa staje się nową warstwą pośrednią do następnej warstwy.

KevinZhou
źródło
0

Kiedyś myślałem, że są jak różnice na poprzednich warstwach. Po przeczytaniu niektórych odpowiedzi tutaj nie byłem taki pewien; są one opisywane jako zestawy zmian w systemie plików . Napisałem kilka plików Dockerfile, aby pokazać, że są bardziej podobne do różnic, tj. Naprawdę zależą od poprzednich warstw.

Biorąc pod uwagę te dwa pliki 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

i

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

można by oczekiwać tego samego zestawu warstw, gdyby dotyczyły tylko zmian w systemie plików, ale tak nie jest:

$ 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...>

i

$ 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...>

Możesz zobaczyć, jak, nawet jeśli zmiany w systemie plików są takie same w obu przypadkach, kolejność ma znaczenie.

olepinto
źródło