Docker storage
В простейшем случае (по учолчанию) все файлы создаваемые внутри контейнера хранятся в слое контейнера, на который позволено вести запись. Данный слой располагается поверх защищенных от записи слоев образа. Данные созданные таким образом не сохранятся при уничножении контейнера.
Записываемый слой данных уникален для каждого контейнера, его сложно извлечь и передать в другой контейнер.
Volume mounts
Тома — это постоянные механизмы хранения, управляемые демоном Docker. Они сохраняют данные даже после удаления контейнеров, использующих их. Volumes хранятся в файловой системе на хосте, но для взаимодействия с ними необходимо смонтировать том в контейнер. Прямой доступ или взаимодействие с данными тома — это неподдерживаемое, неопределенное поведение, которое может привести к тому, что том или его данные будут повреждены неожиданным образом.
Тома идеально подходят для критически важной для производительности обработки данных и долгосрочного хранения. Поскольку место хранения управляется на хосте демона, тома обеспечивают ту же производительность, что и прямой доступ к файловой системе хоста.
Когда вы создаете том, он сохраняется в каталоге на хосте Docker. Когда вы монтируете том в контейнер, этот каталог монтируется в контейнер. Это похоже на то, как работают монтирования bind, за исключением того, что тома управляются Docker и изолированы от основных функций хост-машины.
Почему volumes
- Тома являются предпочтительным механизмом для сохранения данных, созданных и используемых контейнерами Docker.
- Тома проще резервировать и переносить.
- Управлять томами можно с помощью команд Docker CLI или API Docker.
- Тома можно более безопасно распределять между несколькими контейнерами.
- Содержимое новых томов может быть предварительно заполнено контейнером или сборкой.
- Тома следует использовать, когда вашему приложению требуется высокопроизводительный ввод-вывод.
Volumes является плохим выбором, если нужно получать доступ к данным с хостового компьютера. Для таких случаев лучше использовать bind mounts
Тома часто являются лучшим выбором, чем запись данных напрямую в контейнер, поскольку том не увеличивает размер контейнеров, использующих его. Использование тома также быстрее; запись в записываемый слой контейнера требует драйвера хранилища для управления файловой системой. Драйвер хранилища предоставляет объединенную файловую систему, используя ядро Linux. Эта дополнительная абстракция снижает производительность по сравнению с использованием томов, которые записывают напрямую в файловую систему хоста.
Жизненный цикл тома
Содержимое тома существует вне жизненного цикла данного контейнера. Когда контейнер уничтожается, записываемый слой уничтожается вместе с ним. Использование тома гарантирует, что данные будут сохранены, даже если контейнер, использующий его, будет удален.
Заданный том может быть смонтирован в несколько контейнеров одновременно. Когда ни один работающий контейнер не использует том, он по-прежнему доступен Docker и не удаляется автоматически. Вы можете удалить неиспользуемые тома с помощью docker volume prune.
Volume size
Занимаемое место volumes можно узнать несколькими способами:
# Выдает подробную информацию о всех загруженных в систему сущностях docker
docker system df -v
# Следующая команда выдаст информацию по томам
docker system df -v | sed -n '/VOLUME NAME/,/^ *$/p'
# Следующая команда найдет точку монтирования тома и занимаемое им место
du -sh $(docker volume inspect --format '{{ .Mountpoint }}' Volume_Name)
Volumes backup
Данные хранимые в volumes полностью не застрахованы от утери, их необходимо резервировать.
Simple backup
Самый простой способ создать бэкап docker volume - запустить команду резервного копирования во временном контейнере, в который будет монтирован целевой volume.
Сначала нужно определить имя резервируемого тома
Затем нужно запустить контейнер, в который будет примонтирован целевой том и расшарена директория на хосте, в которую будет записан архив с данными из volume
docker run --rm \
--mount source=volume1,target=/data \ # указывает какой том и в какое место в контейнере монтируем
-v $(pwd)/backup:/backup \ # указывем директоию хоста, куда будет сохранена резервная копия
busybox \
tar -czvf /backup/volume1-backup.tar.gz /data # задаем имя бэкапа
Далле с архивом можно делать все, что угодно. Например, передать на другой сервер. Или воссановить эти данные.
Допустим, нам необходимо создать новый volume из имеющегося backup.
docker run --rm \
--mount source=volume-new,target=/data \ # указывает какой том и в какое место в контейнере монтируем
-v $(pwd)/backup:/backup \ # указывем директоию хоста, где сохранена резервная копия
busybox \
cd /data && tar xvf /backup/volume1-backup.tar.gz # распаковываем архив в целевую папку
Подробно про резервное копирование томов
Back up, restore, or migrate data volumes
docker-volume-backup with service
В docker compose для резервного копирования томов удобно использовать сервис резервного копирования, который создается из готового образа.
---
services:
my-service:
# ...
volumes:
- data:/var/my-app
labels:
# перед созданием резервной копии, контейнер будет остановлен
- docker-volume-backup.stop-during-backup=true
backup:
image: offen/docker-volume-backup:latest
restart: always
env_file: ./backup.env # настроики резервирования можно задать в env файле
environment:
BACKUP_CRON_EXPRESSION: "0 3 * * *" # установка расписания
BACKUP_FILENAME: backup-%Y-%m-%dT%H-%M-%S.tar.gz
BACKUP_LATEST_SYMLINK: backup-latest.tar.gz
BACKUP_PRUNING_PREFIX: backup- # задаем настройки ротации бэкапов
BACKUP_RETENTION_DAYS: 7
volumes:
- data:/backup/my-service-backup:ro # указываем какие тома следует бэкапить
- /var/run/docker.sock:/var/run/docker.sock:ro # используется для перезапуска контейнера
- /path/to/local_backups:/archive # указываем директория для хранения бэкапов на хосте
volumes:
data:
docker-volume-backup утилита
Восстановление томов из резервной копии
Самый простой способ востановления volume:
- Остановить контейнер(ы), использующие том.
- При необходимости, можно удалить восстанавливаемый том
docker volume rm vol_name
- При необходимости, можно удалить восстанавливаемый том
- Распоковать резервную копия во временную директорию
- Используя временный одноразовый контейнер, смонтируйте том и скопируйте резервную копию.
Bash
# Создаем контейнер и монтируем в него volume, который необходимо восстановить docker run -d --name temp_restore_container -v restore_vol_name:/backup_restore alpine # Копируем разархивированную директорию внутрь контейнера docker cp /tmp/docker-vol-backup temp_restore_container:/backup_restore # Останавливаем и удаляем контейнер docker stop temp_restore_container docker rm temp_restore_container - Перезапускаем контейнеры использующие восстановленный том
Bind mounts
Прямое монтирование директорий хостовой машины внутрь контейнера позволяет реализовать простой способ совместного доступа к файлам как из контейнера, так и из хоста.
Пробрасывание директории таким способом в контейнер можно сравнить с расшариванием папки.
Монтирование директорий часто применяется для удобного изменения конфигурационных файлов, запущенного в контейнере приложения.
У bind mount есть опция read only, которая защищает данные хранящиеся на хосте от изменений из контейнера.
# Так в контейнер можно пробросить информацию о часовом поясе хоста
docker run -d \
-- name my_container
-v /etc/timezone:/etc/timezone:ro \
-v /etc/localtime:/etc/localtime:ro
nginx:latest
tmpfs mounts
Монтирование tmpfs сохраняет файлы непосредственно в памяти хост-машины, гарантируя, что данные не будут записаны на диск. Это хранилище является эфемерным: данные теряются при остановке или перезапуске контейнера или при перезагрузке хоста. Монтирования tmpfs не сохраняют данные ни на хосте Docker, ни в файловой системе контейнера.
Важно понимать, что tmpfs-mount - это временные данные и хранятся они в
linux tmpfsхостового компьютера!
Linux tmpfs
Свойство tmpfs позволяет создавать файловые системы, чьё содержимое находится в виртуальной памяти. Так как файлы в таких файловых системах, обычно, располагаются в оперативной памяти, то доступ к файлах очень быстр.
Обычно tmpfs – это файловая система, которая хранится в оперативной памяти (RAM) и/или свопе, а не на диске; она монтируется в различные каталоги для временного хранения данных, чаще всего в /tmp, /run, /var/run, /dev/shm, чтобы обеспечить сверхбыстрый доступ к временным файлам, которые исчезают после перезагрузки.
Такой способ монтирования подходит для сценариев, требующих временного хранения в памяти, таких как кэширование промежуточных данных, обработка конфиденциальной информации или сокращение дискового ввода-вывода. Поскольку данные будут писаться в оперетивную память - это может повысить производительность контейнера.
Отличительной особенностью tmpfs-mount является то, что его нельзя расшарить ни между контейнерами, ни между хостовой системой.
Overlay
Overlay использует метод каскадного объединения (overlayfs), который позволяет объединить несколько слоев файловой системы в одну целевую файловую систему. Каждый слой файловой системы представляет собой отдельный файловый набор, который может быть только для чтения или для записи.
Когда контейнер запускается, Docker создает временный слой файловой системы (так называемый "верхний слой") для записи изменений, которые происходят в контейнере. Этот верхний слой монтируется поверх других слоев файловой системы, образующих базовую часть контейнера. При этом изменения, внесенные в файлы, сохраняются только в верхнем слое, что позволяет контейнерам использовать общие слои и экономить место на диске.
В целом, образы контейнеров со многими этапами сборки довольно многослойны,и иногда это может стать проблемой. Для минимизации количества слоев существуют различные стратегии, такие как объединение команд RUN и многоуровневых построений.
Overlay является одним из наиболее эффективных и широко используемых методов объединения файловых систем в Docker, так как он обеспечивает быстрый доступ к данным, низкое потребление ресурсов и поддерживает копирование на запись (copy-on-write) для эффективного управления ресурсами.
Как Docker использует стратегию copy-on-write в overlay2
При старте контейнера, создается "верхний" слой в который контейнер может писать данные, при необходимости. Если возникает ситуация при которой требуется изменить файл из "нижних", доступных только для чтения слоев, применяется механизм copy-on-write:
- Осуществляется сканирование всех слоев сверху-вниз, пока не будет найден файл, который требуется изменить. Найденный файл помещается в cache, для того чтобы ускорить последующие операции.
- Осуществляется операция
copy_up, которая копирует найденный файл в "верхний", доступный для записи, слой контейнера. - После изменения данного файла, контейнер больше "не видит" исходный файл с "нижних" слоев, и знает только про новый, измененный им файл.
Так работает стратегия copy-on-write для драйвера overlay2, для других драйверов (Btrfs, ZFS) она будет работать иначе.
Важно понимать, что если в контейнере запускается приложение, которое активно использует файловый ввод-вывод, необходимо монтировать
volumeи все файловые операции осуществлять через него. Это, и убережет данные, и ускорит работу приложения.
Все слои всех загруженных образов и запущенных контейнеров хранятся в директории /var/lib/docker/overlay2. Каждый слои хранится в отдельной папке
# В директории каждого слоя хранится несколько папок и файлов
ls -lah /var/lib/docker/overlay2/0317b80cb5a892091c8116d658b8e946459c3ebab3f4de9f5362e77bf5a6f9ca
итого 36K
drwx--x--- 4 root root 4,0K ноя 21 2024 .
drwx--x--- 125 root root 16K дек 18 16:52 ..
-rw------- 1 root root 0 ноя 21 2024 committed #
drwxr-xr-x 5 root root 4,0K ноя 21 2024 diff # хранится содержание данного слоя
-rw-r--r-- 1 root root 26 ноя 21 2024 link # хранит сокращенное наименование
-rw-r--r-- 1 root root 144 ноя 21 2024 lower # хранит ссылки на родительские слои
drwx------ 2 root root 4,0K ноя 21 2024 work # служебная директория OverlayFS
У слоев, которые имеют родителей будет еще одна диретория merged, в которой хранится объединенный контент всех родительских директорий.

