Docker volumes: как правильно хранить данные контейнеров, делать бэкапы и не потерять базу при пересоздании

Почему данные исчезают при пересоздании контейнера​


Контейнер — это изолированный процесс с собственной файловой системой, собранной из слоёв образа. Все записи, которые приложение делает внутри контейнера, попадают в верхний записываемый слой (writable layer). При удалении контейнера этот слой удаляется вместе с ним. Если база данных, логи или загруженные файлы не вынесены во внешнее хранилище, они теряются безвозвратно.

Docker предоставляет три механизма для сохранения данных за пределами writable layer:

МеханизмГде хранитсяУправляется черезТипичный сценарий
Named volume/var/lib/docker/volumes/docker volumeБазы данных, состояния сервисов
Bind mountПроизвольный путь на хостеУказывается при запускеКонфиги, разработка, общие файлы
tmpfs mountОперативная памятьУказывается при запускеСекреты, кэш, временные данные

Named volumes: основной инструмент для продакшена​


Named volume — это именованная область хранения, которой управляет Docker.Volume создаётся отдельно от контейнера и сохраняет данные после остановки и удаления контейнера.

Создание и подключение​


Bash:
## Создание volume
docker volume create pgdata

## Запуск контейнера с подключением volume
docker run -d \
  --name postgres \
  -v pgdata:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=secret \
  postgres:16

Флаг -v принимает формат имя_тома:путь_в_контейнере. Если том с таким именем не существует, Docker создаст его автоматически.

Управление томами​


Bash:
## Список всех томов
docker volume ls

## Информация о конкретном томе
docker volume inspect pgdata

## Удаление тома (только если он не используется ни одним контейнером)
docker volume rm pgdata

## Удаление всех неиспользуемых томов
docker volume prune

Команда docker volume inspect показывает точку монтирования на хосте, драйвер и метки. По умолчанию данные лежат в /var/lib/docker/volumes/<имя>/_data/.

Анонимные тома​


Если в Dockerfile образа объявлен VOLUME /var/lib/postgresql/data, Docker создаст анонимный том при запуске контейнера даже без явного указания -v. Анонимные тома не имеют читаемого имени и после удаления контейнера остаются в системе как мусор, если не запущен docker volume prune. Это частая причина разрастания /var/lib/docker/volumes/.

Bind mounts: прямой доступ к файловой системе хоста​


Bind mount привязывает конкретный путь на хосте к пути внутри контейнера. В отличие от named volume, bind mount не управляется Docker и существует как обычный каталог.

Bash:
docker run -d \
  --name nginx \
  -v /opt/nginx/conf.d:/etc/nginx/conf.d:ro \
  -v /opt/nginx/html:/usr/share/nginx/html \
  nginx:latest

Суффикс :ro делает монтирование доступным только для чтения — контейнер не сможет изменить файлы конфигурации.

Когда использовать bind mount вместо volume​


  • Разработка: монтирование исходного кода для горячей перезагрузки.
  • Конфигурационные файлы, которые редактируются на хосте.
  • Совместный доступ нескольких сервисов к одним файлам (например, общая директория загрузок).

Ограничения и риски bind mounts​


  • Путь на хосте должен существовать до запуска контейнера, иначе Docker создаст пустой каталог с правами root.
  • Нет защиты от случайного удаления: rm -rf /opt/nginx/html на хосте мгновенно уничтожает данные контейнера.
  • Производительность на macOS и Windows (Docker Desktop) заметно ниже из-за виртуализации файловой системы.
  • SELinux и AppArmor могут блокировать доступ, если контекст безопасности не настроен.

tmpfs: данные только в памяти​


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

Bash:
docker run -d \
  --name app \
  --tmpfs /run/secrets:size=64m \
  myapp:latest

Параметр size ограничивает максимальный объём. Если приложение запишет больше, получит ошибку ENOSPC.

Пересоздание контейнера без потери данных​


Типичный сценарий обновления: удалить старый контейнер и запустить новый с тем же томом.

Bash:
## Остановка и удаление контейнера (том остаётся)
docker stop postgres
docker rm postgres

## Запуск новой версии с тем же томом
docker run -d \
  --name postgres \
  -v pgdata:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=secret \
  postgres:17

