Volumes в Docker
Проброс папок и томов в Docker решает следующие проблемы, позволяя монтировать директории хоста внутри контейнера либо создавая централизованное хранилище. Таким образом мы получаем следующие преимущества:
- Мы не копируем данные, они хранятся в одно месте для всех контейнеров
- Раньше копирование отнимало время, а сейчас это делать не нужно, контейнеры запускаются быстрее
- У нас появляется больше возможностей для управления данными
Типы томов Docker
Есть два основных способа обмена данными с контейнером, которые часто называют томами:
- Перенаправление папки или файла с хоста в контейнер, называется
bind mount - Создание специального объекта, который имеет больше возможностей управления данными
volume
Основное различие этих двух типов в том, что для volume есть отдельные команды по его созданию, просмотру и удалению в самом Docker. Он так же представляет собой папку в файловой системе хоста, которая по умолчанию определена настройками Docker. Еще одно отличие, это поведение по умолчанию bind mount и volume. Для примера, внутри контейнера, по пути /usr/share/nginx/html/ лежит файл index.html. В случае проброса томов в эту папку поведение будет разным:
- В случае
bind mountфайлindex.html, внутри контейнера, будет удален. Это произойдет даже если папка хоста пустая - В случае
volumeпри первом использовании файлindex.htmlбудет скопирован. При последующих удален
Создание Volumes через docker run
Для монтирования данных используются следующие параметры:
-vили--volume--mount
Их различие в том, что bind mount более явно заставляет указывать источник и цель монтирования папки. Вы можете использовать эти параметры совместно, отдельно, повторяя несколько раз, ограничений нет.
Папки и файлы
Для примера, у меня есть следующая папка на хосте:
/home/alex/docker_data
В случае параметра -v указывается два пути откуда:куда. В случае --mount это именованные параметры разделенные запятыми, пример работы обоих:
-v /home/alex/docker_data:/usr/share/nginx/html
--mount type=bind,source=/home/alex/docker_data,destination=/usr/share/nginx/html
В mount мы используем следующие параметры:
typeсо значениемbindговорит, что мы монтируем папку или файлsourceисточник, папка или файл, который мы хотим подключить к контейнеруdestinationпапка или файл внутри контейнера
В обоих случаях мы можем монтировать данный доступные только для чтения read-only добавив ro в конце:
-v /home/alex/docker_data:/usr/share/nginx/html:ro
--mount type=bind,source=/home/alex/docker_data,destination=/usr/share/nginx/html,ro
Так выглядит запуск контейнера с проброшенной папкой:
docker run -d --name nginx_vol1 -v /home/alex/docker_data:/usr/share/nginx/html:ro nginx
docker run -d --name nginx_vol2 --mount type=bind,source=/home/alex/docker_data,destination=/usr/share/nginx/html,ro nginx
Можно задать следующие опции:
rprivateprivatersharedsharedrslaveslaveroрежим только для чтенияzпапка на хосте может быть использована несколькими контейнерамиZпапка используется только одним контейнером
Первые шесть параметров позволяют управлять тем, как будут влиять изменения в одной точке монтирования тома на другие точки монтирования. По умолчанию используется rprivate, что означает — никак.
Подключение volume
При монтировании тома нужно учитывать следующие моменты:
- Название тома указывается без слешей
- Если тома с этим названием нет, он будет создан
- В случае с
mount, в параметреtype, указываетсяvolume
При использовании docker run использование томов будет выглядеть так:
docker run -d --name nginx_vol1 -v docker_volume:/usr/share/nginx/html nginx
docker run -d --name nginx_vol2 --mount type=volume,source=docker_volume,destination=/usr/share/nginx/html nginx
Так же как и с папками мы можем добавить :ro или ,ro в конец значения, что бы дать права только на чтение директорий.
В предыдущем примере один том был подключен к двум контейнерам. Их совместную работу можно проверить создав файл в одном контейнере, а вывести через другой:
# создаем файл в одном контейнере
docker exec nginx_vol1 touch /usr/share/nginx/html/file1
# проверяем файл через другой контейнер
docker exec nginx_vol2 ls /usr/share/nginx/html
Создание Volumes через Dockerfile
При создании образа через Dockerfile у вас есть возможность создать том, но не использовать существующий. Смонтировать папку, через Dockerfile, нельзя.
Создание тома будет иметь ряд ограничений:
- Вы не сможете указать имя тома или выбрать существующий. Имя будет сгенерировано автоматически
- В любом случае том будет создан во время запуска контейнера т.е. так же как и в случае использования
-v - Каждое создание контейнера будет создавать новый том
Для создания тома есть инструкция VOLUME, пример синтаксиса:
FROM nginx
# указываем точку монтирования внутри контейнера
VOLUME /somedata1
Примерный результат где запускаются 2 контейнера с одного образа:
Аналогичный результат можно получить используя одну из следующих команд:
docker run -v /somedata1 nginx
docker run -v $(docker volume create):/somedata1 nginx
Если вы используете инструкцию VOLUME и параметр -v указывающий на одну и ту же директорию, то -v возьмет верх.
Создание Volumes через docker-compose
Docker-compose позволяет запускать несколько контейнеров используя один файл инструкций. Синтаксис монтирования томов может быть ограниченным и расширенным так же как -v и --mount.
Для монтирования тома, кроме инструкции в самом контейнере, нужно указать дополнительную инструкцию volumes в верхнем уровне. Для папки этого делать не нужно:
version: "3.8"
services:
web:
image: nginx:alpine
volumes:
# том
- somevol:/app
# папка
- /home/alex:/app2
# для тома
volumes:
somevol:
Том somevol может использоваться совместно в нескольких контейнерах.
Если нам нужно дать права на том или папку, то мы просто добавляем ro или rw в конец пути:
...
volumes:
# том
- somevol:/app:ro
# папка
- /home/alex:/app2:rw
...
Для монтирования так же есть расширенный синтаксис, похожий на команду mount в docker. Следующий пример аналогичен предыдущем по эффекту:
version: "3.8"
services:
web:
image: nginx:alpine
volumes:
# том
- type: volume
source: somevol
target: /app1
# папка
- type: bind
source: /home/alex
target: /app2
volumes:
somevol:
Есть еще инструкции, которые вы можете использовать. Ниже только их часть, но они используются редко:
volumes:
- type: volume
source: somevol
target: /app1
# папка только для чтения
read_only: true
# не будет копировать файлы в том, которые уже находятся в контейнере
volume:
nocopy: true
# папка
- type: bind
source: /home/alex
target: /app2
# папка только для чтения
read_only: true
# создаст папку на хосте если ее нет
create_host_path: true
Мы можем использовать один и тот же том в нескольких контейнерах (сервисах). Кроме этого есть инструкция volumes_from, которая использует тома с указанного контейнера, ниже оба примера:
version: "3.8"
services:
container1:
image: nginx:alpine
volumes:
- somevol:/app1
- /home/alex:/app2
container2:
image: nginx:alpine
volumes:
# тот же том, но в другом контейнере
- somevol:/app2
container3:
image: nginx:alpine
# берем тома из сервиса container1
# с доступностью только на чтение
volumes_from:
- container1:ro
volumes:
somevol:
Использование внешних томов
Если вам нужно использовать том, который был создан не в текущем файле docker-compose.yml, то вы можете его указать через параметр external, автоматический такой том не создается:
...
volumes:
somevol:
external: true
Создание тома в другой директории
Через docker-compose.yml мы можем указывать драйвера и опции. Например, создадим тома в другой директории по аналогии с тем, что делали выше:
...
volumes:
my_test_volume:
driver: local
driver_opts:
o: bind
type: none
device: /home/alex/compose_vol1
Монтирование через Tmpfs
Еще одним способом монтирования томов является tmpfs. Данные этого тома хранятся в оперативной памяти. При остановке контейнера, в отличие от других томов, данные будут удалены. Эти данные просто не выгружаются из оперативной памяти. Такой тип тома вы можете создать только на одном контейнере и только в Linux.
Такие типы хранилищ редко используются. Их можно использовать для хранения чувствительных данных (для безопасности) или что бы ускорить работу какого-то приложения, но оба варианта, обычно, реализовываются на стороне приложения. Есть два способа создания tmpfs.
Первый способ:
docker run \
--tmpfs /app \
nginx:latest
# или
docker run -d \
--mount type=tmpfs,destination=/app,tmpfs-size=400,tmpfs-mode=1777 \
nginx:latest
При использовании параметра --tmpfs вы можете указать только директорию, которую планируете использовать.
При использовании mount у вас появляются не обязательные параметры:
tmpfs-sizeразмер в байтах, по умолчанию не ограниченtmpfs-modeправа на файлы, по умолчанию 1777. Можно не указывать специальные разрешения (т.е. 700, например).
Второй способ через docker-compose.yml:
volumes:
foo:
driver: local
driver_opts:
type: "tmpfs"
o: "o=size=100m,uid=1000"
device: "tmpfs"
Просмотр привязанных томов
Вы можете объявлять тома внутри смонтированных папок и наоборот. Это может создавать путаницу, но это требуется в определенных ситуациях. Например некоторые фреймворки используют следующую структуру хранение модулей и приложений:
docker inspect название_контейнера --format "'{{json .Mounts}}'"
sudo docker inspect название_контейнера | grep Mountpoint | awk '{ print $2 }'
Привязка томов из другого контейнера
С помощью параметра --volumes-from мы можем скопировать тома у запущенного или остановившегося тома, в значении мы указываем контейнер:
# контейнер 1
docker run -v $(pwd)/app:/usr/src/app \
-v node_modules:/usr/src/app/node_modules \
--name node1 \
node
# контейнер 2
docker run --volumes-from node1 --name node2 node
Создание и удаление Volume
volume это отдельны объект у docker есть команды, с помощью которых можно им управлять:
Выведет список томов:
docker volume ls
Покажет подробную информацию о томе, даже его расположение на хосте:
docker volume inspect
Создание нового тома:
docker volume create
Удалит все тома, которые не используются контейнерами:
docker volume prune
Удалит один том:
docker volume rm

