Что такое «слои» изображений Docker?


165

Я новичок в Docker и пытаюсь точно понять, что такое образ Docker . Каждое определение образа Docker использует термин «слой», но, похоже, не определяет, что подразумевается под слоем .

Из официальных документов Docker :

Мы уже видели, что образы Docker - это шаблоны только для чтения, из которых запускаются контейнеры Docker. Каждое изображение состоит из серии слоев. Docker использует объединенные файловые системы для объединения этих слоев в одно изображение. Объединенные файловые системы позволяют прозрачно накладывать файлы и каталоги отдельных файловых систем, называемых ветвями, образуя единую согласованную файловую систему.

Поэтому я спрашиваю, что такое слой (точно); Кто-нибудь может привести несколько конкретных примеров из них? И как эти слои «слипаются», образуя изображение?

Ответы:


133

Я мог бы опоздать, но вот мои 10 центов (в дополнение к ответу Ашишджайна):

По сути, слой или слой изображения - это изменение изображения или промежуточного изображения . Каждая команда указывается ( FROM, RUN, COPYи т.д.) в вашем Dockerfile вызывает предыдущее изображение изменения, создавая тем самым новый слой. Вы можете думать об этом как о внесении изменений при использовании git: вы добавляете изменение файла, затем еще одно, затем еще одно ...

Рассмотрим следующий Dockerfile:

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

Сначала мы выбираем начальное изображение: оно rails:onbuild, в свою очередь, имеет много слоев . Мы добавляем еще один слой поверх нашего начального изображения, устанавливая переменную окружения RAILS_ENVс помощью ENVкоманды. Затем мы говорим Docker для запуска bundle exec puma(который загружает сервер rails). Это еще один слой.

Концепция слоев пригодится во время построения изображений. Поскольку слои являются промежуточными изображениями, если вы внесете изменения в свой файл Dockerfile, Docker создаст только слой, который был изменен, и те слои, которые были после этого. Это называется кешированием слоев.

Вы можете прочитать больше об этом здесь .


13
Если вы измените или добавите слой, Docker также создаст все слои, которые появятся позже, потому что изменение может повлиять на них.
Адам

Спасибо за объяснение причины концепции слоев, которая отсутствует в других ответах.
Сета Сомагани

@ Давид, в приведенном выше примере, сколько слоев будет добавлено? 2? или 1?
Гурав Сингла

1
@GouravSingla Должно быть 2. Изменение ENV также является изменением. Похоже, что слой это коммит мерзавца.
PokerFace

