Они имеют смысл для меня на примере ...
Изучение слоев вашей сборки с помощью докера diff
Давайте возьмем надуманный пример 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
Каждая из этих dd
команд выводит файл 1M на диск. Давайте создадим образ с дополнительным флагом для сохранения временных контейнеров:
docker image build --rm=false .
В выводе вы увидите, что каждая из запущенных команд происходит во временном контейнере, который мы теперь храним вместо автоматического удаления:
...
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
Если вы запустите docker diff
на каждом из этих идентификаторов контейнеров, вы увидите, какие файлы были созданы в этих контейнерах:
$ 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
Каждая строка с префиксом - A
это добавление файла, C
указание на изменение существующего файла и D
указание на удаление.
Вот часть TL; DR
Каждая из приведенных выше версий файловой системы контейнера входит в один «слой», который собирается, когда вы запускаете образ как контейнер. Весь файл находится в каждом слое при добавлении или изменении, поэтому каждая из этих chmod
команд, несмотря на просто изменение бита разрешения, приводит к копированию всего файла на следующий слой. Удаленный / data / one файл все еще находится в предыдущих слоях, фактически 3 раза, и будет скопирован по сети и сохранен на диске при извлечении изображения.
Изучение существующих изображений
Вы можете видеть команды, которые входят в создание слоев существующего изображения с docker history
командой. Вы также можете запустить docker image inspect
на изображении и увидеть список слоев в разделе RootFS.
Вот история для вышеупомянутого изображения:
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
Самые новые слои перечислены сверху. Следует отметить, что внизу есть два довольно старых слоя. Они приходят из самого изображения busybox. Когда вы строите одно изображение, вы наследуете все слои изображения, которые вы указываете в FROM
строке. Существуют также слои, добавляемые для изменения метаданных изображения, например CMD
линии. Они практически не занимают места и предназначены для учета того, какие настройки применяются к изображению, которое вы запускаете.
Почему слои?
Слои имеют пару преимуществ. Во-первых, они неизменны. После создания этот слой, идентифицируемый хэшем sha256, никогда не изменится. Эта неизменность позволяет изображениям безопасно создавать и отделяться друг от друга. Если два файла Docker имеют одинаковый начальный набор строк и построены на одном и том же сервере, они будут использовать один и тот же набор начальных слоев, экономя дисковое пространство. Это также означает, что если вы перестраиваете изображение, и только в нескольких последних строках Dockerfile происходят изменения, нужно перестраивать только эти слои, а остальные можно повторно использовать из кэша слоев. Это может очень быстро перестроить образ докера.
Внутри контейнера вы видите файловую систему изображения, но эта файловая система не копируется. Поверх этих слоев изображения контейнер монтирует свой собственный слой файловой системы для чтения и записи. Каждое чтение файла проходит через слои до тех пор, пока не достигнет слоя, который пометил файл для удаления, не найдет копию файла в этом слое или пока не закончится чтение слоев. Каждая запись вносит изменения в определенный для контейнера слой чтения-записи.
Уменьшение раздувания слоя
Недостатком слоев является создание изображений, которые дублируют файлы или отправляют файлы, которые будут удалены в более позднем слое. Решение часто состоит в объединении нескольких команд в одну RUN
команду. В частности, когда вы изменяете существующие файлы или удаляете файлы, вы хотите, чтобы эти шаги выполнялись в той же команде, в которой они были впервые созданы. Переписанный выше Dockerfile будет выглядеть так:
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
И если сравнить полученные изображения:
- busybox: ~ 1 МБ
- первое изображение: ~ 6 МБ
- второе изображение: ~ 2 МБ
Просто соединив несколько строк в надуманном примере, мы получили то же самое результирующее содержание в нашем изображении и сократили наше изображение с 5 МБ до только 1 МБ файла, который вы видите на конечном изображении.