Каждый Docker-образ состоит из слоёв (layers), каждый из которых описывает какую-то инструкцию. Далее – Docker объединяет информацию из каждого слоя, и создает шаблон-образ, из которого запускается контерйнер, в котором выполняются инструкции из каждого слоя, который был включен в данный образ.
Для дальнейших примеров – возьмем образ unutu:latest
:
# docker run -ti ubuntu Unable to find image 'ubuntu:latest' locally latest: Pulling from ubuntu 9377ad319b00: Downloading [> ] 1.08 MB/65.67 MB a82f81f25750: Download complete b207c06aba70: Download complete d55e68e6cc9c: Download complete
В процессе загрузки видны четыре слоя.
Так же их можно увидеть после запуска с помощью docker images -a
:
# docker images -a REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE ubuntu latest d55e68e6cc9c 2 weeks ago 187.9 MB <none> <none> b207c06aba70 2 weeks ago 187.9 MB <none> <none> a82f81f25750 2 weeks ago 187.9 MB <none> <none> 9377ad319b00 2 weeks ago 187.7 MB
Имя репозитория <none>
и тег <none>
тут указывают, что он является промежуточным слоем для какого-то целого образа.
Находим все файлы и каталоги, созданные для этих слоев:
# IDS=(9377ad319b00 a82f81f25750 b207c06aba70 d55e68e6cc9c) # for i in ${IDS[@]}; do > echo -e "nChecking layer $in" > find / -name "*$i*" -exec file {} ; > done Checking layer 9377ad319b00 /var/lib/docker/graph/9377ad319b00884df249b7820e3cf540b1c4631b3b1ee6998a0f7c3d53962e03: directory /var/lib/docker/devicemapper/metadata/9377ad319b00884df249b7820e3cf540b1c4631b3b1ee6998a0f7c3d53962e03: ASCII text, with no line terminators /var/lib/docker/devicemapper/mnt/9377ad319b00884df249b7820e3cf540b1c4631b3b1ee6998a0f7c3d53962e03: directory Checking layer a82f81f25750 /var/lib/docker/graph/a82f81f257507f5cb74e833ff1ae4a6a39dfa654a161f5393f641832872b87d3: directory /var/lib/docker/devicemapper/metadata/a82f81f257507f5cb74e833ff1ae4a6a39dfa654a161f5393f641832872b87d3: ASCII text, with no line terminators /var/lib/docker/devicemapper/mnt/a82f81f257507f5cb74e833ff1ae4a6a39dfa654a161f5393f641832872b87d3: directory Checking layer b207c06aba70 /var/lib/docker/graph/b207c06aba70227e0a2561bb7df20a5fd1310901da98ecc6f4da7dccdc40d961: directory /var/lib/docker/devicemapper/metadata/b207c06aba70227e0a2561bb7df20a5fd1310901da98ecc6f4da7dccdc40d961: ASCII text, with no line terminators /var/lib/docker/devicemapper/mnt/b207c06aba70227e0a2561bb7df20a5fd1310901da98ecc6f4da7dccdc40d961: directory Checking layer d55e68e6cc9c /var/lib/docker/graph/d55e68e6cc9c7f78f1c02001e1a5ce76511db044c659e5c0a4275c54473f2869: directory /var/lib/docker/devicemapper/metadata/d55e68e6cc9c7f78f1c02001e1a5ce76511db044c659e5c0a4275c54473f2869: ASCII text, with no line terminators /var/lib/docker/devicemapper/mnt/d55e68e6cc9c7f78f1c02001e1a5ce76511db044c659e5c0a4275c54473f2869: directory
В данном случае нас интересует каталог /var/lib/docker/graph/,
который ещё назывется graph database.
Именно в нем хранятся файлы слоев образов, которые нам нужны для получения всей картины.
Каждый каталог соответствует одному из имеющихся у Docker слоев:
# ls -l /var/lib/docker/graph/ total 20 drwx------ 2 root root 4096 Dec 26 17:51 9377ad319b00884df249b7820e3cf540b1c4631b3b1ee6998a0f7c3d53962e03 drwx------ 2 root root 4096 Dec 26 17:51 a82f81f257507f5cb74e833ff1ae4a6a39dfa654a161f5393f641832872b87d3 drwx------ 2 root root 4096 Dec 26 17:51 b207c06aba70227e0a2561bb7df20a5fd1310901da98ecc6f4da7dccdc40d961 drwx------ 2 root root 4096 Dec 26 17:51 d55e68e6cc9c7f78f1c02001e1a5ce76511db044c659e5c0a4275c54473f2869 drwx------ 2 root root 4096 Dec 26 17:51 _tmp
Теперь – взглянем на список доступных образов:
# docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE ubuntu latest d55e68e6cc9c 2 weeks ago 187.9 MB
Мы видим 1 образ с IMAGE ID
d55e68e6cc9c.
Проверяем его каталог:
# ls -l /var/lib/docker/graph/d55e68e6cc9c7f78f1c02001e1a5ce76511db044c659e5c0a4275c54473f2869/ total 12 -rw------- 1 root root 71 Dec 26 17:51 checksum -rw------- 1 root root 1288 Dec 26 17:51 json -rw------- 1 root root 1 Dec 26 17:51 layersize
Файл checksum
содержит в себе, как понятно из названия, контрольную сумму слоя:
# cat /var/lib/docker/graph/d55e68e6cc9c7f78f1c02001e1a5ce76511db044c659e5c0a4275c54473f2869/checksum sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
А layersize
– размер слоя (о самих данных – в другой раз).
Теперь мы можем приступить к рассмотрению иерархии слоев для образа ubuntu:latest
.
Структура связей между слоями в Docker – иерархическая. Имеется некий базовый слой, на который “накладываются” остальные слои:
Каждый слой описывает какое-то изменение, которое должно быть выполнено с данными на запущенном контейнере.
Как мы видели – при загрузке одного образа загружается несколько файлов слоев – в нашем случае 4, которые мы и внесли в массив при поиске каталогов:
# IDS=(9377ad319b00 a82f81f25750 b207c06aba70 d55e68e6cc9c)
В каталоге каждого слоя имеется файл json
, который содержит всю информацию о слое:
# cat /var/lib/docker/graph/d55e68e6cc9c7f78f1c02001e1a5ce76511db044c659e5c0a4275c54473f2869/json | python -mjson.tool { "Size": 0, "architecture": "amd64", "config": { ...
Среди прочих ключей – тут присутствует ключ parent
, который указывает на родительский слой:
# cat /var/lib/docker/graph/d55e68e6cc9c7f78f1c02001e1a5ce76511db044c659e5c0a4275c54473f2869/json | python -mjson.tool | grep parent "parent": "b207c06aba70227e0a2561bb7df20a5fd1310901da98ecc6f4da7dccdc40d961"
А слой b207c06aba70 – содержит:
# cat /var/lib/docker/graph/b207c06aba70227e0a2561bb7df20a5fd1310901da98ecc6f4da7dccdc40d961/json | python -mjson.tool | grep parent "parent": "a82f81f257507f5cb74e833ff1ae4a6a39dfa654a161f5393f641832872b87d3"
Далее:
# cat /var/lib/docker/graph/a82f81f257507f5cb74e833ff1ae4a6a39dfa654a161f5393f641832872b87d3/json | python -mjson.tool | grep parent "parent": "9377ad319b00884df249b7820e3cf540b1c4631b3b1ee6998a0f7c3d53962e03"
Корневой же слой этой записи уже не содержит:
# cat /var/lib/docker/graph/9377ad319b00884df249b7820e3cf540b1c4631b3b1ee6998a0f7c3d53962e03/json | python -mjson.tool | grep parent | wc -l 0
Так вместе – они образуют шаблон образа.
Если ещё раз посмотреть на процесс запуска конейнера с этим образом, мы увидим, что:
# docker run -ti ubuntu Unable to find image 'ubuntu:latest' locally latest: Pulling from ubuntu 9377ad319b00: Downloading [> ] 1.08 MB/65.67 MB a82f81f25750: Download complete b207c06aba70: Download complete d55e68e6cc9c: Download complete
Последним загружался 9377ad319b00
– самый большой по размеру, основной слой образа. Остальные слои – мельче, и только описывают изменения по отношению к предыдущему слою.
Далее, при старте контейнера с помощью docker run
– Docker обратится к файлу /var/lib/docker/repositories
(или /var/lib/docker/repositories-devicemapper
в зависимости от драйвера UFS), в котором ищет заданный образ в локальном репозитории.
Если образ с заданным в параметре docker run
репозиторием и тегом найден в файле /var/lib/docker/repositories
– будет использован слой с ID
, указанный для этого имени.
Т.е., у нас имеется образ репозитория ubuntu
с тегом latest
:
# docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE ubuntu latest d55e68e6cc9c 2 weeks ago 187.9 MB
При запуске:
# docker run -ti ubuntu
Docker обратится к файлу /var/lib/docker/repositories-devicemapper
:
# cat /var/lib/docker/repositories-devicemapper | python -mjson.tool { "Repositories": { "ubuntu": { "latest": "d55e68e6cc9c7f78f1c02001e1a5ce76511db044c659e5c0a4275c54473f2869" } } }
В котором найдет имя ubuntu
, тег latest
(т.к. другой в параметрах не указан) и ID
слоя d55e68e6cc9c7f78f1c02001e1a5ce76511db044c659e5c0a4275c54473f2869.
После чего – он обратится к своей graph database, в которой определит зависимости с помощью тега parent
в json
-файлах слоев, начиная от слоя d55e68e6cc9c, что бы определить всю структуру образа.
Собственно, именно так и формируются образы Docker.
Давайте создадим свой новый образ, из имещегося образа Ubuntu.
Создаём Dockerfile
файл, из которого будем собирать этот образ, в котором прописываем:
FROM ubuntu RUN apt-get update CMD ["bash"]
Собираем образ:
# docker build -t ubuntu_upd . Sending build context to Docker daemon 2.048 kB Sending build context to Docker daemon Step 0 : FROM ubuntu ---> d55e68e6cc9c Step 1 : RUN apt-get update ---> Running in ee764ea37105 Ign http://archive.ubuntu.com trusty InRelease Get:1 http://archive.ubuntu.com trusty-updates InRelease [64.4 kB] ... Fetched 21.4 MB in 32s (666 kB/s) Reading package lists... ---> 4fe9b73744a4 Removing intermediate container ee764ea37105 Step 2 : CMD bash ---> Running in b19da762f990 ---> c12cbab7fec0 Removing intermediate container b19da762f990 Successfully built c12cbab7fec0
Проверяем:
# docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE ubuntu_upd latest c12cbab7fec0 31 seconds ago 209.3 MB ubuntu latest d55e68e6cc9c 2 weeks ago 187.9 MB
И каталог /var/lib/docker/graph/
:
# ls -l /var/lib/docker/graph/ total 28 drwx------ 2 root root 4096 Dec 26 19:18 4fe9b73744a437a7b2a2abefb5dd108ad9780791bf843176963aa002cabca1c6 drwx------ 2 root root 4096 Dec 26 17:51 9377ad319b00884df249b7820e3cf540b1c4631b3b1ee6998a0f7c3d53962e03 drwx------ 2 root root 4096 Dec 26 17:51 a82f81f257507f5cb74e833ff1ae4a6a39dfa654a161f5393f641832872b87d3 drwx------ 2 root root 4096 Dec 26 17:51 b207c06aba70227e0a2561bb7df20a5fd1310901da98ecc6f4da7dccdc40d961 drwx------ 2 root root 4096 Dec 26 19:19 c12cbab7fec03e0206c997c07f0f88bd56c80cff0d807b3f5d2b7b868d356495 drwx------ 2 root root 4096 Dec 26 17:51 d55e68e6cc9c7f78f1c02001e1a5ce76511db044c659e5c0a4275c54473f2869
В котором видим два новых слоя:
drwx------ 2 root root 4096 Dec 26 19:19 c12cbab7fec03e0206c997c07f0f88bd56c80cff0d807b3f5d2b7b868d356495
и:
drwx------ 2 root root 4096 Dec 26 19:18 4fe9b73744a437a7b2a2abefb5dd108ad9780791bf843176963aa002cabca1c6
Проверяем родителей, начиная от c12cbab7fec0:
# cat /var/lib/docker/graph/c12cbab7fec03e0206c997c07f0f88bd56c80cff0d807b3f5d2b7b868d356495/json | python -mjson.tool | grep parent "parent": "4fe9b73744a437a7b2a2abefb5dd108ad9780791bf843176963aa002cabca1c6"
И:
# cat /var/lib/docker/graph/4fe9b73744a437a7b2a2abefb5dd108ad9780791bf843176963aa002cabca1c6/json | python -mjson.tool | grep parent "parent": "d55e68e6cc9c7f78f1c02001e1a5ce76511db044c659e5c0a4275c54473f2869"
Запись "parent": "d55e68e6cc9c7f78f1c02001e1a5ce76511db044c659e5c0a4275c54473f2869"
указывает на наш первый образ ubuntu:latest
.
Т.е. – наш образ ubuntu_upd:latest
основан на тех же файлах слоев, которые используются образом ubuntu:latest
+ два “лишних” слоя, которые мы добавили “сами”, это c12cbab7fec0 и 4fe9b73744a4.
Каждая инструкция в Dockerfile
вызывает создание нового слоя при сборке образа.
Каждый из них описывает действие, которое мы указали в нашем Dockerfile
.
В нашем примере их два – это RUN apt-get update
и CMD ["bash"]
:
# cat /var/lib/docker/graph/c12cbab7fec03e0206c997c07f0f88bd56c80cff0d807b3f5d2b7b868d356495/json | python -mjson.tool | grep -B3 CMD "Cmd": [ "/bin/sh", "-c", "#(nop) CMD ["bash"]"
И второй новый слой:
# cat /var/lib/docker/graph/4fe9b73744a437a7b2a2abefb5dd108ad9780791bf843176963aa002cabca1c6/json | python -mjson.tool | grep -B3 apt-get "Cmd": [ "/bin/sh", "-c", "apt-get update"
Про контейнеры и данные в слоях – в следующей части.