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

Cal*_*leb 11 nfs docker

考虑以下 Docker 容器:

docker run --rm -it -v /tmp:/mnt/tmp alpine sh
Run Code Online (Sandbox Code Playgroud)

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

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

mkdir /tmp/nfs
mount -t nfs4 192.168.1.100:/data /tmp/nfs
Run Code Online (Sandbox Code Playgroud)

挂载在主机系统上工作,我看到以下内容:

# ls /tmp/nfs
file1 file2 file3
#
Run Code Online (Sandbox Code Playgroud)

但是在 Docker Container 上,我看到一个空白目录:

# ls /mnt/tmp/nfs
#
Run Code Online (Sandbox Code Playgroud)

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

小智 17

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

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

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

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

还有“共享”传播。这种模式将使容器内部对挂载点的更改传播到主机,反之亦然。由于您的用户甚至没有进行此类更改的权限(除非您添加 CAP_SYS_ADMIN),因此这可能不是您想要的。

您可以在创建挂载时设置传播模式,如下所示:

$ docker run -v /foo:/bar:private
Run Code Online (Sandbox Code Playgroud)

另一种选择是使用卷而不是主机安装。你可以这样做:

$ 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
Run Code Online (Sandbox Code Playgroud)

这将确保始终为您挂载在容器中,不依赖于以某种特定方式设置主机或处理挂载传播。
注意:在设备路径的前面是必需的,只是关于 nfs 内核模块的一些奇怪的东西。
注意:Docker 目前无法<nfs host>从 DNS 名称解析(它会在 1.13 中),因此您需要在此处提供 IP 地址。

有关“共享子树”挂载的更多详细信息:https : //www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt