Skip to content

Latest commit

 

History

History
309 lines (219 loc) · 22.9 KB

File metadata and controls

309 lines (219 loc) · 22.9 KB

Как смонтировать каталог хоста в Docker-контейнере

Источник: How to mount a host directory in a Docker container

Автор вопроса использовал Docker version 0.9.1, build 867b2a9. Ниже приведён ответ для Docker версии 17.06 и новее.

Чтобы синхронизировать локальный каталог с каталогом внутри контейнера, подключите его как монтирование типа bind. Оно связывает исходный каталог в основной системе с целевым каталогом в Docker-контейнере. Это почти то же самое, что и обычное монтирование каталога в Linux.

Согласно документации Docker, теперь для монтирования рекомендуется использовать параметр --mount вместо -v. Он устроен следующим образом:

  • --mount состоит из нескольких пар «ключ — значение», разделённых запятыми. Каждая пара имеет вид <key>=<value>. Синтаксис --mount подробнее, чем -v или --volume, но порядок ключей не имеет значения, а назначение параметров проще понять.
  • type задаёт тип монтирования: bind, volume или tmpfs. В этой статье используется bind.
  • source задаёт источник монтирования. Для bind-монтирования это путь к файлу или каталогу на хосте, где работает демон Docker. Ключ можно записать как source или src.
  • destination задаёт путь внутри контейнера, куда будет смонтирован файл или каталог. Ключ можно записать как destination, dst или target.

Чтобы смонтировать текущий каталог (источник) в /test_container (цель), выполните:

$ docker run -it --mount src="$(pwd)",target=/test_container,type=bind k3_s3

Если в параметрах монтирования есть пробелы, заключите их в кавычки. Если пробелов точно нет, вместо этого можно использовать `pwd`:

$ docker run -it --mount src=`pwd`,target=/test_container,type=bind k3_s3

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

Выбор параметра -v или --mount

Изначально параметр -v, или --volume, использовался для отдельных контейнеров, а --mount — для сервисов Docker Swarm. Начиная с Docker 17.06 параметр --mount можно использовать и с отдельными контейнерами. Обычно его синтаксис более явный и подробный. Главное различие состоит в том, что синтаксис -v объединяет все параметры в одном поле, а --mount разделяет их. Ниже приведено сравнение синтаксиса этих параметров.

Совет. Новым пользователям рекомендуется синтаксис --mount. Опытные пользователи могут быть лучше знакомы с -v или --volume, однако им также рекомендуется --mount, поскольку исследования показали, что этот синтаксис проще в использовании.

  • -v или --volume состоит из трёх полей, разделённых двоеточиями (:). Поля должны располагаться в правильном порядке, а назначение каждого из них не всегда очевидно.
    • При bind-монтировании первое поле содержит путь к файлу или каталогу на хосте.
    • Второе поле содержит путь, по которому файл или каталог монтируется в контейнере.
    • Третье поле необязательно. Оно содержит список разделённых запятыми параметров, например ro, consistent, delegated, cached, z и Z. Эти параметры рассматриваются ниже.
  • --mount состоит из нескольких разделённых запятыми пар <key>=<value>. Синтаксис --mount подробнее, чем -v или --volume, но порядок ключей не имеет значения, а назначение параметров проще понять.
    • type задаёт тип монтирования: bind, volume или tmpfs. В этой статье рассматривается bind-монтирование, поэтому тип всегда равен bind.
    • source задаёт источник монтирования. Для bind-монтирования это путь к файлу или каталогу на хосте, где работает демон Docker. Ключ можно записать как source или src.
    • destination задаёт путь внутри контейнера, куда монтируется файл или каталог. Ключ можно записать как destination, dst или target.
    • Параметр readonly подключает монтирование к контейнеру в режиме только для чтения.
    • Параметр bind-propagation изменяет распространение bind-монтирования. Допустимые значения: rprivate, private, rshared, shared, rslave и slave.
    • Параметр consistency может принимать значения consistent, delegated и cached. Эта настройка применяется только в Docker Desktop для Mac и игнорируется на остальных платформах.
    • Параметр --mount не поддерживает z и Z для изменения меток SELinux.

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

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

