考虑以下 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 中的“主机”端)发生的任何更改在挂载下都将不可见。
有几种方法可以处理这个问题:
先挂载NFS,再启动容器。挂载将传播到容器,但是与之前一样,容器不会看到对挂载的任何更改(包括卸载)。
使用“从”传播。这意味着一旦创建了挂载,源端(docker 主机)上的任何更改都将能够在目标(容器中)中看到。如果您碰巧在进行嵌套安装,则需要使用rslave
( r
for 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