Почему данные исчезают при пересоздании контейнера
Контейнер — это изолированный процесс с собственной файловой системой, собранной из слоёв образа. Все записи, которые приложение делает внутри контейнера, попадают в верхний записываемый слой (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
Если контейнер не видит ожидаемые данные, проверьте:
- Правильность пути внутри контейнера (частая ошибка — лишний слеш или неверный каталог).
- Права доступа: процесс в контейнере может работать от непривилегированного пользователя.
- Не перекрывает ли том пустой каталог из образа при первом запуске.
Типичные ошибки и как их избежать
| Ошибка | Последствие | Решение |
|---|---|---|
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
Если файл на месте — том подключён корректно и данные переживут любое пересоздание контейнера.