Параметры -v и --volume давно входят в состав Docker, поэтому их поведение нельзя изменить. Из-за этого между -v и --mount есть одно важное различие.

Если с помощью -v или --volume выполнить bind-монтирование файла или каталога, которого ещё нет на хосте Docker, параметр -v создаст конечный объект. Он всегда создаётся как каталог.

Если с помощью --mount попытаться выполнить bind-монтирование файла или каталога, которого ещё нет на хосте Docker, Docker не создаст его автоматически, а вернёт ошибку.

Запуск контейнера с bind-монтированием

Предположим, что у вас есть каталог source, а при сборке исходного кода артефакты сохраняются в другом каталоге — source/target/. Нужно, чтобы артефакты были доступны контейнеру в /app/, а после каждой сборки на хосте разработки контейнер получал доступ к её результатам.

Следующая команда выполняет bind-монтирование каталога target/ в /app/ внутри контейнера. Запустите её из каталога source. В Linux и macOS подкоманда $(pwd) подставляет текущий рабочий каталог.

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

  • --mount
$ docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app \
  nginx:latest
  • -v
$ docker run -d \
  -it \
  --name devtest \
  -v "$(pwd)"/target:/app \
  nginx:latest

Чтобы убедиться, что bind-монтирование создано правильно, выполните docker inspect devtest и найдите раздел Mounts:

"Mounts": [
    {
        "Type": "bind",
        "Source": "/tmp/source/target",
        "Destination": "/app",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    }
],

Вывод показывает, что создано монтирование типа bind с правильными исходным и целевым путями. Оно доступно для чтения и записи, а для распространения задано значение rprivate.

Остановите и удалите контейнер:

$ docker container stop devtest
$ docker container rm devtest

Монтирование в непустой каталог контейнера

Если выполнить bind-монтирование в непустой каталог контейнера, существующее содержимое каталога будет скрыто подключённым монтированием. Это может быть полезно, например для тестирования новой версии приложения без сборки нового образа. Однако такое поведение может оказаться неожиданным и отличается от поведения томов Docker.

Следующий пример намеренно доведён до крайности: содержимое каталога /usr/ внутри контейнера заменяется содержимым каталога /tmp/ на хосте. В большинстве случаев после этого контейнер работать не сможет.

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

  • --mount
$ docker run -d \
  -it \
  --name broken-container \
  --mount type=bind,source=/tmp,target=/usr \
  nginx:latest

docker: Error response from daemon: oci runtime error: container_linux.go:262:
starting container process caused "exec: \"nginx\": executable file not found in $PATH".
  • -v
$ docker run -d \
  -it \
  --name broken-container \
  -v /tmp:/usr \
  nginx:latest

docker: Error response from daemon: oci runtime error: container_linux.go:262:
starting container process caused "exec: \"nginx\": executable file not found in $PATH".

Контейнер будет создан, но не запустится. Удалите его:

$ docker container rm broken-container

Bind-монтирование только для чтения

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

Следующий пример изменяет предыдущий: каталог подключается только для чтения. Для этого после точки монтирования внутри контейнера в список параметров, по умолчанию пустой, добавляется ro. Если параметров несколько, разделяйте их запятыми.

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

  • --mount
$ docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app,readonly \
  nginx:latest
  • -v
$ docker run -d \
  -it \
  --name devtest \
  -v "$(pwd)"/target:/app:ro \
  nginx:latest

Чтобы убедиться, что bind-монтирование создано правильно, выполните docker inspect devtest и найдите раздел Mounts:

"Mounts": [
    {
        "Type": "bind",
        "Source": "/tmp/source/target",
        "Destination": "/app",
        "Mode": "ro",
        "RW": false,
        "Propagation": "rprivate"
    }
],

Остановите и удалите контейнер:

$ docker container stop devtest
$ docker container rm devtest

Настройка распространения bind-монтирования

По умолчанию и для bind-монтирований, и для томов используется режим распространения rprivate. Настроить его можно только для bind-монтирований и только на хостах Linux. Распространение монтирования — сложная тема, и большинству пользователей не приходится его настраивать.

Распространение определяет, передаются ли монтирования, созданные внутри bind-монтирования или именованного тома, его копиям. Предположим, что точка /mnt также смонтирована в /tmp. Настройка распространения определяет, будет ли монтирование /tmp/a доступно также как /mnt/a.

