6. Тома и хранилище (Volumes & Storage)
Цель темы: понимать персистентность данных в контейнерах: различать volumes и bind mounts, уметь использовать tmpfs, учитывать права доступа (UID/GID), организовывать резервное копирование и обновление контейнеров без потери данных.
Определения терминов
Volume (том)
Volume — каталог на хосте, управляемый Docker (по умолчанию в /var/lib/docker/volumes/). Том привязывается к пути внутри контейнера; данные в томе сохраняются при удалении контейнера. Том можно использовать разными контейнерами, именовать и создавать заранее. Предпочтительный способ хранения персистентных данных в Docker.
Bind mount
Bind mount — монтирование произвольного каталога или файла с хоста в контейнер. Путь на хосте задаётся явно. Данные живут на хосте; контейнер видит их «как есть». Удобно для разработки (исходники на хосте) и когда путь на хосте жёстко задан (конфиги, сертификаты). При удалении контейнера данные на хосте не трогаются.
tmpfs mount
tmpfs — монтирование области в оперативную память. Данные существуют только пока контейнер запущен; после остановки они исчезают. Подходит для временных, чувствительных или высоконагруженных по I/O данных (кэш, сессии, /tmp).
Volume driver
Volume driver — плагин, который реализует создание и управление томами. Драйвер по умолчанию — local (каталог на диске хоста). Другие драйверы (NFS, облачные хранилища, распределённые ФС) позволяют хранить данные на удалённых или распределённых системах.
Volumes vs bind mounts
| Критерий | Volume | Bind mount |
|---|---|---|
| Расположение на хосте | Управляет Docker (каталог в /var/lib/docker/volumes/) |
Задаёте вы (любой путь) |
| Создание | docker volume create или при первом run |
Путь должен существовать (или создаётся при run с опцией) |
| Переносимость | Не привязан к конкретному пути хоста | Зависит от структуры каталогов хоста |
| Производительность | На Linux обычно без лишних накладных расходов | Прямой доступ к ФС хоста |
| Типичное использование | Данные приложений, БД | Исходники, конфиги, сертификаты с хоста |
Практика
Для данных, которые должны переживать контейнер (БД, загрузки, состояние приложения), предпочтительны volumes. Bind mount — когда нужен прямой доступ к конкретному месту на хосте.
Работа с volumes
Создание и использование
docker volume create mydata
docker run -d --name db -v mydata:/var/lib/postgresql/data postgres
Путь в контейнере: /var/lib/postgresql/data; данные хранятся в томе mydata.
Анонимные тома
Если указать только путь в контейнере (без имени тома), Docker создаст анонимный том — он будет удалён при docker rm -v или при docker volume prune.
docker run -v /var/lib/app data myapp
Просмотр и удаление
docker volume ls
docker volume inspect mydata
docker volume rm mydata
docker volume prune # удалить неиспользуемые тома
В Dockerfile: VOLUME
Инструкция VOLUME в Dockerfile объявляет точку монтирования. Не создаёт том при сборке; при запуске без -v создаётся анонимный том. Используется, чтобы явно обозначить каталоги с данными.
VOLUME /var/lib/postgresql/data
Bind mounts
Монтирование каталога
docker run -v /host/path:/container/path myapp
# или явный синтаксис:
docker run --mount type=bind,source=/host/path,target=/container/path myapp
Каталог /host/path на хосте должен существовать (или указать :ro только для чтения). На Windows/macOS пути отличаются (например, через Docker Desktop).
Монтирование файла
docker run -v /host/config.json:/app/config.json:ro myapp
Файл на хосте подставляется в контейнер; :ro — только чтение.
Read-only
docker run -v mydata:/data:ro myapp
Контейнер не сможет писать в смонтированный том/каталог.
Внимание
При bind mount с хоста в контейнер с root внутри контейнера созданные файлы могут принадлежать root на хосте. Учитывайте UID/GID (см. ниже).
tmpfs
Данные в RAM; после остановки контейнера пропадают.
docker run --tmpfs /tmp:size=100M,mode=1777 alpine
# или через --mount:
docker run --mount type=tmpfs,target=/cache,tmpfs-size=100000000 myapp
Ограничение размера (size/tmpfs-size) и режим каталога задаются при создании контейнера.
Volume drivers
Драйвер по умолчанию — local. Для NFS, облака или других бэкендов нужен соответствующий плагин и указание драйвера при создании тома.
docker volume create --driver local \
--opt type=nfs \
--opt o=addr=192.168.1.1,rw \
--opt device=:/path/on/nfs \
nfsdata
docker run -v nfsdata:/data myapp
Точный синтаксис зависит от драйвера (см. документацию плагина).
Права доступа (UID/GID)
Контейнер и хост разделяют одни и те же UID/GID в рамках одного ядра. Если процесс в контейнере пишет в volume или bind mount от root (UID 0), на хосте файлы тоже будут от root. Если приложение в контейнере работает от непривилегированного пользователя (например, UID 1000), файлы на томе будут с UID 1000; на хосте их может быть неудобно редактировать под другим пользователем.
Решения
- В образе: задать USER с нужным UID (например, совпадающим с пользователем на хосте для bind mount в разработке).
- На хосте: перед первым запуском создать каталог и выставить владельца:
chown 1000:1000 /host/data. - При монтировании: часть драйверов и опций (зависит от версии и драйвера) позволяют задать UID/GID для корня тома.
При использовании volumes Docker создаёт каталог с правами root по умолчанию; приложение в контейнере, работающее от non-root, может не иметь записи. Тогда в Dockerfile создают пользователя с нужным UID и делают USER; либо при первом запуске инициализируют каталог тома от root (entrypoint), затем переключаются на приложение.
Production
БД в контейнере: данные в named volume (-v pgdata:/var/lib/postgresql/data). При обновлении образа контейнер пересоздаётся, том остаётся — данные сохраняются. Backup: делают дамп из контейнера (docker exec db pg_dump ...) или снапшот тома/ФС на хосте.
Резервное копирование и восстановление
Дамп из контейнера
Данные в томе доступны процессу внутри контейнера. Резервная копия — через команду внутри контейнера (дамп БД, архив каталога).
docker exec db pg_dump -U postgres mydb > backup.sql
docker exec app tar czf - /var/lib/app/data > data.tar.gz
Восстановление: скопировать файл в контейнер и выполнить restore (или примонтировать и запустить restore-скрипт).
Копирование из/в том через временный контейнер
docker run --rm -v mydata:/data -v $(pwd):/backup alpine tar czf /backup/mydata.tar.gz -C /data .
docker run --rm -v mydata:/data -v $(pwd):/backup alpine tar xzf /backup/mydata.tar.gz -C /data
Том должен быть примонтирован к работающему контейнеру; здесь используется одноразовый контейнер только для tar.
Паттерны использования
| Паттерн | Описание |
|---|---|
| Named volumes для данных приложений и БД | Данные переживают контейнер; обновление образа без потери данных. |
| Bind mount для конфигов и сертификатов | Явный путь на хосте, обновление без пересборки образа. |
| tmpfs для временных и чувствительных данных | Кэш, сессии, /tmp — не писать на диск. |
| Учитывать UID/GID | Чтобы не было проблем с правами на хосте и в томах, задавать USER в образе и при необходимости инициализировать каталоги. |
Антипаттерны
| Антипаттерн | Почему плохо | Что делать |
|---|---|---|
| Хранить важные данные только в слое контейнера | При удалении контейнера данные теряются. | Использовать named volumes для персистентных данных. |
| Писать большие объёмы в слой контейнера | Раздувание верхнего слоя overlay, медленная работа. | Вынести данные в тома. |
| Игнорировать права при bind mount | Файлы от root на хосте или отказ в записи в контейнере. | Согласовать UID/GID (USER в образе, chown на хосте). |
| Не планировать backup для томов с БД | При сбое нет точки восстановления. | Регулярные дампы или снапшоты тома/ФС. |
Практика: БД в контейнере и обновление без потери данных
БД в контейнере
docker volume create pgdata
docker run -d --name db \
-v pgdata:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=secret \
postgres:15
Данные в томе pgdata. При docker rm db том остаётся. Новый контейнер с тем же томом подхватит данные:
docker run -d --name db \
-v pgdata:/var/lib/postgresql/data \
postgres:15
Обновление образа без потери данных
- Остановить контейнер:
docker stop db. - Сделать backup при необходимости:
docker exec db pg_dump ...(до остановки) или снапшот тома. - Удалить контейнер:
docker rm db. - Запустить новый контейнер с тем же томом и новым тегом образа:
docker run -d --name db -v pgdata:/var/lib/postgresql/data postgres:16.
Том не привязан к контейнеру; данные сохраняются. Важно совместимость формата данных между версиями БД (миграции при необходимости).
Примеры из production
Миграция БД при обновлении
Перед переходом на новую мажорную версию СУБД делают дамп, проверяют совместимость, при необходимости выполняют миграции в тестовом окружении. В production обновление образа с тем же томом только при поддержке обратной совместимости данных или после миграционного скрипта.
Общий том для нескольких контейнеров
Несколько контейнеров могут монтировать один и тот же том (read-only или read-write). Например, один пишет логи/данные, другой читает для анализа. Следует учитывать блокировки и целостность данных (файловая система не координирует одновременную запись разными процессами).
Backup томов в облаке
Периодически создавать архив тома (через временный контейнер с tar или через снапшот диска/ФС на хосте) и загружать в объектное хранилище (S3, GCS). Восстановление — скачать архив и развернуть в новый том.