简而言之:我正在尝试在Docker中安装主机目录,但是我无法从容器内访问它,即使访问权限看起来很好。
细节:
我在做
sudo docker run -i -v /data1/Downloads:/Downloads ubuntu bash
然后
ls -al
它给了我:
total 8892
drwxr-xr-x. 23 root root 4096 Jun 18 14:34 .
drwxr-xr-x. 23 root root 4096 Jun 18 14:34 ..
-rwxr-xr-x. 1 root root 0 Jun 18 14:34 .dockerenv
-rwx------. 1 root root 9014486 Jun 17 22:09 .dockerinit
drwxrwxr-x. 18 1000 1000 12288 Jun 16 11:40 Downloads
drwxr-xr-x. 2 root root 4096 Jan 29 18:10 bin
drwxr-xr-x. 2 root root 4096 Apr 19 2012 boot
drwxr-xr-x. 4 root root 340 Jun 18 14:34 dev
drwxr-xr-x. 56 root root 4096 Jun 18 14:34 etc
drwxr-xr-x. 2 root root 4096 Apr 19 2012 home
以及更多类似的线(我认为这是相关部分)。
如果我这样做
cd /Downloads
ls
结果是
ls: cannot open directory .: Permission denied
主机是Fedora 20,Docker 1.0.0和go1.2.2。
任何想法出了什么问题?
答案 0 :(得分:226)
这是一个selinux问题。
您可以暂时发出
su -c "setenforce 0"
在主机上访问或运行
添加selinux规则chcon -Rt svirt_sandbox_file_t /path/to/volume
答案 1 :(得分:214)
有关完整故事,请参阅this Project Atomic blog post about Volumes and SELinux。
具体做法是:
由于Docker最终合并了一个补丁,最近这变得容易了 将出现在docker-1.7中(我们一直在携带补丁 RHEL,CentOS和Fedora上的docker-1.6。
此补丁增加了对" z"的支持。和" Z"作为卷的选项 坐骑(-v)。
例如:
docker run -v /var/db:/var/db:z rhel7 /bin/sh
会自动执行
chcon -Rt svirt_sandbox_file_t /var/db
在手册页中描述。更好的是,你可以使用Z。
docker run -v /var/db:/var/db:Z rhel7 /bin/sh
这将使用确切的MCS标记容器内的内容 标签表示容器将运行,基本上它运行
chcon -Rt svirt_sandbox_file_t -l s0:c1,c2 /var/db
,其中s0:c1,c2
不同 每个容器。
答案 2 :(得分:66)
警告:此解决方案存在安全风险。
尝试以特权格式运行容器:
sudo docker run --privileged=true -i -v /data1/Downloads:/Downloads ubuntu bash
另一个选项(我还没试过)是创建一个特权容器,然后在其中创建非特权容器。
答案 3 :(得分:29)
来自access.redhat.com:Sharing_Data_Across_Containers:
主机卷设置不可移植,因为它们与主机有关,可能无法在任何其他计算机上运行。 因此,没有Dockerfile等效于将主机目录挂载到容器。另外,请注意主机系统不了解容器SELinux策略。因此,如果强制执行SELinux策略,则无论rw设置如何,装入的主机目录都不可写入容器。目前,您可以通过将适当的SELinux策略类型分配给主机目录来解决此问题":
chcon -Rt svirt_sandbox_file_t host_dir
其中host_dir是主机系统上安装到容器的目录的路径。
它似乎只是一种解决方法,但我尝试了它的工作原理
答案 4 :(得分:12)
我核实了chcon -Rt svirt_sandbox_file_t /path/to/volume
确实有效,您不必作为特权容器运行。
这是:
答案 5 :(得分:9)
通常,主机卷装入的权限问题是因为根据主机上文件的uid / gid权限,容器内的uid / gid无法访问该文件。但是,这种情况不同。
权限字符串drwxr-xr-x.
末尾的点表示已配置SELinux。在SELinux上使用主机安装时,您需要在卷定义的末尾传递一个额外的选项:
z
选项指示绑定安装内容在多个容器之间共享。Z
选项指示绑定装入内容是私有的且未共享。
您的卷安装命令将如下所示:
sudo docker run -i -v /data1/Downloads:/Downloads:z ubuntu bash
在https://docs.docker.com/storage/#configure-the-selinux-label
上了解有关使用SELinux进行主机安装的更多信息。对于其他看到以其他用户身份运行容器的问题,您需要确保容器内用户的uid / gid有权访问主机上的文件。在生产服务器上,这通常是通过控制映像构建过程中的uid / gid来匹配具有访问文件权限的主机上的uid / gid来完成的(甚至更好的是,不要在生产环境中使用主机挂载)。>
命名卷通常比主机挂载更可取,因为命名卷将从映像目录初始化卷目录,包括所有文件所有权和权限。当卷为空并且使用命名卷创建容器时,会发生这种情况。
MacOS用户现在拥有OSXFS,它可以在Mac主机和容器之间自动处理uid / gid。一个没有帮助的地方是嵌入式VM内的文件被挂载到容器中,例如/var/lib/docker.sock。
对于开发环境,每个开发人员可能会更改主机uid / gid,我的首选解决方案是使用以root用户身份运行的入口点启动容器,将用户的uid / gid固定在容器内以匹配主机卷uid /。 gid,然后使用gosu
从root用户放到容器用户,以在容器内运行该应用程序。重要的脚本是我的基本图像脚本中的fix-perms
,可在以下位置找到:https://github.com/sudo-bmitch/docker-base
fix-perms
脚本中的重要内容是:
# update the uid
if [ -n "$opt_u" ]; then
OLD_UID=$(getent passwd "${opt_u}" | cut -f3 -d:)
NEW_UID=$(stat -c "%u" "$1")
if [ "$OLD_UID" != "$NEW_UID" ]; then
echo "Changing UID of $opt_u from $OLD_UID to $NEW_UID"
usermod -u "$NEW_UID" -o "$opt_u"
if [ -n "$opt_r" ]; then
find / -xdev -user "$OLD_UID" -exec chown -h "$opt_u" {} \;
fi
fi
fi
这会将用户的uid和文件的uid获取到容器内,如果它们不匹配,则调用usermod
来调整uid。最后,它会进行递归查找以修复所有未更改uid的文件。与运行带有-u $(id -u):$(id -g)
标志的容器相比,我更喜欢这样做,因为上面的入口点代码不需要每个开发人员都运行脚本来启动容器,并且用户拥有的卷之外的任何文件都将已更正其权限。
您还可以让docker通过使用执行绑定安装的命名卷从映像初始化主机目录。该目录必须预先存在,并且您需要提供主机目录的绝对路径,这与可能是相对路径的撰写文件中的主机卷不同。该目录也必须为空,以便docker对其进行初始化。定义绑定卷的命名卷的三个不同选项如下:
# create the volume in advance
$ docker volume create --driver local \
--opt type=none \
--opt device=/home/user/test \
--opt o=bind \
test_vol
# create on the fly with --mount
$ docker run -it --rm \
--mount type=volume,dst=/container/path,volume-driver=local,volume-opt=type=none,volume-opt=o=bind,volume-opt=device=/home/user/test \
foo
# inside a docker-compose file
...
volumes:
bind-test:
driver: local
driver_opts:
type: none
o: bind
device: /home/user/test
...
最后,如果您尝试使用用户名称空间,则会发现主机卷存在权限问题,因为容器的uid / gid移位了。在这种情况下,避免主机卷而仅使用命名卷可能是最简单的。
答案 6 :(得分:5)
试试docker volume create
。
mkdir -p /data1/Downloads
docker volume create --driver local --name hello --opt type=none --opt device=/data1/Downloads --opt o=uid=root,gid=root --opt o=bind
docker run -i -v hello:/Downloads ubuntu bash
查看文档https://docs.docker.com/engine/reference/commandline/volume_create/
答案 7 :(得分:3)
我遇到了类似的问题,我的问题是主机的UID与容器用户的UID不匹配造成的。解决方法是将用户的UID作为参数传递给docker构建,并使用相同的UID创建容器的用户。
在DockerFile中:
ARG UID=1000
ENV USER="ubuntu"
RUN useradd -u $UID -ms /bin/bash $USER
在构建步骤中:
docker build <path/to/Dockerfile> -t <tag/name> --build-arg UID=$UID
之后,根据OP运行容器和命令给了我预期的结果。
答案 8 :(得分:0)
我通过使用数据容器解决了这个问题,这也具有将数据与应用程序层隔离的优势。你可以像这样运行它:
docker run --volumes-from=<container-data-name> ubuntu
这个tutorial提供了有关数据容器使用的很好的解释。
答案 9 :(得分:0)
在我的情况下,问题有所不同。我不知道为什么,但是即使主机上的目录运行了chmod 777
,在docker内部,它仍然显示为755
。
在容器sudo chmod 777 my_volume_dir
中运行修复了该问题。
答案 10 :(得分:0)
这个问题是因为您的机器上启用了 SELinux。检查下面并禁用它。
[root@nfs-server ~]# getenforce
Enforcing
[root@nfs-server ~]# setenforce 0
[root@nfs-server ~]# getenforce
Permissive
如果您只想禁用 SELinux,您可以使用 --security-opt label:disable 标志来实现。
docker run --security-opt label:disable -v /run/docker.sock:/run/docker.sock POWERFULLCONTAINER
答案 11 :(得分:-1)
sudo -s
在MAC上帮了我大忙