Тома

Тома являются предпочтительным механизмом для сохранения данных, создаваемых и используемых контейнерами Docker. В то время как связующие монтирования зависит от структуры каталогов и ОС хост-машины, тома полностью управляются Docker. Тома имеют ряд преимуществ перед связующими монтированиями:

  • Тома легче резервировать или переносить, чем связывающие монтирования.

  • Вы можете управлять томами с помощью команд Docker CLI или Docker API.

  • Тома работают как в контейнерах Linux, так и в контейнерах Windows.

  • Тома можно более безопасно распределять между несколькими контейнерами.

  • Драйверы томов позволяют хранить тома на удалённых хостах или у облачных провайдеров, шифровать содержимое томов или добавлять другие функциональные возможности.

  • Содержимое новых томов может быть предварительно заполнено контейнером.

  • Тома на Docker Desktop имеют гораздо более высокую производительность, чем связывающее монтирование с хостов Mac и Windows.

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

volumes on the Docker host

Если ваш контейнер генерирует неперсистентные данные состояния, рассмотрите возможность использования монтирование tmpfs, чтобы не хранить данные где-либо постоянно и увеличить производительность контейнера, избегая записи в записываемый слой контейнера.

Тома используют распространение привязки rprivate, и распространение привязки не настраивается для томов.

Выберите флаг -v или –mount

В целом, --mount является более явным и многословным. Самое большое различие заключается в том, что синтаксис -v объединяет все опции в одном поле, а синтаксис --mount разделяет их. Вот сравнение синтаксиса для каждого флага.

