Montando um diretório NFS no volume do host compartilhado com o Docker

8

Considere o seguinte contêiner do Docker:

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

Isso monta o diretório do host / tmp em / mnt / tmp dentro do contêiner alpino.

Agora, no sistema host, montei um volume NFS no diretório / tmp:

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

A montagem funciona no sistema host e vejo o seguinte:

# ls /tmp/nfs
file1 file2 file3
#

Mas no Docker Container, vejo um diretório em branco:

# ls /mnt/tmp/nfs
#

Eu sei que posso contornar isso fazendo a montagem diretamente no Docker Container. Mas estou realmente interessado em saber por que a montagem funciona no contêiner do host, mas não no contêiner do docker?

Caleb
fonte
Você pode precisar descrever seu sistema operacional, versão do docker, etc. Eu apenas tentei isso com o Centos 7 e o docker 1.10 de extras e funcionou conforme o esperado; o conteúdo da montagem NFS apareceu dentro de um contêiner debian / jessie. Além disso, se você possui controles de segurança (por exemplo, SELinux) e outros sinalizadores.
Stephen Harris
Estou usando o Ubuntu 16.04 com Docker versão 1.12.0-dev, sem controles de segurança extras. O problema só aparece quando eu faço o NFS Mount depois de criar o contêiner Alpine. Se eu fizer o NFS Mount antes de criar o contêiner Alpine, o vejo como esperado.
Caleb

Respostas:

15

Isso acontece porque o volume está usando a privatepropagação de montagem. Isso significa que quando a montagem acontecer, quaisquer alterações que ocorram no lado de origem (por exemplo, o lado "host" no caso do Docker) não serão visíveis embaixo da montagem.

Existem algumas maneiras de lidar com isso:

  1. Primeiro, monte o NFS e inicie o contêiner. A montagem será propagada para o contêiner, no entanto, como antes, qualquer alteração na montagem não será vista pelo contêiner (incluindo desmontagens).

  2. Use propagação "escrava". Isso significa que, uma vez criada a montagem, quaisquer alterações no lado de origem (host do docker) poderão ser vistas no destino (no contêiner). Se você estiver montando aninhados, convém usar rslave( rpara recursivo).

Também há propagação "compartilhada". Esse modo faria com que as mudanças no ponto de montagem de dentro do contêiner se propagassem para o host, e vice-versa. Como seu usuário nem teria privilégios para fazer essas alterações (a menos que você adicione CAP_SYS_ADMIN), provavelmente não é isso que você deseja.

Você pode definir o modo de propagação ao criar a montagem da seguinte maneira:

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

A outra alternativa seria usar um volume em vez de uma montagem de host. Você pode fazer o seguinte:

$ 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

Isso garantirá sempre a montagem no contêiner para você, não dependerá de ter o host configurado de alguma maneira específica ou de lidar com a propagação da montagem.
nota : :na parte da frente do caminho do dispositivo é necessário, apenas algo estranho no módulo do nfs kernel.
note : No momento, o Docker não resolve a <nfs host>partir de um nome DNS (será em 1.13), portanto, você precisará fornecer o endereço IP aqui.

Mais detalhes sobre montagens de "subárvore compartilhada": https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt

cpuguy83
fonte
Resposta muito boa. Você poderia editá-lo para ser mais uma resposta independente que explica como definir MountFlags = escravo no Docker Daemon e não depender do contexto de outras respostas. Então vou mudar isso para a resposta aceita.
Caleb #
4

Ative a propagação de montagem compartilhada no volume incluindo o sinalizador: shared no final do argumento do volume:

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

Se o Docker foi instalado por meio de um gerenciador de pacotes ou script de instalação para systemd, pode ser necessário ajustar o argumento do daemon MountFlags. Para fazer isso, localize o arquivo docker.service:

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

No meu caso no Ubuntu 16.04, ele estava localizado em /etc/systemd/system/multi-user.target.wants/docker.service. Edite este arquivo com vi ou nano e verifique se a opção MountFlags lê:

MountFlags=shared

Salve o arquivo, recarregue os argumentos do daemon e reinicie a janela de encaixe:

$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

Agora você deve poder definir o sinalizador de propagação de montagem compartilhada em volumes ao usar "docker run".

Caleb
fonte
3

A partir da janela de encaixe 17.06, é possível montar compartilhamentos NFS no contêiner diretamente quando você o executa, sem a necessidade de recursos extras

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

Como alternativa, você pode criar o volume antes do contêiner:

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

Obtenha a dica em https://github.com/moby/moby/issues/28809

ThiagoAlves
fonte