Что такое слои образа Docker?
Образы Docker состоят из нескольких слоев, которые в совокупности предоставляют контент, который вы видите в своих контейнерах. Но что такое слой и чем он отличается от полного изображения?
В этой статье вы узнаете, как различать эти два понятия и почему разница имеет значение. Хотя вы можете использовать Docker без полного понимания слоев, знание их назначения поможет вам определить возможности оптимизации.
Что такое изображение?
«Образ» Docker ведет себя как шаблон, из которого можно создавать согласованные контейнеры. Если бы Docker был традиционной виртуальной машиной, образ можно было бы сравнить с ISO-образом, используемым для установки вашей виртуальной машины. Это ненадежное сравнение, поскольку Docker отличается от виртуальных машин как с точки зрения концепции, так и с точки зрения реализации, но, тем не менее, это полезная отправная точка.
Образы определяют начальное состояние файловой системы новых контейнеров. Они объединяют исходный код вашего приложения и его зависимости в автономный пакет, который готов к использованию со средой выполнения контейнера. В образе содержимое файловой системы представлено в виде нескольких независимых слоев.
Что такое Слои?
Слои — это результат того, как создаются образы Docker. Каждый шаг в Dockerfile создает новый «слой», который, по сути, является разницей изменений файловой системы с момента последнего шага. Инструкции метаданных, такие как LABEL
и MAINTAINER
, не создают слоев, поскольку они не влияют на файловую систему.
Это изображение имеет две инструкции (COPY
и RUN
), поэтому оно создаст два слоя:
FROM ubuntu:latest
COPY foo.txt /foo.txt
RUN date > /built-on.txt
- На первом этапе
foo.txt
копируется в новый слой, основанный на образеubuntu:latest
. - Второй шаг запускает команду
date
и направляет ее вывод в файл. Это создает второй слой, основанный на предыдущем.
Создайте foo.txt
в своем рабочем каталоге:
$ echo "Hello World" > foo.txt
Теперь создайте образец изображения:
$ docker build . -t demo:latest
Sending build context to Docker daemon 2.56kB
Step 1/3 : FROM ubuntu:latest
---> df5de72bdb3b
Step 2/3 : COPY foo.txt /foo.txt
---> 4932aede6a15
Step 3/3 : RUN date > /built-on.txt
---> Running in 91d260fc2e68
Removing intermediate container 91d260fc2e68
---> 6f653c6a60fa
Successfully built 6f653c6a60fa
Successfully tagged foo:latest
Каждый шаг сборки выдает идентификатор созданного слоя. Слой последнего шага становится окончательным изображением, поэтому он помечается тегом foo:latest
.
Последовательность показывает, что слои являются действительными образами Docker. Хотя термин «слой» обычно не используется для обозначения изображения с тегами, все изображения с тегами технически являются просто слоями с назначенным идентификатором.
Вы можете запустить контейнер из образа промежуточного слоя:
$ docker run -it 4932aede6a15 sh
# cat /foo.txt
Hello World
# cat /built-on.txt
cat: /built-on.txt: No such file or directory
В этом примере контейнер запускается из слоя, созданного на втором этапе сборки. foo.txt
доступен в контейнере, но built-on.txt
не существует, поскольку он не был добавлен до третьего шага. Этот файл доступен только в файловых системах последующих слоев.
Роль слоев
Слои содержат изменения, созданные на этапе сборки, по отношению к предыдущему слою в Dockerfile. Инструкции FROM
— это особый случай, когда они ссылаются на последний слой существующего изображения.
Слои позволяют кэшировать шаги сборки, чтобы избежать избыточной работы. Docker может пропустить неизмененные инструкции в вашем файле Docker, повторно используя ранее созданный слой. Следующий шаг основывается на этом существующем слое, а не на создании нового.
Вы можете увидеть это, изменив свой Dockerfile следующим образом:
FROM ubuntu:latest
COPY foo.txt /foo.txt
RUN date +%Y-%m-%d > /built-on.txt
Изменился третий шаг сборки. Теперь перестройте свой образ:
$ docker build . -t demo:latest
Sending build context to Docker daemon 3.584kB
Step 1/3 : FROM ubuntu:latest
---> df5de72bdb3b
Step 2/3 : COPY foo.txt /foo.txt
---> Using cache
---> 4932aede6a15
Step 3/3 : RUN date +%Y-%m-%d > /built-on.txt
---> Running in 2b91ec0462c4
Removing intermediate container 2b91ec0462c4
---> c6647ff378c1
Successfully built c6647ff378c1
Successfully tagged demo:latest
Второй этап сборки отображается как Использование кеша
и создает тот же идентификатор слоя. Docker может пропустить сборку этого слоя, поскольку он уже был создан ранее, а foo.txt
не изменился с момента первой сборки.
Это кэширование работает только до момента изменения слоя. Все шаги после этого слоя также необходимо будет перестроить, чтобы они основывались на новой версии файловой системы.
Слои и операции извлечения
Еще одним преимуществом слоев является возможность частичного извлечения изображения. После того, как вы загрузите несколько изображений на свой компьютер, вы часто обнаружите, что новые извлечения могут пропускать некоторые слои, которые у вас уже есть. Этот образ содержит 13 слоев, но только шесть должны были быть загружены операцией извлечения:
docker pull php:8.0-apache
8.0-apache: Pulling from library/php
7a6db449b51b: Already exists
ad2afdb99a9d: Already exists
dbc5aa907229: Already exists
82f252ab4ad1: Already exists
bf5b34fc9894: Already exists
6161651d3d95: Already exists
cf2adf296ef1: Already exists
f0d7c5221e44: Pull complete
f647198f6316: Pull complete
c37afe1da4e5: Pull complete
09c93531cbca: Pull complete
fef371007dd3: Pull complete
52043dbb1c06: Pull complete
Digest: sha256:429889e8f9eac0a806a005b0728a004303b0d49d77b09496d39158707abd6280
Status: Downloaded newer image for php:8.0-apache
docker.io/library/php:8.0-apache
Другие слои уже присутствовали на хосте Docker, поэтому их можно было использовать повторно. Это повышает производительность и позволяет избежать потери пропускной способности сети.
Проверка слоев изображения
Вы можете просмотреть список слоев в изображении, выполнив команду docker image history
. На каждом слое отображается идентификатор созданного образа и инструкция Dockerfile, вызвавшая изменение. Вы также можете увидеть общий размер содержимого внутри слоя.
$ docker image history
IMAGE CREATED CREATED BY SIZE COMMENT
6f653c6a60fa 4 minutes ago /bin/sh -c date > /built-on.txt 29B
f8420d1a96f3 4 minutes ago /bin/sh -c #(nop) COPY file:a5630a7506b26a37... 0B
df5de72bdb3b 4 weeks ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 4 weeks ago /bin/sh -c #(nop) ADD file:396eeb65c8d737180... 77.8MB
Последний слой отображается как <missing>
, поскольку он относится к слою в базовом образе ubuntu:latest
. Это недоступно локально, так как во время сборки удаляется только последний слой базового образа (df5de72bdb3b
). Нет необходимости самостоятельно натягивать все промежуточные слои, когда вы хотите использовать конкретное изображение.
Краткое содержание
Образы и слои Docker обычно являются взаимозаменяемыми терминами. Слой — это изображение, и изображение формируется из одного или нескольких слоев. Основное различие заключается в тегах: образ будет помечен и предназначен для конечных пользователей, в то время как термин «уровень» обычно относится к непомеченным промежуточным образам, созданным как часть операции сборки. Они не видны, если вы не ищете их.
Есть еще одна тема, связанная со слоями: запущенные контейнеры добавляют дополнительный доступный для записи слой поверх своего изображения. Слои, полученные из образа контейнера, доступны только для чтения, поэтому модификации файловой системы, сделанные контейнером, нацелены на его эфемерный доступный для записи слой. Доступный для записи слой отбрасывается, когда контейнер останавливается или удаляется.