Монтирование каталога NFS в том хоста, который используется совместно с Docker

8

Рассмотрим следующий контейнер Docker:

docker run --rm -it -v /tmp:/mnt/tmp alpine sh

Это монтирует каталог хоста / tmp в / mnt / tmp внутри контейнера alpine.

Теперь в хост-системе я монтирую том NFS в каталог / tmp:

mkdir /tmp/nfs
mount -t nfs4 192.168.1.100:/data /tmp/nfs

Монтирование работает на хост-системе, и я вижу следующее:

# ls /tmp/nfs
file1 file2 file3
#

Но в Docker Container я вижу пустой каталог:

# ls /mnt/tmp/nfs
#

Я знаю, что могу обойти это, выполнив монтирование непосредственно в Docker Container. Но мне действительно интересно знать, почему монтирование работает на контейнере хоста, а не в контейнере докера?

Калеб
источник
Возможно, вам придется описать вашу ОС, версию докера и т. Д. И т. Д. Я только что попробовал это с Centos 7 и докером 1.10 из дополнений, и это сработало, как и ожидалось; содержимое монтирования NFS появилось внутри контейнера debian / jessie. Также есть ли у вас элементы управления безопасностью (например, SELinux) и другие флаги.
Стивен Харрис
Я использую Ubuntu 16.04 с версией Docker 1.12.0-dev, без дополнительных мер безопасности. Проблема проявляется только тогда, когда я выполняю монтирование NFS после создания контейнера Alpine. Если я создаю NFS Mount до создания контейнера Alpine, я вижу его, как и ожидалось.
Калеб

Ответы:

15

Это происходит потому, что том использует privateраспространение монтирования. Это означает, что после монтирования любые изменения, происходящие на стороне источника (например, на стороне хоста в случае Docker), не будут видны под монтированием.

Есть несколько способов справиться с этим:

  1. Сначала смонтируйте NFS, затем запустите контейнер. Монтирование будет распространяться на контейнер, однако, как и прежде, любые изменения в монтировании не будут видны контейнеру (включая размонтирование).

  2. Используйте «рабское» распространение. Это означает, что после создания монтирования любые изменения на стороне источника (хост-докер) будут видны в цели (в контейнере). Если вы делаете вложенные монтирования, вы захотите использовать rslave( rдля рекурсии).

Существует также «совместное» распространение. Этот режим будет вносить изменения в точку монтирования изнутри контейнера, распространяющегося на хост, а также наоборот. Поскольку ваш пользователь даже не будет иметь привилегий для внесения таких изменений (если вы не добавите CAP_SYS_ADMIN), это, вероятно, не то, что вы хотите.

Вы можете установить режим распространения при создании монтирования следующим образом:

$ docker run -v /foo:/bar:private

Другой альтернативой будет использование тома, а не хоста. Вы можете сделать это так:

$ docker volume create \
    --name mynfs \
    --opt type=nfs \
    --opt device=:<nfs export path> \
    --opt o=addr=<nfs host> \
    mynfs
$ docker run -it -v mynfs:/foo alpine sh

Это гарантирует, что вы всегда будете монтировать в контейнере, не полагаясь на настройку хоста каким-либо особым образом или на распространение монтирования.
примечание : в :начале пути к устройству требуется что-то странное в модуле ядра nfs.
примечание : Docker в настоящее время не разрешает <nfs host>DNS-имя (оно будет в 1.13), поэтому вам нужно указать здесь IP-адрес.

Более подробная информация о монтировании «разделяемого поддерева»: https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt

cpuguy83
источник
Очень хороший ответ Не могли бы вы отредактировать его, чтобы он был скорее отдельным ответом, объясняющим, как установить MountFlags = slave в Docker Daemon и не зависеть от контекста других ответов. Тогда я переключу это на принятый ответ.
Калеб
4

Включите распространение общего монтирования на томе, добавив флаг: shared в конце аргумента тома:

docker run --rm -it -v /tmp:/mnt/tmp:shared alpine sh

Если Docker был установлен через менеджер пакетов или сценарий установки для systemd, вам может потребоваться настроить аргумент демона MountFlags. Для этого найдите файл docker.service:

$ sudo find /etc -name "docker.service"

В моем случае в Ubuntu 16.04 он был расположен по адресу /etc/systemd/system/multi-user.target.wants/docker.service. Отредактируйте этот файл с помощью vi или nano и убедитесь, что опция MountFlags гласит:

MountFlags=shared

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

$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

Теперь у вас должна быть возможность установить общий флаг распространения монтирования на томах при использовании «запуска Docker».

Калеб
источник
3

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

export NFS_VOL_NAME=mynfs NFS_LOCAL_MNT=/mnt/mynfs NFS_SERVER=my.nfs.server.com NFS_SHARE=/my/server/path NFS_OPTS=vers=4,soft

docker run --mount \
  "src=$NFS_VOL_NAME,dst=$NFS_LOCAL_MNT,volume-opt=device=:$NFS_SHARE,\"volume-opt=o=addr=$NFS_SERVER,$NFS_OPTS\",type=volume,volume-driver=local,volume-opt=type=nfs" \
  busybox ls $NFS_LOCAL_MNT

Кроме того, вы можете создать том перед контейнером:

docker volume create --driver local \
  --opt type=nfs --opt o=addr=$NFS_SERVER,$NFS_OPTS \
  --opt device=:$NFS_SHARE $NFS_VOL_NAME

docker run --rm -v $NFS_VOL_NAME:$NFS_LOCAL_MNT busybox ls $NFS_LOCAL_MNT

Получил подсказку с https://github.com/moby/moby/issues/28809

ThiagoAlves
источник