Skip to main content

Що таке Docker volume і навіщо він потрібен?

Docker volume це стандартний спосіб тримати дані живими через рестарти і ребілди container. Container задумано як замінні; volume це частина, що не замінна.

Теорія

TL;DR

  • Volume це named, Docker-managed директорія поза файловою системою будь-якого container.
  • Container A пише у /data (змонтовано з volume pgdata); ти робиш docker rm A; створюєш container B з тим самим mount; B бачить дані A.
  • Volume лежить під /var/lib/docker/volumes/<name>/_data на Linux. На Mac/Windows за Docker Desktop VM.
  • Створюється через docker volume create або неявно при першому посиланні через docker run -v.
  • Рекомендований спосіб обробки персистентного стану. Альтернатива (писати у writable-шар container) втрачає дані при видаленні container.

Швидкий приклад

bash
# Створимо volume явно (або пропустимо і дамо docker run створити) $ docker volume create pgdata pgdata # Запустимо базу, що зберігає файли у volume $ docker run -d --name db \ -v pgdata:/var/lib/postgresql/data \ -e POSTGRES_PASSWORD=devpass \ postgres:16 # Вставимо дані $ docker exec -it db psql -U postgres -c "CREATE TABLE users(id int);" # Знищимо container, volume і його дані лишаються $ docker rm -f db # Запустимо новий container; старі дані ще там $ docker run -d --name db2 \ -v pgdata:/var/lib/postgresql/data \ -e POSTGRES_PASSWORD=devpass \ postgres:16 $ docker exec db2 psql -U postgres -c "\\dt" # таблиця users все ще у списку

Чотири docker rm не змогли стерти дані. Саме для цього volume.

Чому container без volume втрачають дані

Файлова система container це шари image (read-only) плюс тонкий writable-шар зверху через OverlayFS. Записи у writable-шар йдуть через copy-on-write у upperdir. Коли робиш docker rm, upperdir видаляється. Усе, що там було, зникло.

Volume mount перехоплює записи у конкретний шлях і направляє їх поза union FS у host-директорію, якою Docker керує незалежно. Writable-шар container ніколи не тримає ці дані; docker rm їх не дістає.

Команди життєвого циклу volume

bash
docker volume create <name> # явне створення з опціями docker volume ls # список всіх volume docker volume inspect <name> # показати driver, mountpoint, labels docker volume rm <name> # видалити volume (не має бути used) docker volume prune # видалити всі unused volume

Більшість workflow пропускає volume create, бо docker run -v <name>:<path> авто-створює volume при першому використанні. Явне створення важливе, коли треба driver-специфічні опції (--driver, --driver-opt).

Синтаксис монтування: -v vs --mount

bash
# Коротка форма -v (компактна, поширена у туторіалах) docker run -v pgdata:/var/lib/postgresql/data postgres:16 # Розгорнута форма --mount (явна, рекомендована для проду) docker run --mount type=volume,source=pgdata,target=/var/lib/postgresql/data postgres:16

