Постоянное хранилище сохраняет данные при замене подключённых к нему контейнеров
DevOps

Docker volumes и bind mounts: где хранить постоянные данные

Выбираем между named volume и bind mount, подключаем данные безопасно, проверяем переживание пересоздания контейнера и готовим восстановимую копию.

Содержание

При запуске Docker добавляет поверх образа записываемый слой: в него попадают новые и изменённые файлы конкретного контейнера. Этот слой исчезает вместе с контейнером. Поэтому базу данных, пользовательские загрузки и другие постоянные файлы подключают отдельно — через Docker volume или каталог самого сервера.

Как выбрать место для данных

Для данных, которыми управляет приложение, обычно выбирайте named volume. Для файлов, которые должен видеть и контролировать администратор на host, используйте точный bind mount, по возможности read-only. В обоих случаях заранее определите способ согласованного backup и проверьте восстановление: постоянное хранилище защищает от пересоздания контейнера, но не от удаления, повреждения или ошибки приложения.

Чем отличаются три способа хранения

Записываемый слой контейнера

Подходит для временных файлов, которые можно потерять при замене экземпляра. Не размещайте здесь единственную копию базы, пользовательских загрузок или важных ключей.

Именованный Docker volume

Docker создаёт и обслуживает место хранения, а контейнер получает его по имени. Такой volume не зависит от идентификатора контейнера и остаётся после обычного docker rm.

docker volume create app-data
docker run -d --name data-demo \
  --mount type=volume,src=app-data,dst=/data \
  alpine:3.22 sh -c 'date -u > /data/created.txt; while :; do sleep 3600; done'

Синтаксис --mount длиннее -v, зато явно показывает тип, источник и назначение. Это снижает риск принять host path за имя volume.

Подключённый каталог сервера

Конкретный путь хоста, то есть машины с Docker, подключается внутрь контейнера. В документации Docker это называется bind mount. Он удобен для конфигурации, исходников в разработке, выгрузок и файлов, которыми управляют обычные системные инструменты.

docker run --rm \
  --mount type=bind,src=/srv/example/config,dst=/app/config,readonly \
  alpine:3.22 ls -la /app/config

Путь /srv/example/config должен существовать на Docker host. Процесс внутри контейнера по умолчанию способен изменять writable bind mount, поэтому не подключайте широкие каталоги вроде /, /etc или домашнего каталога. Если запись не нужна, задавайте readonly.

Проверьте, что volume переживает контейнер

Пока data-demo работает, посмотрите его подключения и прочитайте созданный файл. Обе команды только выводят данные и не меняют volume:

docker inspect data-demo --format '{{json .Mounts}}'
docker exec data-demo cat /data/created.txt

Удалите контейнер и подключите тот же volume к новому:

docker rm -f data-demo
docker run --rm \
  --mount type=volume,src=app-data,dst=/data,readonly \
  alpine:3.22 cat /data/created.txt

Если вывод сохранился, данные живут вне writable layer. Это не тест резервной копии: повреждение app-data затронет все контейнеры, которые его используют.

Выберите mount по назначению

Named volume подходит, когда:

  • приложению нужен постоянный каталог данных;
  • host не должен редактировать файлы вручную;
  • стек требуется переносить без привязки к одинаковой структуре каталогов;
  • хранилищем удобнее управлять командами Docker.

Bind mount подходит, когда:

  • конфигурация хранится в известном каталоге host;
  • код монтируется в dev-контейнер;
  • результат должен сразу появляться в обычной файловой системе;
  • внешний backup-агент читает конкретный host path.

Не монтируйте один каталог поверх директории образа, не проверив её содержимое: mount скрывает находившиеся там файлы. Если после подключения приложение перестало видеть встроенную конфигурацию, пересоздайте контейнер без mount или подключите его в другой путь.

Права принадлежат числовым UID и GID