Директоия для каждого отдельного слоя хранит файлы, которые уникальны для этого слоя, а на "нижние" слои хранятся "жесткие" ссылки. Это позволяет эффективно использовать дисковое пространство хоста.
У работающего контейнера также есть своя директория с определенным контентом.
ls -l /var/lib/docker/overlay2/<directory-of-running-container>
total 16
-rw-r--r-- 1 root root 64 Jun 20 16:39 lower-id # Содержит ID верхнего слоя образа, из которого собран контейнер
drwxr-xr-x 1 root root 4096 Jun 20 16:39 merged # Содержит итоговую версия ФС контейнера
drwxr-xr-x 4 root root 4096 Jun 20 16:39 upper # Содержит файлы и папки, которые изменил контейнер. По сути - это "верхний" слой
drwx------ 3 root root 4096 Jun 20 16:39 work # служебная директория OverlayFS
Как контейнер читает файлы
- Искомого файла нет в слое контейнера. Чтение происходит из "нижнего" слоя docker-image.
- Искомый файл есть только в слое контейнера. Чтение происходит напрямую из слоя контейнера.
- Искомый файл есть и слое контейнера и в слое образа. Читаеся "верхняя" версия файла, а "нижняя" версия файла игнорируется.
Как контейнер изменяет существующие файлы
Если при работе контенера возникла ситуация при которой требуется изменить файл лежащий в "нижних" слоях образа, файл будет скопирован с помощью операции copy_up в "верхний" слой контейнера, и уже там изменен. Про старую версия файла из "нижнего" слоя контейнер "забудет".
Как контейнер удаляет существующие файлы и директории
- Если внутри контейнера происходит удаление файла из "нижних" слоев, в "верхнем" слое контейнера создается специальный
whiteoutфайл, после чего контейнер больше "не видит" "удаленный" файл. На самом деле "удаленный" файл из нижнего слоя остается нетронутым, поскольку все "нижние" слои доступны контейнеру только для чтения. - Если внутри контейнера происходит удалени директории из "нижнего" слоя, происходит аналогичная ситуация: в "верхнем" слое контейнера создается специальная
opaque directory, а исхордная директория из нижнего слоя остается нетронутой.
Порядок слоев имеет значение
Если в объединяемых слоях содержаться одинаковые файлы, то в итоговую merged-версию ФС контейнера попадет версия файла из более "верхних" слоев. Другими словами, директории и файлы верхних слоев перекрывают нижние.