Каждый 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"
Про контейнеры и данные в слоях — в следующей части.