Docker не переводит автоматически пользователя контейнера в одноимённого пользователя host. В Linux ядро проверяет числовые UID и GID. Сначала узнайте пользователя процесса и владельца каталога:

docker inspect --format '{{.Config.User}}' data-demo
docker exec data-demo id
docker exec data-demo stat -c '%u:%g %a %n' /data

Не лечите Permission denied командой chmod -R 777. Определите UID/GID процесса, назначьте минимальные права только целевому каталогу и повторите проверку записи. Для готового образа также читайте документацию издателя: некоторые контейнеры меняют пользователя при старте или сами подготавливают каталог.

Создайте и проверьте тестовую копию файлов

Для каталога с простыми файлами можно создать архив через временный контейнер:

mkdir -p ./backup
docker run --rm \
  --mount type=volume,src=app-data,dst=/source,readonly \
  --mount type=bind,src="$(pwd)/backup",dst=/backup \
  alpine:3.22 tar -C /source -czf /backup/app-data.tar.gz .

Проверьте список файлов, не распаковывая архив поверх оригинала:

tar -tzf ./backup/app-data.tar.gz | head

Для базы данных простой tar работающего каталога может быть несогласованным. Используйте pg_dump, mariadb-dump или другой штатный механизм, либо корректно остановите запись перед файловой копией. Копию храните вне Docker host и периодически восстанавливайте в отдельный volume.

Пример тестового восстановления:

docker volume create app-data-restore-test
docker run --rm \
  --mount type=volume,src=app-data-restore-test,dst=/restore \
  --mount type=bind,src="$(pwd)/backup",dst=/backup,readonly \
  alpine:3.22 tar -C /restore -xzf /backup/app-data.tar.gz

docker run --rm \
  --mount type=volume,src=app-data-restore-test,dst=/data,readonly \
  alpine:3.22 find /data -maxdepth 2 -type f

После проверки удалите только тестовые ресурсы:

docker volume rm app-data-restore-test

Основной app-data удаляйте лишь после отдельного подтверждения, что он больше не нужен и восстановимая копия существует. Команды docker volume prune и docker compose down -v способны удалить данные нескольких сервисов или всего Compose-проекта.

Что проверить перед эксплуатацией

  • точный каталог данных взят из документации образа;
  • mount виден в docker inspect и действительно имеет нужный режим;
  • новый контейнер читает данные после удаления старого;
  • UID/GID и права не требуют запуска от root;
  • backup создаётся согласованно и хранится вне узла;
  • восстановление выполнено в отдельное место и проверено приложением;
  • процедура удаления требует явного выбора конкретного volume.

Основы жизненного цикла контейнера описаны в материале о run, exec и inspect. В следующем шаге пригодится статья о сетях и публикации портов, а управление несколькими mounts станет проще после перехода к Docker Compose.

Источники

Рекламное местоВаша компания здесьРазместить рекламу

Самопроверка

Проверьте, что материал усвоен

Ответьте на все вопросы. Результат сохранится только в этом браузере и будет учтён в статистике прочитанных материалов.

01Какой объект переживает обычное удаление использующего его контейнера?
02Как безопаснее подключить конфигурационный файл, который контейнер не должен менять?
03Что обязательно подтверждает пригодность резервной копии?

Разбираем коротко

Частые вопросы

Сохранятся ли данные named volume после удаления контейнера?

Да, жизненный цикл named volume отделён от контейнера. Но volume можно удалить отдельной командой или параметром docker compose down -v, поэтому резервная копия всё равно необходима.

Когда bind mount лучше Docker volume?

Когда файл должен явно храниться и редактироваться на host: например, конфигурация, каталог исходников при разработке или экспорт. Для данных приложения обычно удобнее named volume.

Достаточно ли скопировать каталог базы данных командой tar?

Не всегда. Работающая база может изменить связанные файлы во время копирования. Используйте штатный логический backup или согласованную остановку и обязательно проверяйте восстановление.