Том pgdata не привязан к конкретному контейнеру, поэтому данные сохраняются. Главное — не удалить сам том командой docker volume rm или docker system prune -a --volumes.

Опасная команда: docker system prune --volumes​


Флаг --volumes в docker system prune удаляет все неиспользуемые тома. Если контейнер остановлен, его тома считаются неиспользуемыми. Перед выполнением этой команды убедитесь, что нужные тома подключены к работающим контейнерам или защищены метками.

Бэкап и восстановление volume​


Бэкап через временный контейнер​


Стандартный способ — запустить одноразовый контейнер, который монтирует целевой том и архивирует его содержимое.

Bash:
docker run --rm \
  -v pgdata:/source:ro \
  -v /backup:/backup \
  alpine:latest \
  tar czf /backup/pgdata_$(date +%Y%m%d_%H%M%S).tar.gz -C /source .

Разбор флагов:

  • --rm — контейнер удаляется после завершения.
  • -v pgdata:/source:ro — целевой том монтируется только для чтения.
  • -v /backup:/backup — каталог на хосте для сохранения архива.
  • tar czf — создание gzip-архива.

Восстановление из бэкапа​


Bash:
docker run --rm \
  -v pgdata:/target \
  -v /backup:/backup:ro \
  alpine:latest \
  tar xzf /backup/pgdata_20250101_030000.tar.gz -C /target

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

Бэкап на уровне приложения​


Для баз данных tar файловой системы — не всегда корректный подход. PostgreSQL, MySQL и MongoDB имеют собственные утилиты логического дампа (pg_dump, mysqldump, mongodump), которые гарантируют консистентность. Файловый бэкап подходит для простых случаев или при использовании файловых систем с поддержкой снапшотов.

Docker Compose: декларативное управление томами​


В docker-compose.yml тома объявляются в секции volumes верхнего уровня и подключаются в сервисах.

YAML:
services:
  db:
    image: postgres:16
    volumes:
      - dbdata:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: secret

volumes:
  dbdata:

При docker compose down тома сохраняются. Флаг --volumes удаляет их вместе с контейнерами:

Bash:
## Безопасная остановка — тома остаются
docker compose down

## Опасно: удаляет и тома
docker compose down --volumes

Диагностика: куда смотрят данные контейнера​


Bash:
## Посмотреть все монтирования конкретного контейнера
docker inspect --format='{{json .Mounts}}' postgres | python3 -m json.tool

## Проверить размер тома на хосте
du -sh /var/lib/docker/volumes/pgdata/_data/

## Найти контейнеры, использующие конкретный том
docker ps -a --filter volume=pgdata

Если контейнер не видит ожидаемые данные, проверьте:

  1. Правильность пути внутри контейнера (частая ошибка — лишний слеш или неверный каталог).
  2. Права доступа: процесс в контейнере может работать от непривилегированного пользователя.
  3. Не перекрывает ли том пустой каталог из образа при первом запуске.

Типичные ошибки и как их избежать​


ОшибкаПоследствиеРешение
docker system prune --volumes при остановленном контейнереУдаление данныхНе использовать --volumes без проверки
Bind mount на несуществующий путьDocker создаёт пустой каталог от rootСоздавать каталог заранее с нужными правами
Пересоздание контейнера без указания того же томаДанные теряютсяВсегда явно указывать -v имя:путь
Бэкап тома работающей БД через tarНеконсистентный дампИспользовать pg_dump / mysqldump или останавливать сервис
Анонимные тома от VOLUME в DockerfileНакопление мусораПериодически запускать docker volume prune

Проверка результата после настройки​


После создания контейнера с томом убедитесь, что данные действительно переживают пересоздание:

Bash:
## Записать тестовый файл
docker exec postgres touch /var/lib/postgresql/data/testfile

## Пересоздать контейнер
docker stop postgres && docker rm postgres
docker run -d --name postgres -v pgdata:/var/lib/postgresql/data -e POSTGRES_PASSWORD=secret postgres:16

## Проверить наличие файла
docker exec postgres ls /var/lib/postgresql/data/testfile

Если файл на месте — том подключён корректно и данные переживут любое пересоздание контейнера.

Источники​


 
Назад
Верх Низ