Обидві працюють. --mount більш розгорнута, але менш двозначна (немає «перша частина це host-шлях чи ім'я volume?»). Прод-скрипти і Compose-файли зазвичай беруть --mount.

Типові помилки

Класти стан у writable-шар

bash
# НЕПРАВИЛЬНО: записи йдуть у writable-шар container; зникають при rm $ docker run -d --name db postgres:16 $ docker rm -f db # бай, таблиці # ПРАВИЛЬНО: записи йдуть у volume; переживають видалення container $ docker run -d --name db -v pgdata:/var/lib/postgresql/data postgres:16

Перша форма виглядає, ніби працює (дані там, поки container крутиться) і тихо знищує дані при першому ж docker rm.

Забути, який шлях всередині container тримає стан

Кожен image документує свій data-шлях. Postgres використовує /var/lib/postgresql/data. Mongo використовує /data/db. Redis (з персистенцією) використовує /data. Змонтуй неправильний шлях, і змонтуєш порожню директорію, а реальний стан все одно піде у writable-шар.

Перевіряй doc image (або директиву VOLUME у його Dockerfile, що перелічує шляхи, які image очікує бути volume-mounted).

Видалити volume, який в use

bash
$ docker volume rm pgdata Error response from daemon: remove pgdata: volume is in use

Спочатку зупини і видали container, потім видаляй. Або docker rm -v, щоб видалити container разом з його анонімними volume.

Монтування non-empty шляху container поверх volume на першому запуску

Коли монтуєш named volume у шлях container, що вже має файли (з image), Docker копіює ці image-файли у порожній volume при першому старті. Після цього volume стає source of truth; апдейти image не поширюються. Дивує перший раз, коли апгрейдиш image бази і дивуєшся, чому нові файли не з'явилися.

Реальне застосування

  • Бази у проді: кожен Postgres, MySQL, Mongo, Redis container у проді використовує named volume для свого data-dir. Без винятків.
  • Локальна розробка з Docker Compose: блок volumes: нагорі compose.yaml визначає named volume; сервіси їх монтують. Зупиняй і піднімай стек скільки хочеш, дані зберігаються.
  • CI/CD тестові фікстури: test runner піднімає Postgres-container з named volume, проганяє міграції + seed один раз, потім перевикористовує volume між тестовими прогонами, щоб пропустити setup-час.
  • Аплоади застосунку: /app/uploads змонтовано у named volume для застосунку, що приймає user file uploads. Інакше кожен redeploy їх стирає.

Питання для поглиблення

Q: Що відбудеться, якщо я взагалі забуду флаг -v?


A: Усе, що container пише, іде у його writable-шар. Container працює нормально, поки крутиться. У момент docker rm весь цей стан зникає. Для ефемерних навантажень (CI білди, тести) це навмисно. Для баз або чогось важливого це data-loss баг.

Q: Де саме на моєму диску лежать файли volume?


A: На Linux: /var/lib/docker/volumes/<volume-name>/_data/. На macOS / Windows всередині Docker Desktop Linux VM (не видно прямо на host filesystem). Знайти шлях через docker volume inspect <name> --format '{{.Mountpoint}}'.

Q: Чи можуть два container поділяти volume?


A: Так. Змонтуй той самий volume у два container, і обидва бачать ті самі файли. Корисно для sidecar log shipper, shared cache або producer/consumer патернів. Конкурентні записи можуть один одного затирати; координуй на рівні застосунку.

Q: Що таке anonymous volume?


A: Volume, створений без імені (авто-згенерований UUID). Трапляється, коли Dockerfile має VOLUME /path без явного -v під час запуску. Легко загубити: docker volume ls -f dangling=true їх знаходить, docker volume prune видаляє. Краще named volume.

Q: (Senior) Коли б ти НЕ використав Docker volume?


A: Три кейси. (1) Для ефемерного стану (CI білди, single-test-run container) пропусти volume; дані задумано померти. (2) Для розробки, де хочеш host-side редагувати ті самі файли, бери bind mount замість volume. (3) Для стану, що вимагає конкретної файлової системи (high-IOPS NVMe, розподілена FS як NFS/Ceph) бери volume driver plugin або bind mount у конкретний filesystem mount.

Приклади

Postgres з персистентним volume

bash
$ docker volume create pgdata $ docker run -d --name pg \ -v pgdata:/var/lib/postgresql/data \ -e POSTGRES_PASSWORD=devpass \ -p 5432:5432 \ postgres:16 # Підключаємось, працюємо $ psql -h localhost -U postgres postgres=# CREATE DATABASE app; # Container знищено; volume лишається $ docker rm -f pg # Перестворюємо; дані ще там $ docker run -d --name pg \ -v pgdata:/var/lib/postgresql/data \ -e POSTGRES_PASSWORD=devpass \ -p 5432:5432 \ postgres:16 $ psql -h localhost -U postgres -c '\l' | grep app app | postgres | UTF8 | ...

База app пережила повне знищення container.

Приклад з Compose

yaml
# compose.yaml services: db: image: postgres:16 environment: POSTGRES_PASSWORD: devpass volumes: - pgdata:/var/lib/postgresql/data redis: image: redis:7 volumes: - redis-data:/data volumes: pgdata: redis-data:
bash
$ docker compose up -d # Postgres + Redis з персистентним станом. $ docker compose down # container зникли $ docker compose up -d # container повернулися; дані не зачеплені $ docker compose down -v # -v ще й стирає volume (деструктивно!)

docker compose down за замовчуванням лишає volume. Флаг -v їх видаляє, це kill switch для стану.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Коментарі

Ще немає коментарів