Введение
По умолчанию все файлы, созданные внутри контейнера, хранятся на записной слое контейнера. Это означает, что:
- Если контейнер больше не существует, данные теряются,
- Записной слой контейнера тесно связан с хост-машиной, и
- Чтобы управлять файловой системой, вам нужен драйвер хранилища, который обеспечивает объединенную файловую систему, используя ядро Linux. Эта дополнительная абстракция снижает производительность по сравнению с «датовыми томами», которые записываются напрямую в файловую систему.
Docker предоставляет два варианта для хранения файлов на хост-машине: «томы» (volumes) и «привязанные точки монтирования» (bind mounts). Если вы запускаете Docker на Linux, вы также можете использовать «монтирование tmpfs», а при использовании Docker на Windows вы также можете использовать «именованный канал» (named pipe).

- «Томы» (
Volumes) хранятся в файловой системе хоста, которая управляется Docker. - «Привязанные точки монтирования» (
Bind mounts) хранятся в любом месте системы хоста. - «Монтирование tmpfs» (
tmpfs mounts) хранится только в памяти хоста.
Исходно флаг --mount использовался для служб Docker Swarm, а флаг --volume — для отдельных контейнеров. Начиная с Docker 17.06 и выше, вы также можете использовать --mount для отдельных контейнеров, и он в целом более явный и подробный, чем --volume.
Томы
«Датовый том» (data volume) или «том» (volume) — это каталог, который обходит объединенную файловую систему Docker.
Есть три типа томов:
- анонимный том,
- именованный том, и
- хостовой том.
Анонимный том
Создадим экземпляр популярной открытой исходной кодовой NoSQL-базы данных CouchDB и используем анонимный том для хранения файлов данных для базы данных.
Для запуска экземпляра CouchDB используйте образ CouchDB с Docker Hub по адресу https://hub.docker.com/_/couchdb. В документации сказано, что по умолчанию для CouchDB записываются файлы базы данных на диск в системе хоста с использованием собственного внутреннего управления томами.
Запустите следующую команду:
docker run -d -p 5984:5984 --name my-couchdb -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=passw0rd1 couchdb:3.1
CouchDB создаст анонимный том и сгенерирует именованный хэш. Проверьте тома на вашей системе хоста:
labex:~/ $ docker volume ls
DRIVER VOLUME NAME
local 1d292aca855adb9de9be7acea88f6d3f8e6a08eef5bfd986a81f073f1906b82f
Установите переменную окружения VOLUME со значением сгенерированного имени:
export VOLUME=<VOLUME NAME>
И проверьте том, который был создан, используя хэш-имя, сгенерированное для тома:
$ docker volume inspect $VOLUME
[
{
"CreatedAt": "2020-09-24T14:10:07Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/f543c5319ebd96b7701dc1f2d915f21b095dfb35adbb8dc851630e098d526a50/_data",
"Name": "f543c5319ebd96b7701dc1f2d915f21b095dfb35adbb8dc851630e098d526a50",
"Options": null,
"Scope": "local"
}
]
Вы видите, что Docker создал и управляет томом в файловой системе Docker-хоста по пути /var/lib/docker/volumes/$VOLUME_NAME/_data. Обратите внимание, что это не путь на хост-машине, а часть управляемой Docker файловой системы.
Создайте новую базу данных mydb и вставьте новый документ с сообщением hello world.
curl -X PUT -u admin:passw0rd1 http://127.0.0.1:5984/mydb
curl -X PUT -u admin:passw0rd1 http://127.0.0.1:5984/mydb/1 -d '{"msg": "hello world"}'
Остановите контейнер и запустите его снова:
docker stop my-couchdb
docker start my-couchdb
Получите документ из базы данных, чтобы проверить, сохранились ли данные.
curl -X GET -u admin:passw0rd1 http://127.0.0.1:5984/mydb/_all_docs
curl -X GET -u admin:passw0rd1 http://127.0.0.1:5984/mydb/1
Вывод:
## $ curl -X GET -u admin:passw0rd1 http://127.0.0.1:5984/mydb/_all_docs
{"total_rows":1,"offset":0,"rows":[
{"id":"1","key":"1","value":{"rev":"1-c09289617e06b96bc747fb1201fea7f1"}}
]}
## $ curl -X GET -u admin:passw0rd1 http://127.0.0.1:5984/mydb/1
{"_id":"1","_rev":"1-c09289617e06b96bc747fb1201fea7f1","msg":"hello world"}
Общий доступ к томам
Вы можете поделиться анонимным томом с другим контейнером, используя параметр --volumes-from.
Создайте контейнер busybox с анонимным томом, смонтированным в каталог /data внутри контейнера, и с использованием командной строки напишите сообщение в файл журнала.
$ docker run -it --name busybox1 -v /data busybox sh
/ ## echo "hello from busybox1" > /data/hi.log
/ ## ls /data
hi.log
/ ## exit
Убедитесь, что контейнер busybox1 остановлен, но не удален.
labex:~/ $ docker ps -a | grep busybox1
f4dbf9ee7513 busybox "sh" 2 minutes ago Exited (0) About a minute ago busybox1
Затем создайте второй контейнер busybox с именем busybox2, используя параметр --volumes-from, чтобы поделиться томом, созданным контейнером busybox1:
$ docker run --rm -it --name busybox2 --volumes-from busybox1 busybox sh
/ ## ls -al /data
total 12
drwxr-xr-x 2 root root 4096 Jan 23 07:20.
drwxr-xr-x 1 root root 4096 Jan 23 07:24..
-rw-r--r-- 1 root root 20 Jan 23 07:20 hi.log
/ ## cat /data/hi.log
hello from busybox1
/ ## exit
Docker создал анонимный том, который вы смогли поделиться с использованием параметра --volumes-from, и создал новый анонимный том.
labex:~/ $ docker volume ls
DRIVER VOLUME NAME
local 0f971b2477d5fc0d0c2b31fc908ee59d6b577b4887e381964650ce6853890dc9
local 1d292aca855adb9de9be7acea88f6d3f8e6a08eef5bfd986a81f073f1906b82f
Очистите существующие тома и контейнеры.
docker stop my-couchdb
docker rm my-couchdb
docker rm busybox1
docker volume rm $(docker volume ls -q)
docker system prune -a
clear
Именованный том
«Именованный том» (named volume) и «анонимный том» похожи тем, что Docker управляет их расположением. Однако именованный том можно ссылаться по имени при монтировании в каталог контейнера. Это полезно, если вы хотите поделиться томом между несколькими контейнерами.
Сначала создайте именованный том:
docker volume create my-couchdb-data-volume
Проверьте, был ли том создан:
$ docker volume ls
DRIVER VOLUME NAME
local my-couchdb-data-volume
Теперь создайте контейнер CouchDB с именем my-couchdb-name-vol, используя именованный том:
docker run -d -p 59840:5984 --name my-couchdb-name-vol -v my-couchdb-data-volume:/opt/couchdb/data -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=passw0rd1 couchdb:3.1
Подождите, пока контейнер CouchDB запустится и будет доступен.
Создайте новую базу данных mydb и вставьте новый документ с сообщением hello world.
curl -X PUT -u admin:passw0rd1 http://127.0.0.1:59840/mydb
curl -X PUT -u admin:passw0rd1 http://127.0.0.1:59840/mydb/1 -d '{"msg": "hello world"}'
Теперь легко поделиться томом с другим контейнером. Например, прочитайте содержимое тома с использованием образа busybox и поделитесь томом my-couchdb-data-volume, смонтировав том в каталог внутри контейнера busybox.
labex:~/ $ docker run --rm -it --name busybox -v my-couchdb-data-volume:/myvolume busybox sh
/ #
/ ## ls -al /myvolume
total 40
drwxr-xr-x 4 5984 5984 4096 Jan 23 07:30.
drwxr-xr-x 1 root root 4096 Jan 23 07:31..
drwxr-xr-x 2 5984 5984 4096 Jan 23 07:29.delete
-rw-r--r-- 1 5984 5984 8388 Jan 23 07:30 _dbs.couch
-rw-r--r-- 1 5984 5984 8385 Jan 23 07:29 _nodes.couch
drwxr-xr-x 4 5984 5984 4096 Jan 23 07:30 shards
/ ## exit
Вы можете проверить файловую систему Docker для томов, запустив контейнер busybox с привилегированными правами и установив идентификатор процесса в host, чтобы проверить систему хоста, и перейти по управляемым Docker каталогам.
docker run -it --privileged --pid=host busybox nsenter -t 1 -m -u -n -i sh
/ ## ls -l /var/lib/docker/volumes
total 28
-rw------- 1 root root 32768 Nov 10 15:54 metadata.db
drwxr-xr-x 3 root root 4096 Nov 10 15:54 my-couchdb-data-volume
/ ## exit
Очистите:
docker stop my-couchdb
docker rm my-couchdb
docker volume rm my-couchdb-data-volume
docker system prune -a
docker volume prune
clear
Хостовой том
Когда вы хотите легко получить доступ к каталогу тома напрямую с хост-машины, вместо использования управляемых Docker каталогов, вы можете создать хостовой том.
Используйте каталог в текущем рабочем каталоге (указанный командой pwd), называемый data, или выберите свой собственный каталог данных на хост-машине, например, /home/couchdb/data. Мы позволим Docker создать каталог $(pwd)/data, если он еще не существует. Мы монтируем хостовой том внутри контейнера CouchDB в каталог контейнера /opt/couchdb/data, который является стандартным каталогом данных для CouchDB.
Запустите следующую команду:
cd /home/labex/project
docker run -d -p 5984:5984 --name my-couchdb -v $(pwd)/data:/opt/couchdb/data -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=passw0rd1 couchdb:3.1
Проверьте, был ли создан каталог data:
$ ls -al
total 20
drwxrwxr-x 3 labex labex 4096 Aug 29 14:14.
drwxr-x--- 25 labex labex 4096 Aug 29 14:14..
drwxr-xr-x 3 5984 5984 4096 Aug 29 14:14 data
и что CouchDB создал здесь файлы данных:
$ ls -al data
total 32
drwxr-xr-x 3 5984 5984 4096 Aug 29 14:14.
drwxrwxr-x 3 labex labex 4096 Aug 29 14:14..
-rw-r--r-- 1 5984 5984 4257 Aug 29 14:14 _dbs.couch
drwxr-xr-x 2 5984 5984 4096 Aug 29 14:14.delete
-rw-r--r-- 1 5984 5984 8385 Aug 29 14:14 _nodes.couch
Также проверьте, что теперь Docker не создал управляемый том, потому что мы теперь используем хостовой том.
docker volume ls
и
docker run -it --privileged --pid=host busybox nsenter -t 1 -m -u -n -i sh
sh-5.1## ls -l /var/lib/docker/volumes
total 28
brw------- 1 root root 252, 3 Jan 23 15:15 backingFsBlockDev
-rw------- 1 root root 32768 Jan 23 15:33 metadata.db
drwx-----x 3 root root 4096 Jan 23 15:26 my-couchdb-data-volume
sh-5.1## exit
Создайте новую базу данных mydb и вставьте новый документ с сообщением hello world.
curl -X PUT -u admin:passw0rd1 http://127.0.0.1:5984/mydb
curl -X PUT -u admin:passw0rd1 http://127.0.0.1:5984/mydb/1 -d '{"msg": "hello world"}'
Обратите внимание, что CouchDB создал папку shards:
$ ls -al data
total 40
drwxr-xr-x 4 5984 5984 4096 Aug 29 14:15.
drwxrwxr-x 3 labex labex 4096 Aug 29 14:14..
-rw-r--r-- 1 5984 5984 8388 Aug 29 14:15 _dbs.couch
drwxr-xr-x 2 5984 5984 4096 Aug 29 14:14.delete
-rw-r--r-- 1 5984 5984 8385 Aug 29 14:14 _nodes.couch
drwxr-xr-x 4 5984 5984 4096 Aug 29 14:15 shards
Выведите содержимое каталога shards:
$ ls -al data/shards
total 16
drwxr-xr-x 4 5984 5984 4096 Aug 29 14:15.
drwxr-xr-x 4 5984 5984 4096 Aug 29 14:15..
drwxr-xr-x 2 5984 5984 4096 Aug 29 14:15 00000000-7fffffff
drwxr-xr-x 2 5984 5984 4096 Aug 29 14:15 80000000-ffffffff
и первый шар:
$ ls -al data/shards/00000000-7fffffff/
total 20
drwxr-xr-x 2 5984 5984 4096 Aug 29 14:15.
drwxr-xr-x 4 5984 5984 4096 Aug 29 14:15..
-rw-r--r-- 1 5984 5984 8346 Aug 29 14:15 mydb.1693289721.couch
Шарды — это горизонтальное разделение данных в базе данных. Разделение данных на шарды и распределение копий каждого шарда по разным узлам в кластере обеспечивает большую устойчивость данных к потере узлов. CouchDB автоматически разбивает базы данных на шарды и распределяет подмножества документов между узлами.
Очистите:
docker stop my-couchdb
docker rm my-couchdb
sudo rm -rf $(pwd)/data
docker system prune -a
Привязанные точки монтирования
Синтаксис mount рекомендуется Docker вместо синтаксиса volume. Привязанные точки монтирования имеют ограниченную функциональность по сравнению с томами. Файл или каталог ссылается на его полный путь на хост-машине при монтировании в контейнер. Привязанные точки монтирования зависят от того, что на файловой системе хоста есть определенная структура каталогов, и вы не можете использовать Docker CLI для управления привязанными точками монтирования. Обратите внимание, что привязанные точки монтирования могут изменить файловую систему хоста с помощью процессов, запускающихся в контейнере.
Вместо использования синтаксиса -v с тремя полями, разделенными двоеточием (:), синтаксис mount более подробный и использует несколько пар ключ-значение:
- type: bind, volume или tmpfs,
- source: путь к файлу или каталогу на хост-машине,
- destination: путь в контейнере,
- readonly,
- bind-propagation: rprivate, private, rshared, shared, rslave, slave,
- consistency: consistent, delegated, cached,
- mount.
cd /home/labex/project
mkdir data
docker run -it --name busybox --mount type=bind,source="$(pwd)"/data,target=/data busybox sh
Введите команду в контейнере:
echo "hello busybox" > /data/hi.txt
exit
Проверьте, был ли создан файл на хост-машине.
cat data/hi.txt
[Необязательно] OverlayFS
OverlayFS — это реализация объединенного файлового systému (union mount filesystem) для Linux. Чтобы понять, что такое том Docker, полезно знать, как работают слои и файловая система в Docker.
Для запуска контейнера Docker берет образ с чтением только и создает поверх него новый слой с записью. Чтобы рассматривать слои как единый целый, Docker использует объединенную файловую систему или OverlayFS (Overlay File System), конкретно драйвер хранилища overlay2.
Чтобы увидеть файлы, управляемые Docker-хостом, вам нужно иметь доступ к файловой системе процесса Docker. С использованием флагов --privileged и --pid=host вы можете получить доступ к пространству имен идентификаторов процессов хоста изнутри контейнера, такого как busybox. Затем вы можете перейти по директории /var/lib/docker/overlay2 Docker, чтобы увидеть загруженные слои, управляемые Docker.
Чтобы просмотреть текущий список слоев в Docker:
$ docker run -it --privileged --pid=host busybox nsenter -t 1 -m -u -n -i sh
/ ## ls -l /var/lib/docker/overlay2
total 16
drwx------ 3 root root 4096 Sep 25 19:44 0e55ecaa4d17c353191e68022d9a17fde64fb5e9217b07b5c56eb4c74dad5b32
drwx------ 5 root root 4096 Sep 25 19:44 187854d05ccd18980642e820b0d2be6a127ba85d8ed96315bb5ae37eb1add36d
drwx------ 4 root root 4096 Sep 25 19:44 187854d05ccd18980642e820b0d2be6a127ba85d8ed96315bb5ae37eb1add36d-init
drwx------ 2 root root 4096 Sep 25 19:44 l
/ ## exit
Скачайте образ ubuntu и проверьте снова:
docker pull ubuntu
docker run -it --privileged --pid=host busybox nsenter -t 1 -m -u -n -i sh
Введите команду, чтобы снова увидеть список слоев:
ls -l /var/lib/docker/overlay2/ & exit
Вы увидите, что при скачивании образа ubuntu неявно были скачаны 4 новых слоя:
- a611792b4cac502995fa88a888261dfba0b5d852e72f9db9e075050991423779
- d181f1a41fc35a45c16e8bfcb8eee6f768f3b98f82210a43ea65f284a45fcd65
- dac2f37f6280a076836d39b87b0ae5ebf5c0d386b6d8b991b103aadbcebaa7c6
- f3e921b440c37c86d06cd9c9fb70df50edad553c36cc87f84d5eeba734aae709
Драйвер хранилища overlay2 по существу накладывает разные директории на хосте и представляет их в виде одной директории.
- базовый слой или lowerdir,
- слой
diffили upperdir, - слой overlay (представление пользователя), и
- директория
work.
OverlayFS ссылается на нижние директории как lowerdir, которая содержит базовый образ и слои с чтением только (R/O), которые были загружены.
Верхняя директория называется upperdir и представляет собой слой контейнера с записью (R/W).
Объединенное представление или слой overlay называется merged.
Наконец, workdir является обязательной и представляет собой пустую директорию, используемую overlay для внутреннего использования.
Драйвер overlay2 поддерживает до 128 нижних слоев OverlayFS. Директория l содержит сокращенные идентификаторы слоев в виде символических ссылок.

Очистите:
docker system prune -a
clear
Резюме
В этом практическом занятии вы узнали, как управлять данными в контейнерах с использованием томов (volumes) и привязанных точек монтирования (bind mounts). Вы также узнали о драйвере хранилища overlay2 и том, как он использует объединенную файловую систему для управления слоями.