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.