У каждого режима распространения есть рекурсивный вариант. Предположим, что /tmp/a также смонтирован в /foo. В этом случае настройки распространения определяют, будут ли существовать /mnt/a и /tmp/a.

Режим Описание
shared Подмонтирования исходного монтирования доступны его копиям, а подмонтирования копий также распространяются на исходное монтирование.
slave Аналогичен shared, но действует только в одном направлении. Если в исходном монтировании появляется подмонтирование, оно доступно копии. Если подмонтирование появляется в копии, исходное монтирование его не видит.
private Монтирование изолировано. Его подмонтирования не доступны копиям, а подмонтирования копий не доступны исходному монтированию.
rshared Аналогичен shared, но распространение также охватывает точки монтирования, вложенные в любые исходные точки или их копии.
rslave Аналогичен slave, но распространение также охватывает точки монтирования, вложенные в любые исходные точки или их копии.
rprivate Значение по умолчанию. Аналогичен private: точки монтирования внутри исходного монтирования или его копий не распространяются ни в одном направлении.

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

Подробнее см. в документации ядра Linux по общим поддеревьям.

Следующий пример дважды монтирует каталог target/ в контейнер. Для второго монтирования заданы и режим ro, и режим распространения rslave.

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

  • --mount
$ docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app \
  --mount type=bind,source="$(pwd)"/target,target=/app2,readonly,bind-propagation=rslave \
  nginx:latest
  • -v
$ docker run -d \
  -it \
  --name devtest \
  -v "$(pwd)"/target:/app \
  -v "$(pwd)"/target:/app2:ro,rslave \
  nginx:latest

Теперь при создании /app/foo/ также появится /app2/foo/.

Настройка метки SELinux

При использовании SELinux параметры z и Z позволяют изменить метку SELinux файла или каталога на хосте, который монтируется в контейнер. Это влияет непосредственно на объект в основной системе и может иметь последствия за пределами Docker.

  • Параметр z означает, что содержимое bind-монтирования используется совместно несколькими контейнерами.
  • Параметр Z означает, что содержимое bind-монтирования является закрытым и не используется совместно.

Используйте эти параметры с особой осторожностью. Bind-монтирование системного каталога, например /home или /usr, с параметром Z может сделать хост неработоспособным, после чего метки файлов основной системы придётся восстанавливать вручную.

Важно. При использовании bind-монтирований с сервисами метки SELinux (:Z и :z), а также :ro игнорируются. Подробнее см. в moby/moby #32579.

В следующем примере параметр z разрешает нескольким контейнерам совместно использовать содержимое bind-монтирования.

Изменить метку SELinux с помощью параметра --mount невозможно.

$ docker run -d \
  -it \
  --name devtest \
  -v "$(pwd)"/target:/app:z \
  nginx:latest

Настройка согласованности монтирования в macOS

Docker Desktop для Mac использует osxfs, чтобы передавать каталоги и файлы из macOS в виртуальную машину Linux. Благодаря этому общие каталоги и файлы становятся доступны Docker-контейнерам, запущенным в Docker Desktop для Mac.

По умолчанию такие общие ресурсы полностью согласованы: при каждой записи на хосте macOS или через монтирование внутри контейнера изменения сбрасываются на диск, поэтому все участники видят одинаковое состояние данных. В некоторых случаях полная согласованность может серьёзно снижать производительность. Начиная с Docker 17.05 согласованность можно настраивать отдельно для каждого монтирования и контейнера. Доступны следующие варианты:

  • consistent или default — полная согласованность, используемая по умолчанию и описанная выше.
  • delegated — состояние монтирования, которое видит среда выполнения контейнера, считается основным. Изменения из контейнера могут появляться на хосте с задержкой.
  • cached — состояние монтирования, которое видит хост macOS, считается основным. Изменения на хосте могут появляться в контейнере с задержкой.

Во всех операционных системах хоста, кроме macOS, эти параметры полностью игнорируются.

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

  • --mount
$ docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,destination=/app,consistency=cached \
  nginx:latest
  • -v
$ docker run -d \
  -it \
  --name devtest \
  -v "$(pwd)"/target:/app:cached \
  nginx:latest

docker