![使主机上的 NFS 挂载在 Docker 容器内可见且可读写](https://linux22.com/image/1592052/%E4%BD%BF%E4%B8%BB%E6%9C%BA%E4%B8%8A%E7%9A%84%20NFS%20%E6%8C%82%E8%BD%BD%E5%9C%A8%20Docker%20%E5%AE%B9%E5%99%A8%E5%86%85%E5%8F%AF%E8%A7%81%E4%B8%94%E5%8F%AF%E8%AF%BB%E5%86%99.png)
我是 Docker 新手,我想使用它来为我的代码获取一个可控的编译环境。
我已经有一个包含所有所需工具的 Docker 映像。我目前的问题如下:
- 我的代码位于主机中,位于从 NFS 服务器挂载的主文件夹中
- 我想让这个文件夹在 Docker 容器内以 r+w 权限可见
这是我第一次尝试(以源文件夹作为卷运行容器)以及收到的错误:
[mbrandalero@machine ~]$ docker run -it --cap-add sys_admin -v "/homes/mbrandalero/src:/usr/local/src/" mbrandalero/my-image bash
/usr/bin/docker-current: Error response from daemon: error while creating mount source path '/homes/mbrandalero/src': mkdir /homes/mbrandalero/src: permission denied.
(显然它正在尝试在主机端创建目录,但该目录已经存在)
奇怪的是,当我尝试以整个主文件夹作为卷运行容器时,它可以工作但会产生不同的错误(文件夹中没有写权限):
[mbrandalero@machine ~]$ docker run -it --cap-add sys_admin -v "/homes/mbrandalero/:/usr/local/src/home" mbrandalero/my_image bash
root@46712ad936f2:/usr/local/src# cd home/
bash: cd: home/: Permission denied
root@46712ad936f2:/usr/local/src# ls -lah | grep "\(\.\|home\)"
total 4.0K
drwxr-xr-x 1 root root 18 May 20 14:50 .
drwxr-xr-x 1 root root 17 May 15 14:06 ..
drwxr-x--- 30 10031 10031 4.0K May 20 15:03 home
我做得对吗?我遗漏了什么?
附加信息:
- 操作系统:CentOS Linux 7.6
- Docker 版本:1.13.1
更新(1):
显然,在运行时设置用户 IDdocker run
可以修复此问题,但这是正确的做法吗?以这种方式运行可以解决问题,但看起来很奇怪(我得到的用户名是“我没有名字!”):
[mbrandalero@machine ~]$ docker run -it --cap-add sys_admin -v "/homes/mbrandalero/:/usr/local/src/home" --user $(id -u) mbrandalero/my_image bash
I have no name!@2efec822e572:/usr/local/src$ cd home/
I have no name!@2efec822e572:/usr/local/src/home$
答案1
正如您在更新中指出的那样,文件上的 UID 未映射到绑定挂载中,这是 Linux 进行绑定挂载的方式。您可以使用不同的 UID 启动容器,但这会导致容器内的 /etc/passwd 映射到不同的用户,甚至没有(在您的情况下)用户。有多种选择,但我的偏好是使用 usermod 命令修改容器的 UID,该命令在图像的入口点内运行,并使用我的修复权限脚本。这需要以 root 身份运行,但随后您可以使用gosu
在运行命令时返回到用户。我在我的dockercon 演示。
请注意,除了绑定挂载到主机 NFS 目录之外,您还可以直接在 NFS 服务器上执行卷挂载。以下是执行此操作的几个示例:
# create a reusable volume
$ docker volume create --driver local \
--opt type=nfs \
--opt o=nfsvers=4,addr=nfs.example.com,rw \
--opt device=:/path/to/dir \
foo
# or from the docker run command
$ docker run -it --rm \
--mount type=volume,dst=/container/path,volume-driver=local,volume-opt=type=nfs,\"volume-opt=o=nfsvers=4,addr=nfs.example.com\",volume-opt=device=:/host/path \
foo
# or to create a service
$ docker service create \
--mount type=volume,dst=/container/path,volume-driver=local,volume-opt=type=nfs,\"volume-opt=o=nfsvers=4,addr=nfs.example.com\",volume-opt=device=:/host/path \
foo
# inside a docker-compose file
...
volumes:
nfs-data:
driver: local
driver_opts:
type: nfs
o: nfsvers=4,addr=nfs.example.com,rw
device: ":/path/to/dir"
...
答案2
如果您以 身份运行容器root
,您可能会看到该error while creating mount source path ... permission denied
消息,因为 NFS 主机正在重新映射所有远程根 UID(又名 root-squash)。
https://en.wikipedia.org/wiki/Unix_security#Root_squash
这是一项安全导向功能,可防止恶意行为者以自己的root
用户身份挂载您的共享,然后对数据进行恶意操作。因此,NFS 挂载通常root_squash
默认设置该选项,以防止此类问题。如果您需要容器以 身份运行root
,您可以使用no_root_squash
NFS 主机/etc/exports
文件中的选项覆盖此选项。
/srv/nfs/shared_folder <hostname>(rw,sync,no_subtree_check,no_root_squash)
http://nfs.sourceforge.net/nfs-howto/ar01s03.html
https://www.thegeekdiary.com/understanding-the-etc-exports-file/
答案3
这对我有用 - 将主机 nfs 路径声明为容器的卷。并且主机 nfs 路径是非主目录路径,它不是主目录。
我的主机 nfs 挂载点:/mnt/abc
我的 docker-compose.yml 文件片段。
...
volumes:
- /mnt/abc/:/abc
...