0

Volumes в Docker

09.08.2023

Проброс папок и томов в Docker решает следующие проблемы, позволяя монтировать директории хоста внутри контейнера либо создавая централизованное хранилище. Таким образом мы получаем следующие преимущества:

  1. Мы не копируем данные, они хранятся в одно месте для всех контейнеров
  2. Раньше копирование отнимало время, а сейчас это делать не нужно, контейнеры запускаются быстрее
  3. У нас появляется больше возможностей для управления данными

Типы томов 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

Можно задать следующие опции:

  1. rprivate
  2. private
  3. rshared
  4. shared
  5. rslave
  6. slave
  7. ro режим только для чтения
  8. z папка на хосте может быть использована несколькими контейнерами
  9. 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, нельзя.

Создание тома будет иметь ряд ограничений:

  1. Вы не сможете указать имя тома или выбрать существующий. Имя будет сгенерировано автоматически
  2. В любом случае том будет создан во время запуска контейнера т.е. так же как и в случае использования -v
  3. Каждое создание контейнера будет создавать новый том

Для создания тома есть инструкция VOLUME, пример синтаксиса:

FROM nginx
# указываем точку монтирования внутри контейнера
VOLUME /somedata1

Примерный результат где запускаются 2 контейнера с одного образа:

Volumes в Docker 1

Аналогичный результат можно получить используя одну из следующих команд:

docker run -v /somedata1 nginx

docker run -v  $(docker volume create):/somedata1 nginx
Volumes в Docker 2

Если вы используете инструкцию 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

Свежие комментарии

Подписка

Лучшие статьи

Рубрики

Популярное

Previous Story

Настройка потоковой репликации PostgreSQL

Next Story

Запускаем PostgreSQL в Docker

Latest from Blog

Настройка простого беспроводного репитера на устройстве MikroTik

При развертывании беспроводных сетей достаточно часто возникают ситуации, когда в некоторых местах квартиры или офиса мощность Wi-Fi сигнала недостаточна для уверенной работы. Конечно, наиболее действенным решением является создание централизованно управляемой сети и

Настройка и использование Redis

В этой статье мы расскажем что такое Redis, его преимущества и для каких целей он используется. Redis (REmote DIctionary Server) — сетевое журналируемое хранилище данных типа “ключ” — “значение” с открытым исходным кодом. По сути Redis представляет собой базу данных

Настройка и использование Memcached

В статье мы расскажем, что такое Memcache, зачем он нужен и как он влияет на работу некоторых популярных CMS. Кеширование данных – немаловажный момент в работе любого веб-проекта. С ростом посещаемости увеличиваются требования к

Как настроить логирование и ротацию логов Nginx

Вовремя настроенное журналирование позволяет в дальнейшем избежать неожиданных проблем с веб-сервером. Информация, хранящаяся в логах (или журналах) сервера, помогает быстро оценить ситуацию и устранить ошибки. В этой статье мы рассмотрим возможности логирования

Распределение выполнения запросов внутри приватной сети VPS через HAProxy

Приватную сеть можно использовать для распределения выполнения загрузки и балансировки запросов между несколькими серверами. Рассмотрим организацию такой балансировки на примере HAProxy – бесплатного ПО, предназначенного для распределения нагрузки и организации проксирования TCP-пакетов между несколькими обслуживающими
Go toTop