考虑以下 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 中的“主机”端)发生的任何更改在挂载下将不可见。
有几种方法可以处理这个问题:
先挂载 NFS,然后启动容器。挂载将传播到容器,但是与以前一样,容器不会看到对挂载的任何更改(包括卸载)。
使用“从属”传播。这意味着一旦创建了挂载,源端(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