将 NFS 目录挂载到与 Docker 共享的主机卷中

将 NFS 目录挂载到与 Docker 共享的主机卷中

考虑以下 Docker 容器:

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

这会将主机目录 /tmp 挂载到 alpine 容器内的 /mnt/tmp 中。

现在,在主机系统上,我将 NFS 卷安装到 /tmp 目录:

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

安装在主机系统上运行,我看到以下内容:

# ls /tmp/nfs
file1 file2 file3
#

但在 Docker 容器上,我看到一个空白目录:

# ls /mnt/tmp/nfs
#

我知道我可以通过直接在 Docker 容器中进行挂载来解决这个问题。但我真的很想知道为什么挂载在主机容器上有效但在 docker 容器中不起作用?

答案1

发生这种情况是因为卷正在使用private安装传播。这意味着一旦挂载发生,源端(例如 Docker 中的“主机”端)发生的任何更改在挂载下将不可见。

有几种方法可以处理这个问题:

  1. 先挂载 NFS,然后启动容器。挂载将传播到容器,但是与以前一样,容器不会看到对挂载的任何更改(包括卸载)。

  2. 使用“从属”传播。这意味着一旦创建了挂载,源端(docker 主机)上的任何更改都将能够在目标(容器中)中看到。如果您碰巧正在进行嵌套安装,您将需要使用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

答案2

通过在卷参数末尾添加 :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

保存文件,重新加载守护进程参数,然后重新启动 docker:

$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

现在,您应该能够在使用“docker run”时在卷上设置共享安装传播标志。

答案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

相关内容