Последняя веб-ссылка ( https://labs.ctl.io/caching-docker-images/) не работает. У кого-нибудь есть предложения по замене?
Джонни Юта

72

Образ контейнера Docker создается с использованием файла Docker . Каждая строка в файле Docker создаст слой. Рассмотрим следующий фиктивный пример:

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 

Это создаст окончательное изображение, где общее количество слоев будет Х + 3


32
Хотя я не понизил голосование, я думаю, что это объясняет, как создавать слои, но никоим образом не отвечает на вопрос о том, что такое слой.
Лассе В. Карлсен

2
Я согласен с @ LasseV.Karlsen, ashishjain. Я не отрицал вас и фактически запрещаю вам пытаться помочь мне (так что +1) - но для того, чтобы я мог дать вам зеленый чек, мне нужно понять, что такое слой на самом деле! Еще раз спасибо, продолжайте!
Смеб

3
лучший ответ имо. для многих из нас, кто переходит к «использованию докера», это дает нам представление о том, как работают слои.
DTC

6
«Каждая строка в докер-файле создаст слой» - мне было очень полезно это знать
akirekadu

2
@akirekadu Это не полная история. Большинство линий создают слой, но только инструкции ADD, COPY или RUN создают слои, которые увеличивают размер получающегося изображения контейнера. Я сказал большинство строк, потому что, если вы объединяете команды в цепочку или экранируете символы новой строки с помощью обратной косой черты, последовательность связанных команд / экранированные символы новой строки образуют одну команду.
Скотт Симонтис

41

Они имеют смысл для меня на примере ...

Изучение слоев вашей сборки с помощью докера 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 МБ файла, который вы видите на конечном изображении.


Обход слоев во время чтения файла влечет за собой некоторые накладные расходы, верно? Чтобы сэкономить эти накладные расходы, имеет ли смысл объединять несколько команд (которые в любом случае должны выполняться вместе) в одном RUN?
Сергей Колесников

@SergiyKolesnikov зависит от того, сколько времени вы хотите потратить на преждевременную оптимизацию. Риск - это тратить часы на разработку, гигабайты дополнительной пропускной способности и хранилища, чтобы сэкономить миллисекунды времени выполнения. Как и во многих аспектах, связанных с производительностью, существуют крайности, и необходимо измерить проблему, прежде чем прилагать усилия для ее устранения.
BMitch

19

Начиная с Docker v1.10, с введением адресного хранилища контента, понятие «уровень» стало совсем другим. Слои не имеют представления об изображении или принадлежности к изображению, они становятся просто коллекциями файлов и каталогов, которые могут совместно использоваться изображениями. Слои и изображения стали разделенными.

Например, на местном уровне , построенное изображение из базового изображения, скажем, ubuntu:14.04, то docker historyкоманда дает цепочку изображения, но некоторые из идентификаторов изображений , будут показаны как «не хватает» , потому что история сборки больше не загружаются. И слои, которые составляют эти изображения, можно найти через

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

Содержимое слоя сохраняется при /var/lib/docker/aufs/diffвыборе драйвера хранилища aufs. Но имена слоев имеют случайно сгенерированный идентификатор кэша, кажется, что связь между уровнем и его идентификатором кэша известна только Docker Engine по соображениям безопасности. Я все еще ищу способ узнать

  1. Соответствующее отношение между изображением и его составным слоем (слоями)
  2. Фактическое расположение и размер слоя на диске

Этот блог обеспечил много понимания.


В этой записи SO я опубликовал довольно наивный способ ответить на два вопроса, которые я разместил.
Ruifeng Ma

13

Спецификация имиджа Per Docker через The Moby Project :

Изображения состоят из слоев. Каждый слой представляет собой набор изменений файловой системы. Слои не имеют метаданных конфигурации, таких как переменные среды или аргументы по умолчанию - это свойства изображения в целом, а не какого-либо конкретного слоя.

Итак, по сути, слой - это просто набор изменений, внесенных в файловую систему.


Мне потребовалось всего несколько часов, чтобы найти его, но благодаря этому элегантно простому ответу я, наконец, понял, что такое слой: "Each [Docker] layer is a set of filesystem changes."(Предполагая, что это правда.) По какой-то причине я не понял этого фундаментального вопроса при чтении многочисленных других документов / блоги / Q + A's / etc, и я подозреваю, что ограничение было их, а не мое. Несмотря ни на что, браво Адитья за то, что добрался до сути дела.
Джонни Юта

12

Я думаю, что официальный документ дает довольно подробное объяснение: https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/ .


(источник: docker.com )

Изображение состоит из множества слоев, которые обычно создаются из Dockerfile, каждая строка в Dockerfile создает новый слой, и в результате получается изображение, которое обозначается формой repo:tag, например ubuntu:15.04.

Для получения дополнительной информации, пожалуйста, прочитайте официальные документы выше.


2

Спасибо @David Castillo за полезную информацию . Я думаю, что слой - это какое-то двоичное изменение или инструкция изображения, которую можно легко сделать или отменить. Они делаются шаг за шагом, то же самое, что слой на слое, поэтому мы назвали «слой».

Для получения дополнительной информации вы можете увидеть «историю докеров» следующим образом:

докер изображения - дерево
Предупреждение: «--tree» устарело, оно скоро будет удалено. Смотрите использование.
└─511136ea3c5a Виртуальный размер: 0 B Тэги: нуля: последние
  59─59e359cb35ef Виртуальный размер: 85,18 МБ
    └─e8d37d9e3476 Виртуальный размер: 85,18 МБ Метки: debian: wheezy
      └─c58b36b8f285 Виртуальный размер: 85,18 МБ
        └─90ea6e05b074 Виртуальный размер: 118,6 МБ
          └─5dc74cffc471 Виртуальный размер: 118,6 MB Тэги: vim: последние


5
нашел новую информацию о слоях : когда Docker монтирует rootfs, он начинает работать только для чтения, как при традиционной загрузке Linux, но затем, вместо того, чтобы перевести файловую систему в режим чтения-записи, он использует преимущество монтирования объединения для добавления файловая система для чтения и записи поверх файловой системы только для чтения. На самом деле может быть несколько файловых систем только для чтения, расположенных друг над другом. Мы думаем о каждой из этих файловых систем как о слое .
hiproz

1

Мое личное понимание состоит в том, что мы можем сравнивать слой докера с коммитом github. Для вашего базового образа (вашего нового главного репо) вы делаете несколько коммитов, каждый коммит меняет ваш главный статус, то же самое в докере, каждый слой выполняет некоторую операцию на основе предыдущего промежуточного слоя. И затем этот слой становится новым промежуточным слоем для следующего слоя.


0

Раньше я думал, что они похожи на различия в предыдущих слоях. Прочитав некоторые ответы здесь, я не был так уверен; они описаны как наборы изменений в файловой системе . Я написал несколько файлов Docker, чтобы показать, что они больше похожи на diff, то есть они действительно зависят от предыдущих слоев.

Учитывая эти два 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

и

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

можно было бы ожидать того же набора слоев, если бы они только что изменили файловую систему, но это не так:

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

и

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

Вы можете видеть, как, даже если изменения в файловой системе одинаковы в обоих случаях, порядок имеет значение.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.