Если вам нужно указать параметры драйвера тома, вы должны использовать --mount.

  • -v или --volume: Состоит из трёх полей, разделенных символами двоеточия (:). Поля должны быть расположены в правильном порядке, а значение каждого поля не сразу очевидно.

  • В случае именованных томов первое поле - это имя тома, которое является уникальным для данной хост-машины. Для анонимных томов первое поле пропускается.

  • Второе поле - это путь, по которому файл или каталог монтируются в контейнере.

  • Третье поле является необязательным и представляет собой список опций, разделенных запятыми, например, ro. Эти параметры рассматриваются ниже.

  • --mount: Состоит из нескольких пар ключ-значение, разделённых запятыми, каждая из которых состоит из кортежа <key>=<value>. Синтаксис --mount более многословен, чем -v или --volume, но порядок ключей не имеет значения, а значение флага легче понять.

  • type монтирования, который может связать, volume или tmpfs. В данной теме рассматриваются тома, поэтому тип всегда volume.

  • source монтирования. Для именованных томов это имя тома. Для анонимных томов это поле пропускается. Может быть указано как source или src.

  • destination принимает в качестве значения путь, по которому файл или каталог смонтирован в контейнере. Может быть указан как destination, dst или target.

  • Если присутствует опция readonly, заставляет связывающее монтирование (bind mount) быть монтированным в контейнер только для чтения. Может быть указан как readonly или ro.

  • Опция volume-opt, которая может быть указана более одного раза, принимает пару ключ-значение, состоящую из имени опции и ее значения.

    Эскейп-значения из внешнего CSV-парсера

    Если драйвер тома принимает в качестве параметра список, разделённый запятыми, необходимо экранировать значение из внешнего CSV-парсера. Чтобы исключить volume-opt, окружите его двойными кавычками (") и окружите весь параметр монтирования одинарными кавычками (').

    Например, драйвер local принимает параметры монтирования в виде списка, разделенного запятыми, в параметре o. В этом примере показан правильный способ вывода списка.

    $ docker service create \
        --mount 'type=volume,src=<VOLUME-NAME>,dst=<CONTAINER-PATH>,volume-driver=local,volume-opt=type=nfs,volume-opt=device=<nfs-server>:<nfs-path>,"volume-opt=o=addr=<nfs-address>,vers=4,soft,timeo=180,bg,tcp,rw"'
        --name myservice \
        <IMAGE>
    

В примерах ниже, где это возможно, показан синтаксис --mount и -v, причем --mount представлен первым.

Различия между поведением -v и --mount

В отличие от bind mounts, все опции для томов доступны для флагов --mount и -v.

При использовании томов с сервисами поддерживается только --mount.

Создание и управление томами

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

Создание тома:

$ docker volume create my-vol

Список томов:

$ docker volume ls

local               my-vol

Осмотр тома:

$ docker volume inspect my-vol
[
    {
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
        "Name": "my-vol",
        "Options": {},
        "Scope": "local"
    }
]

Удаление тома:

$ docker volume rm my-vol

Запуск контейнера с томом

Если вы запускаете контейнер с томом, который еще не существует, Docker создаёт его за вас. Следующий пример монтирует том myvol2 в /app/ в контейнере.

Примеры -v и --mount, приведенные ниже, дают одинаковый результат. Вы не сможете запустить их оба, если не удалите контейнер devtest и том myvol2 после запуска первого.

Для --mount

$ docker run -d \
  --name devtest \
  --mount source=myvol2,target=/app \
  nginx:latest

Для -v

$ docker run -d \
  --name devtest \
  -v myvol2:/app \
  nginx:latest

Используйте docker inspect devtest, чтобы проверить, что том был создан и смонтирован правильно. Поищите раздел Mounts:

"Mounts": [
    {
        "Type": "volume",
        "Name": "myvol2",
        "Source": "/var/lib/docker/volumes/myvol2/_data",
        "Destination": "/app",
        "Driver": "local",
        "Mode": "",
        "RW": true,
        "Propagation": ""
    }
],

Это показывает, что монтирование является томом, показывает правильный источник и назначение, а также то, что монтирование выполняется на чтение и запись.

Остановите контейнер и удалите том. Обратите внимание, что удаление тома - это отдельный шаг.

$ docker container stop devtest

$ docker container rm devtest

$ docker volume rm myvol2

Использование тома в Docker Compose

Вот пример одного сервиса Docker Compose с томом:

services:
  frontend:
    image: node:lts
    volumes:
      - myapp:/home/node/app
volumes:
  myapp:

При первом запуске команды docker compose up создаётся том. Этот же том используется повторно при последующем выполнении команды.

Вы можете создать том непосредственно вне Compose, используя docker volume create, а затем сослаться на него внутри docker-compose.yml следующим образом:

services:
  frontend:
    image: node:lts
    volumes:
      - myapp:/home/node/app
volumes:
  myapp:
    external: true

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

Запуск службы с томами

Когда вы запускаете службу и определяете том, каждый контейнер службы использует свой локальный том. Ни один из контейнеров не может совместно использовать эти данные, если вы используете драйвер тома local. Однако некоторые драйверы томов поддерживают совместное хранение данных.

В следующем примере запускается служба nginx с четырьмя репликами, каждая из которых использует локальный том под названием myvol2.

$ docker service create -d \
  --replicas=4 \
  --name devtest-service \
  --mount source=myvol2,target=/app \
  nginx:latest

Используйте docker service ps devtest-service, чтобы убедиться, что служба запущена:

$ docker service ps devtest-service

ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
4d7oz1j85wwn        devtest-service.1   nginx:latest        moby                Running             Running 14 seconds ago

Вы можете удалить службу, чтобы остановить выполнение задач:

$ docker service rm devtest-service

Удаление службы не приводит к удалению томов, созданных службой. Удаление томов - это отдельный шаг.

Различия в синтаксисе для сервисов

Команда docker service create не поддерживает флаг -v или --volume. При монтировании тома в контейнеры службы необходимо использовать флаг --mount.

Заполнение тома с помощью контейнера

Если вы запускаете контейнер, который создаёт новый том, и контейнер имеет файлы или каталоги в монтируемом каталоге, например /app/, содержимое каталога копируется в том. Затем контейнер монтирует и использует том, а другие контейнеры, использующие этот том, также получают доступ к предварительно заполненному содержимому.

Чтобы проиллюстрировать это, в следующем примере запускается контейнер nginx и заполняется новый том nginx-vol содержимым каталога /usr/share/nginx/html контейнера. Здесь Nginx хранит HTML-содержимое по умолчанию.

Примеры --mount и -v имеют одинаковый конечный результат.

Для --mount

$ docker run -d \
  --name=nginxtest \
  --mount source=nginx-vol,destination=/usr/share/nginx/html \
  nginx:latest

Для -v

$ docker run -d \
  --name=nginxtest \
  -v nginx-vol:/usr/share/nginx/html \
  nginx:latest

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

$ docker container stop nginxtest

$ docker container rm nginxtest

$ docker volume rm nginx-vol

Используйте том, доступный только для чтения

Для некоторых приложений разработки контейнеру необходимо записывать данные в связывающее монтирование, чтобы изменения распространялись обратно на хост Docker. В других случаях контейнеру требуется доступ к данным только для чтения. Несколько контейнеров могут монтировать один и тот же том. Вы можете одновременно монтировать один том как read-write для одних контейнеров и как read-only для других.

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

Примеры --mount и -v дают тот же результат.

Для --mount

$ docker run -d \
  --name=nginxtest \
  --mount source=nginx-vol,destination=/usr/share/nginx/html,readonly \
  nginx:latest

Для -v

$ docker run -d \
  --name=nginxtest \
  -v nginx-vol:/usr/share/nginx/html:ro \
  nginx:latest

Используйте docker inspect nginxtest, чтобы проверить, что монтирование только для чтения было создано правильно. Поищите раздел Mounts:

"Mounts": [
    {
        "Type": "volume",
        "Name": "nginx-vol",
        "Source": "/var/lib/docker/volumes/nginx-vol/_data",
        "Destination": "/usr/share/nginx/html",
        "Driver": "local",
        "Mode": "",
        "RW": false,
        "Propagation": ""
    }
],

Остановите и извлеките контейнер, а также удалите том. Удаление тома - это отдельный шаг.

$ docker container stop nginxtest

$ docker container rm nginxtest

$ docker volume rm nginx-vol

Обмен данными между машинами

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

общее хранилище

Существует несколько способов добиться этого при разработке приложений. Один из них - добавить в приложение логику для хранения файлов в облачной системе хранения объектов, например Amazon S3. Другой способ - создать тома с помощью драйвера, поддерживающего запись файлов во внешнюю систему хранения, например NFS или Amazon S3.

Драйверы томов позволяют абстрагировать базовую систему хранения от логики приложения. Например, если ваши службы используют том с драйвером NFS, вы можете обновить службы для использования другого драйвера, например, для хранения данных в облаке, без изменения логики приложения.

Использование драйвера тома

При создании тома с помощью docker volume create или при запуске контейнера, который использует ещё не созданный том, можно указать драйвер тома. В следующих примерах используется драйвер тома vieux/sshfs, сначала при создании отдельного тома, а затем при запуске контейнера, который создает новый том.

Первоначальная настройка

В следующем примере предполагается, что у вас есть два узла, первый из которых является узлом Docker и может подключаться ко второму узлу с помощью SSH.

На хосте Docker установите плагин vieux/sshfs:

$ docker plugin install --grant-all-permissions vieux/sshfs

Создание тома с помощью драйвера тома

В этом примере указан пароль SSH, но если на двух хостах настроены общие ключи, пароль можно не указывать. Каждый драйвер тома может иметь ноль или более настраиваемых параметров, каждый из которых задается с помощью флага -o.

$ docker volume create --driver vieux/sshfs \
  -o sshcmd=test@node2:/home/test \
  -o password=testpassword \
  sshvolume

Запуск контейнера, который создаёт том с помощью драйвера тома

В следующем примере указан пароль SSH. Однако если на двух хостах настроены общие ключи, пароль можно не указывать. Каждый драйвер тома может иметь ноль или более настраиваемых параметров.

Примечание

Если драйвер тома требует передачи каких-либо опций, для монтирования тома необходимо использовать флаг --mount, а не -v.

$ docker run -d \
  --name sshfs-container \
  --volume-driver vieux/sshfs \
  --mount src=sshvolume,target=/app,volume-opt=sshcmd=test@node2:/home/test,volume-opt=password=testpassword \
  nginx:latest

Создание службы, которая создаёт том NFS

В следующем примере показано, как можно создать том NFS при создании службы. Он использует 10.0.0.10 в качестве сервера NFS и /var/docker-nfs в качестве экспортируемого каталога на сервере NFS. Обратите внимание, что в качестве драйвера тома указан local.

NFSv3

$ docker service create -d \
  --name nfs-service \
  --mount 'type=volume,source=nfsvolume,target=/app,volume-driver=local,volume-opt=type=nfs,volume-opt=device=:/var/docker-nfs,volume-opt=o=addr=10.0.0.10' \
  nginx:latest

NFSv4

$ docker service create -d \
    --name nfs-service \
    --mount 'type=volume,source=nfsvolume,target=/app,volume-driver=local,volume-opt=type=nfs,volume-opt=device=:/var/docker-nfs,"volume-opt=o=addr=10.0.0.10,rw,nfsvers=4,async"' \
    nginx:latest

Создание томов CIFS/Samba

Вы можете монтировать ресурс Samba непосредственно в docker без настройки точки монтирования на хосте.

$ docker volume create \
    --driver local \
    --opt type=cifs \
    --opt device=//uxxxxx.your-server.de/backup \
    --opt o=addr=uxxxxx.your-server.de,username=uxxxxxxx,password=*****,file_mode=0777,dir_mode=0777 \
    --name cif-volume

Обратите внимание, что опция addr требуется, если используется имя хоста вместо IP, чтобы docker мог выполнить поиск по имени хоста.

Резервное копирование, восстановление или миграция томов данных

Тома полезны при резервном копировании, восстановлении и миграции. Используйте флаг --volumes-from для создания нового контейнера, который монтирует этот том.

Резервное копирование тома

Например, создайте новый контейнер с именем dbstore:

$ docker run -v /dbdata --name dbstore ubuntu /bin/bash

В следующей команде:

  • Запустите новый контейнер и смонтируйте том из контейнера dbstore

  • Смонтируйте каталог локального хоста как /backup

  • Передаем команду, которая разрывает содержимое тома dbdata в файл backup.tar внутри нашего каталога /backup.

$ docker run --rm --volumes-from dbstore -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata

Когда команда завершится и контейнер остановится, он создаст резервную копию тома dbdata.

Восстановление тома из резервной копии

Только что созданную резервную копию можно восстановить в тот же контейнер или в другой контейнер, созданный в другом месте.

Например, создайте новый контейнер с именем dbstore2:

$ docker run -v /dbdata --name dbstore2 ubuntu /bin/bash

Затем распакуйте файл резервной копии в томе данных нового контейнера:

$ docker run --rm --volumes-from dbstore2 -v $(pwd):/backup ubuntu bash -c "cd /dbdata && tar xvf /backup/backup.tar --strip 1"

Вы можете использовать описанные выше методы для автоматизации тестирования резервного копирования, миграции и восстановления с помощью предпочитаемых инструментов.

Удаление томов

Том данных Docker сохраняется после удаления контейнера. Существует два типа томов:

  • Именованные тома имеют конкретный источник извне контейнера, например, awesome:/bar.

  • Анонимные тома не имеют конкретного источника, поэтому при удалении контейнера можно дать команду демону Docker Engine удалить их.

Удаление анонимных томов

Чтобы автоматически удалить анонимные тома, используйте параметр --rm. Например, эта команда создаёт анонимный том /foo. При удалении контейнера Docker Engine удаляет том /foo, но не удаляет том awesome.

$ docker run --rm -v /foo -v awesome:/bar busybox top

Примечание

Если другой контейнер связывает тома с --volumes-from, определения томов копируются и анонимный том остается и после удаления первого контейнера.

Удаление всех томов

Чтобы удалить все неиспользуемые тома и освободить место:

$ docker volume prune

Следующие шаги