Разбираемся с Docker: как создаются образы
От любого инструмента, который внедряем в проект, мы ждём стабильной работы. Docker не исключение. Чтобы иметь возможность оперативно выявлять потенциальные проблемы и избегать сбоев, необходимо понимать внутренние особенности технологии. Эта статья — сборник заметок, которые помогут разобраться, как создаются образы контейнеров.
Введение
Уточним, почему появилась потребность в образах контейнеров. До того, как Docker Inc. прославил контейнеризацию, для развёртывания нескольких приложений на одном сервере использовалась виртуализация. При этом каждая виртуальная машина зависела от операционной системы и приложений.
Предположим, одна виртуальная машина занимала около 10 ГБ пространства в ОС, для запуска 100 таких в физической системе требовалось около 1 ТБ. Если все виртуальные машины работали под управлением одной и той же ОС, огромная часть дискового пространства тратилась на хранение повторяющихся файлов. Приходилось устанавливать зависимости приложений на каждую виртуальную машину отдельно, даже если большинство из них имело общие зависимости. Эти ограничения приводили к множеству операционных проблем, связанных с обновлением, распространением и др.
Некоторые вендоры для кода приложения и зависимостей предоставляли разные диски, которые совместно использовали один родительский диск с операционной системой. В какой-то степени это помогало, но приводило к снижению производительности и тому же набору операционных проблем.
Компания dotCloud начала экспериментировать с конструкциями Linux для запуска приложений как изолированных систем. Она предложила механизм упаковки приложения со всеми зависимостями и процессами в контейнер. Такое решение позволило отделить приложение от инфраструктуры. Теперь контейнеры не зависят от базовой инфраструктуры, легко перемещаются между облачной и локальной инфраструктурами. А разработчикам не нужно волноваться, в какой среде будет функционировать их приложение и будут ли в этой среде необходимые для тестирования опции и зависимости.
Образ — главный элемент в контейнеризации Docker. В нём содержатся процессы и зависимости, необходимые для нормальной работы приложения. Как правило, разработчики предпочитают скачивать готовые образы из репозиториев, а не создавать их с нуля, так как уже есть огромное количество готовых компонент для самых разных задач.
«Docker для админов и разработчиков»
На базовый образ Docker накладываются доступные только для чтения слои, которые образуются после любых изменений в образе. Каждый новый слой — это актуальная версия образа. А финальный образ — это объединение всех слоев в один. Каждый слой образа сохраняется, чтобы при необходимости быстро откатываться назад. Такое решение экономит пространство диска и сокращает время сборки контейнера.
Прежде чем продолжить погружение в тему, разберёмся с Union File System — системой Linux, которая используется для работы с образами.
Union File System
Union File System позволяет объединять содержимое одной или нескольких файловых систем, сохраняя его физически отдельно. Существуют различные реализации UnionFS — AUFS, OverlayFS и др. Каждая из них имеет свои плюсы и минусы. Рассмотрим концепцию объединённой файловой системы на примере OverlayFS.
OverlayFS работает со слоями — одним или несколькими нижними слоями и одним верхним слоем. Нижние обрабатываются как доступные только для чтения, а верхний — для чтения и записи. OverlayFS поддерживает стандартный вид слоев.
OverlayFS берёт содержимое как из нижних, так и из верхних слоев. Если файл существует на двух уровнях (F3), тот, что в верхнем слое, имеет приоритет над тем, что в нижнем. Каждая операция с файлом или каталогом имеет определённый результат.
Добавление новых файлов
При добавлении нового файла OverlayFS переносит его на верхний слой:
Удаление существующего файла
Когда вы удаляете файл или папку с верхнего слоя, они просто удаляются. Однако, когда вы удаляете файл, находящийся на нижнем слое, создаётся специальное символьное устройство с тем же именем на верхнем слое. Нижние слои доступны только для чтения, вы не можете удалить файлы или папки оттуда. Символьное устройство в верхнем слое указывает, что файл или папка должны быть скрыты от единого представления.
Изменения существующего файла
Ничего не произойдёт, если вы измените файл с верхнего слоя. Но если начать вносить изменения в файл с нижнего слоя, он скопируется в верхний, и изменения будут вноситься в эту копию.
OverlayFS в действии
Перед созданием объединённой файловой системы подготовьте несколько директорий:
$ mkdir lower upper merged work demo
$ cd demo
$ echo "This is a file 1 in lower layer" > lower/f1
$ echo "This is a file 2 in lower layer" > lower/f2
$ echo "This is a file 3 in lower layer" > lower/f3
$ echo "This is a file 3 in upper layer" > upper/f3
$ echo "This is a file 4 in upper layer" > upper/f4
$ cd ..
$ tree demo/
demo/
├── lower
│ ├── f1
│ ├── f2
│ └── f3
├── merged
├── upper
│ ├── f3
│ └── f4
└── work
Для создания объединённой файловой системы используйте команду mount:
$ sudo mount overlay -t overlay -o lowerdir=demo/lower/,upperdir=demo/upper/,workdir=demo/work/ demo/merged/
Workdir можно рассматривать как временное рабочее пространство, в котором файлы копируются снизу вверх:
$ ls -l demo/merged/
total 16
-rw-rw-r-- 1 rchaganti rchaganti 32 Oct 18 12:29 f1
-rw-rw-r-- 1 rchaganti rchaganti 32 Oct 18 12:29 f2
-rw-rw-r-- 1 rchaganti rchaganti 32 Oct 18 12:31 f3
-rw-rw-r-- 1 rchaganti rchaganti 32 Oct 18 12:31 f4
$ cat demo/merged/f3
This is a file 3 in upper layer
После успешного выполнения команды mount вы увидите, что содержимое нижнего и верхнего слоёв объединено для обеспечения стандартного представления. Теперь можем приступать к операциям, показанным на рисунках.
Добавление файла
$ echo "A new file called f5 in merged view" > demo/merged/f5
$ ls -l demo/merged/
total 20
-rw-rw-r-- 1 rchaganti rchaganti 32 Oct 18 12:29 f1
-rw-rw-r-- 1 rchaganti rchaganti 32 Oct 18 12:29 f2
-rw-rw-r-- 1 rchaganti rchaganti 32 Oct 18 12:31 f3
-rw-rw-r-- 1 rchaganti rchaganti 32 Oct 18 12:31 f4
-rw-rw-r-- 1 rchaganti rchaganti 36 Oct 18 13:34 f5
$ ls -l demo/upper/
total 12
-rw-rw-r-- 1 rchaganti rchaganti 32 Oct 18 12:31 f3
-rw-rw-r-- 1 rchaganti rchaganti 32 Oct 18 12:31 f4
-rw-rw-r-- 1 rchaganti rchaganti 36 Oct 18 13:34 f5
При добавлении файла в объединённое представление он добавляется на верхний слой, поскольку это слой чтения/записи.
Удаление файла
$ rm demo/merged/f2
$ ls -l demo/merged/
total 16
-rw-rw-r-- 1 rchaganti rchaganti 32 Oct 18 12:29 f1
-rw-rw-r-- 1 rchaganti rchaganti 32 Oct 18 12:31 f3
-rw-rw-r-- 1 rchaganti rchaganti 32 Oct 18 12:31 f4
-rw-rw-r-- 1 rchaganti rchaganti 36 Oct 18 13:34 f5
$ ls -l demo/upper/
total 12
c--------- 2 root root 0, 0 Oct 18 13:36 f2
-rw-rw-r-- 1 rchaganti rchaganti 32 Oct 18 12:31 f3
-rw-rw-r-- 1 rchaganti rchaganti 32 Oct 18 12:31 f4
-rw-rw-r-- 1 rchaganti rchaganti 36 Oct 18 13:34 f5
$ ls -l demo/lower/
total 12
-rw-rw-r-- 1 rchaganti rchaganti 32 Oct 18 12:29 f1
-rw-rw-r-- 1 rchaganti rchaganti 32 Oct 18 12:29 f2
-rw-rw-r-- 1 rchaganti rchaganti 32 Oct 18 12:29 f3
Удаление файла F2 приводит к появлению символьного устройства на верхнем слое. Нижний слой остаётся нетронутым.
Изменение файла
Когда вы меняете файл, в зависимости от того, где он находится (вверху или внизу), выполняется действие копирования. Если вы меняете файл F1 с нижнего слоя, он сначала копируется на верхний, и изменения вносятся уже в копию.
$ cat demo/merged/f1
This is a file 1 in lower layer
$ echo "adding some random content" >> demo/merged/f1
$ cat demo/merged/f1
This is a file 1 in lower layer
adding some random content
$ cat demo/upper/f1
This is a file 1 in lower layer
adding some random content
$ cat demo/lower/f1
This is a file 1 in lower layer
В приведённом выше примере, файл F1 с нижнего слоя остаётся нетронутым. Копия F1 отображается на верхнем слое и модифицируется для добавления новой строки.
Мы разобрались, как работает OverlayFS. Теперь посмотрим, как движки контейнеризации используют файловую систему для управления образами контейнеров.
Образы контейнеров
Движки контейнеризация поддерживают различные типы драйверов хранилища, ответственных за объединение многоуровневых слоев в стандартный вид. Неизменный характер слоев делает образ контейнера многоразовым и позволяет запускать несколько контейнеров из одного образа. Каждый контейнер получает собственный слой, что облегчает запись в файловую систему, а также изменение или удаление существующих файлов. Драйвер хранилища отвечает за управление слоем копирования при записи (CoW). Механизм Docker по умолчанию использует overlay2 в качестве драйвера хранилища:
Разберёмся, как это работает. Сначала извлечём образ из Docker hub:
$ docker pull ravikanth/hello-world:v1
v1: Pulling from ravikanth/hello-world
213ec9aee27d: Pull complete
0d351817f207: Pull complete
8b09275c3de5: Pull complete
Digest: sha256:9964e0545f1cbd09fc902dda80664ba4b23b5f4bd32b1a0e7ab135f819c5ed6c
Status: Downloaded newer image for ravikanth/hello-world:v1
docker.io/ravikanth/hello-world:v1
Образ, который мы только что извлекли, состоит из трех слоев. Драйвер overlay 2 на хосте Docker хранит слои изображения в /var/lib/docker/overlay 2.
$ sudo ls -l /var/lib/docker/overlay2
total 16
drwx--x--- 4 root root 4096 Oct 18 15:32 cf0b92185c9ba3bdf11d19e53252063def37acf80fe3491597fd5a8914e1a696
drwx--x--- 4 root root 4096 Oct 18 15:32 d63b70e8f22063b1eb4296b70c2e231392e28b104475104e14f7346530e0dd39
drwx--x--- 3 root root 4096 Oct 18 15:32 ec861e1affe1b78197b7cc7f6f4381c6de0e490e04a60dc1ff684a9f7ddeab22
drwx------ 2 root root 4096 Oct 18 15:32 l
В данном случае директории являются несжатыми слоями образа. Директория l содержит символические ссылки на директорию diff внутри каждого несжатого слоя.
Если у вас уже есть несколько образов на хосте docker, вы можете использовать команду docker image inspect, чтобы найти правильные идентификаторы несжатого слоя для конкретного образа:
$ docker image inspect ravikanth/hello-world:v1 | jq -r '.[0] | {Data: .GraphDriver.Data}'
{
"Data": {
"LowerDir": "/var/lib/docker/overlay2/d63b70e8f22063b1eb4296b70c2e231392e28b104475104e14f7346530e0dd39/diff:/var/lib/docker/overlay2/ec861e1affe1b78197b7cc7f6f4381c6de0e490e04a60dc1ff684a9f7ddeab22/diff",
"MergedDir": "/var/lib/docker/overlay2/cf0b92185c9ba3bdf11d19e53252063def37acf80fe3491597fd5a8914e1a696/merged",
"UpperDir": "/var/lib/docker/overlay2/cf0b92185c9ba3bdf11d19e53252063def37acf80fe3491597fd5a8914e1a696/diff",
"WorkDir": "/var/lib/docker/overlay2/cf0b92185c9ba3bdf11d19e53252063def37acf80fe3491597fd5a8914e1a696/work"
}
}
Если вы видите этот output, свойство Lower Dir имеет более одного связанного с ним слоя:
/var/lib/docker/overlay2/d63b70e8f22063b1eb4296b70c2e231392e28b104475104e14f7346530e0dd39/diff
/var/lib/docker/overlay2/ec861e1affe1b78197b7cc7f6f4381c6de0e490e04a60dc1ff684a9f7ddeab22/diff
Слои представлены в порядке их расположения в образе контейнера. Следовательно, ec861e1affe1b78197b7cc7f6f4381c6de0e490e04a60dc1ff684a9f7ddeab22 — нижний слой, а d63b70e8f22063b1eb4296b70c2e231392e28b104475104e14f7346530e0dd39 — тот, что находится над нижним слоем.
Теперь, когда мы знаем порядок расположения слоев, перейдём к просмотру того, что находится в каждом из несжатых директорий.
Нижний слой содержит три записи:
$ sudo ls -l /var/lib/docker/overlay2/ec861e1affe1b78197b7cc7f6f4381c6de0e490e04a60dc1ff684a9f7ddeab22
total 8
-rw------- 1 root root 0 Oct 18 15:32 committed
drwxr-xr-x 19 root root 4096 Oct 18 15:32 diff
-rw-r--r-- 1 root root 26 Oct 18 15:32 link
$ sudo ls -l /var/lib/docker/overlay2/ec861e1affe1b78197b7cc7f6f4381c6de0e490e04a60dc1ff684a9f7ddeab22/diff
total 68
drwxr-xr-x 2 root root 4096 Aug 9 14:17 bin
drwxr-xr-x 2 root root 4096 Aug 9 14:17 dev
drwxr-xr-x 16 root root 4096 Aug 9 14:17 etc
drwxr-xr-x 2 root root 4096 Aug 9 14:17 home
drwxr-xr-x 7 root root 4096 Aug 9 14:17 lib
drwxr-xr-x 5 root root 4096 Aug 9 14:17 media
drwxr-xr-x 2 root root 4096 Aug 9 14:17 mnt
drwxr-xr-x 2 root root 4096 Aug 9 14:17 opt
dr-xr-xr-x 2 root root 4096 Aug 9 14:17 proc
drwx------ 2 root root 4096 Aug 9 14:17 root
drwxr-xr-x 2 root root 4096 Aug 9 14:17 run
drwxr-xr-x 2 root root 4096 Aug 9 14:17 sbin
drwxr-xr-x 2 root root 4096 Aug 9 14:17 srv
drwxr-xr-x 2 root root 4096 Aug 9 14:17 sys
drwxrwxrwt 2 root root 4096 Aug 9 14:17 tmp
drwxr-xr-x 7 root root 4096 Aug 9 14:17 usr
drwxr-xr-x 12 root root 4096 Aug 9 14:17 var
$ sudo cat /var/lib/docker/overlay2/ec861e1affe1b78197b7cc7f6f4381c6de0e490e04a60dc1ff684a9f7ddeab22/link
BOOTVTCSKJUJXUJWX765MW7TMJ
$ sudo ls -l /var/lib/docker/overlay2/l/BOOTVTCSKJUJXUJWX765MW7TMJ
lrwxrwxrwx 1 root root 72 Oct 18 15:32 /var/lib/docker/overlay2/l/BOOTVTCSKJUJXUJWX765MW7TMJ -> ../ec861e1affe1b78197b7cc7f6f4381c6de0e490e04a60dc1ff684a9f7ddeab22/diff
Образ основан на alpine: latest, поэтому директория diff на нижнем уровне содержит файлы операционной системы. Перейдём к следующему слою. Если вы посмотрите на последнюю команду, filename d в несжатом слое ссылается на директорию diff в том же слое.
Идём к следующему слою в стеке:
$ sudo ls -l /var/lib/docker/overlay2/d63b70e8f22063b1eb4296b70c2e231392e28b104475104e14f7346530e0dd39
total 16
-rw------- 1 root root 0 Oct 18 15:32 committed
drwxr-xr-x 2 root root 4096 Oct 18 15:32 diff
-rw-r--r-- 1 root root 26 Oct 18 15:32 link
-rw-r--r-- 1 root root 28 Oct 18 15:32 lower
drwx------ 3 root root 4096 Oct 18 15:32 work
$ sudo ls -l /var/lib/docker/overlay2/d63b70e8f22063b1eb4296b70c2e231392e28b104475104e14f7346530e0dd39/diff
total 0
-rw-r--r-- 1 root root 0 Oct 16 10:54 hello.txt
$ sudo cat /var/lib/docker/overlay2/d63b70e8f22063b1eb4296b70c2e231392e28b104475104e14f7346530e0dd39/diff/hello.txt
$ sudo cat /var/lib/docker/overlay2/d63b70e8f22063b1eb4296b70c2e231392e28b104475104e14f7346530e0dd39/link
FYTORA5MEBUHE4B4ZY4IPI3P5D
$ sudo cat /var/lib/docker/overlay2/d63b70e8f22063b1eb4296b70c2e231392e28b104475104e14f7346530e0dd39/lower
l/BOOTVTCSKJUJXUJWX765MW7TMJ
Все несжатые слои выше нижнего слоя содержат пять записей. В директории diff находится содержимое слоя — нулевой файл hello.txt. Файл с именем lower указывает на нижний слой в стеке. И в этом примере он указывает на директорию diff нижнего слоя. Вы можете игнорировать остальные записи в этом несжатом слое. В каждом слое link файл содержит символическую ссылку на diff директорию.
Есть ещё один слой:
$ sudo ls -l /var/lib/docker/overlay2/cf0b92185c9ba3bdf11d19e53252063def37acf80fe3491597fd5a8914e1a696
total 16
drwxr-xr-x 2 root root 4096 Oct 18 15:32 diff
-rw-r--r-- 1 root root 26 Oct 18 15:32 link
-rw-r--r-- 1 root root 57 Oct 18 15:32 lower
drwx------ 3 root root 4096 Oct 18 15:32 work
$ sudo ls -l /var/lib/docker/overlay2/cf0b92185c9ba3bdf11d19e53252063def37acf80fe3491597fd5a8914e1a696/diff
total 4
-rw-r--r-- 1 root root 35 Oct 16 10:54 hello.txt
$ sudo cat /var/lib/docker/overlay2/cf0b92185c9ba3bdf11d19e53252063def37acf80fe3491597fd5a8914e1a696/diff/hello.txt
this is an update to the hello.txt
$ sudo cat /var/lib/docker/overlay2/cf0b92185c9ba3bdf11d19e53252063def37acf80fe3491597fd5a8914e1a696/link
S2YWWPZHW75GE3EIQ7ULNMR5RZ
$ sudo cat /var/lib/docker/overlay2/cf0b92185c9ba3bdf11d19e53252063def37acf80fe3491597fd5a8914e1a696/lower
l/FYTORA5MEBUHE4B4ZY4IPI3P5D:l/BOOTVTCSKJUJXUJWX765MW7TMJ
Этот слой имеет два слоя под ним, поэтому вы видите две разделённые записи в файле с именем lower. Как и в предыдущем слое, diff директория содержит файл hello.txt. Поскольку это верхний слой или самый верхний слой, при создании контейнера вы должны увидеть содержимое этого файла hello.txt внутри контейнера.
Чтобы лучше понять это, визуализируем структуру слоя:
Образы контейнеров не меняются. Вы не можете ничего писать напрямую в эти слои. Единственный способ использовать их — создать инстанс контейнера. Вы можете сделать это с помощью команды запуска docker.
$ docker run -d --name helloworld ravikanth/hello-world:v1 sleep 1000000
cff3f0322cb6765f06575bba9405d8ffeb0fecac237c5f615b906a9546d2a413
На предыдущей иллюстрации вы видели, как из одного образа можно создать несколько контейнеров и как каждый из них получает собственный слой чтения/записи. Теоретически этот слой должен ассоциироваться со слоями несжатого образа. Эту ассоциацию можно найти с помощью команды проверки контейнера docker:
$ docker container inspect helloworld | jq -r '.[0] | {Data: .GraphDriver.Data}'
{
"Data": {
"LowerDir": "/var/lib/docker/overlay2/38705da4838102c3b9d2ae8aeb49844dac84dae95881df61fcffd1685caf9751-init/diff:/var/lib/docker/overlay2/cf0b92185c9ba3bdf11d19e53252063def37acf80fe3491597fd5a8914e1a696/diff:/var/lib/docker/overlay2/d63b70e8f22063b1eb4296b70c2e231392e28b104475104e14f7346530e0dd39/diff:/var/lib/docker/overlay2/ec861e1affe1b78197b7cc7f6f4381c6de0e490e04a60dc1ff684a9f7ddeab22/diff",
"MergedDir": "/var/lib/docker/overlay2/38705da4838102c3b9d2ae8aeb49844dac84dae95881df61fcffd1685caf9751/merged",
"UpperDir": "/var/lib/docker/overlay2/38705da4838102c3b9d2ae8aeb49844dac84dae95881df61fcffd1685caf9751/diff",
"WorkDir": "/var/lib/docker/overlay2/38705da4838102c3b9d2ae8aeb49844dac84dae95881df61fcffd1685caf9751/work"
}
}
Нижняя директория имеет четыре слоя в следующем порядке:
/var/lib/docker/overlay2/38705da4838102c3b9d2ae8aeb49844dac84dae95881df61fcffd1685caf9751-init/diff
/var/lib/docker/overlay2/cf0b92185c9ba3bdf11d19e53252063def37acf80fe3491597fd5a8914e1a696/diff
/var/lib/docker/overlay2/d63b70e8f22063b1eb4296b70c2e231392e28b104475104e14f7346530e0dd39/diff
/var/lib/docker/overlay2/ec861e1affe1b78197b7cc7f6f4381c6de0e490e04a60dc1ff684a9f7ddeab22/diff
По сути, это три слоя образов, которые вы видели ранее, и дополнительный слой-контейнер. Поскольку образы не меняются, MergeDir и WorkDir не имеют большого значения. Директория /var/lib/docker/overlay2/38705da4838102c3b9d2ae8aeb49844dac84dae95881df61fcffd1685caf9751/merged содержит стандартное представление всех слоев образа.
$ sudo ls -l /var/lib/docker/overlay2/38705da4838102c3b9d2ae8aeb49844dac84dae95881df61fcffd1685caf9751/merged
total 72
drwxr-xr-x 2 root root 4096 Aug 9 14:17 bin
drwxr-xr-x 1 root root 4096 Oct 18 18:10 dev
drwxr-xr-x 1 root root 4096 Oct 18 18:10 etc
-rw-r--r-- 1 root root 35 Oct 16 10:54 hello.txt
drwxr-xr-x 2 root root 4096 Aug 9 14:17 home
drwxr-xr-x 7 root root 4096 Aug 9 14:17 lib
drwxr-xr-x 5 root root 4096 Aug 9 14:17 media
drwxr-xr-x 2 root root 4096 Aug 9 14:17 mnt
drwxr-xr-x 2 root root 4096 Aug 9 14:17 opt
dr-xr-xr-x 2 root root 4096 Aug 9 14:17 proc
drwx------ 2 root root 4096 Aug 9 14:17 root
drwxr-xr-x 2 root root 4096 Aug 9 14:17 run
drwxr-xr-x 2 root root 4096 Aug 9 14:17 sbin
drwxr-xr-x 2 root root 4096 Aug 9 14:17 srv
drwxr-xr-x 2 root root 4096 Aug 9 14:17 sys
drwxrwxrwt 2 root root 4096 Aug 9 14:17 tmp
drwxr-xr-x 7 root root 4096 Aug 9 14:17 usr
drwxr-xr-x 12 root root 4096 Aug 9 14:17 var
$ sudo cat /var/lib/docker/overlay2/38705da4838102c3b9d2ae8aeb49844dac84dae95881df61fcffd1685caf9751/merged/hello.txt
this is an update to the hello.txt
Теперь попробуем записать новый файл в запущенный контейнер:
$ docker exec helloworld touch /hello-world.txt
rchaganti@ubuntu02:~$ docker exec helloworld ls -l
total 60
drwxr-xr-x 2 root root 4096 Aug 9 08:47 bin
drwxr-xr-x 5 root root 340 Oct 18 12:40 dev
drwxr-xr-x 1 root root 4096 Oct 18 12:40 etc
-rw-r--r-- 1 root root 0 Oct 18 13:11 hello-world.txt
-rw-r--r-- 1 root root 35 Oct 16 05:24 hello.txt
drwxr-xr-x 2 root root 4096 Aug 9 08:47 home
drwxr-xr-x 7 root root 4096 Aug 9 08:47 lib
drwxr-xr-x 5 root root 4096 Aug 9 08:47 media
drwxr-xr-x 2 root root 4096 Aug 9 08:47 mnt
drwxr-xr-x 2 root root 4096 Aug 9 08:47 opt
dr-xr-xr-x 252 root root 0 Oct 18 12:40 proc
drwx------ 2 root root 4096 Aug 9 08:47 root
drwxr-xr-x 2 root root 4096 Aug 9 08:47 run
drwxr-xr-x 2 root root 4096 Aug 9 08:47 sbin
drwxr-xr-x 2 root root 4096 Aug 9 08:47 srv
dr-xr-xr-x 12 root root 0 Oct 18 12:40 sys
drwxrwxrwt 2 root root 4096 Aug 9 08:47 tmp
drwxr-xr-x 7 root root 4096 Aug 9 08:47 usr
drwxr-xr-x 12 root root 4096 Aug 9 08:47 var
Проверим каталог Upper Dir, чтобы увидеть, отображается ли он:
$ sudo ls -l /var/lib/docker/overlay2/38705da4838102c3b9d2ae8aeb49844dac84dae95881df61fcffd1685caf9751/diff
total 0
-rw-r--r-- 1 root root 0 Oct 18 18:41 hello-world.txt
Если вы выполните те же действия, что и для определения связей между несколькими слоями образа, вы увидите связь между слоем CoW и нижними неизменяемыми слоями изображения.
Это запутанная диаграмма, но она демонстрирует суть. Самый верхний слой — это слой CoW для чтения/записи, а все слои ниже доступны только для чтения. Итак, мы рассмотрели, как драйвер хранилища управляет образами. Возможно, где-то вы читали, что образы контейнеров можно использовать с любым движком контейнеризации. В следующей статье разберём, как это стало возможно, и поговорим об Open Container Initiative и спецификации образа OCI.
«Docker для админов и разработчиков»
Материал основан на статье «Understanding container images — The fundamentals».