为什么docker容器提示"Permission denied"?

Why does docker container prompt "Permission denied"?

我使用以下命令 运行 一个 docker 容器,并将目录从主机(/root/database)映射到容器(/tmp/install/database):

# docker run -it --name oracle_install -v /root/database:/tmp/install/database bofm/oracle12c:preinstall bash

但是在容器中,我发现我无法使用 ls 来列出 /tmp/install/database/ 中的内容,尽管我是 root 并且拥有所有权限:

[root@77eb235aceac /]# cd /tmp/install/database/
[root@77eb235aceac database]# ls
ls: cannot open directory .: Permission denied
[root@77eb235aceac database]# id
uid=0(root) gid=0(root) groups=0(root)
[root@77eb235aceac database]# cd ..
[root@77eb235aceac install]# ls -alt
......
drwxr-xr-x. 7 root root  4096 Jul  7  2014 database

我在主机中检查 /root/database,一切正常:

[root@localhost ~]# ls -lt
......
drwxr-xr-x.  7 root root       4096 Jul  7  2014 database

为什么docker容器提示"Permission denied"?

更新:
根本原因与SELinux有关。其实我去年就遇到过类似的

共享目录在容器内被拒绝的权限可能是因为此共享目录存储在设备上。默认情况下,容器无法访问任何设备。添加选项 $docker run --privileged 允许容器访问 所有 设备并执行内核调用。 这被认为是不安全的。

更简洁的共享设备方式是使用选项 docker run --device=/dev/sdb(如果 /dev/sdb 是您要共享的设备)。

来自手册页:

  --device=[]
      Add a host device to the container (e.g. --device=/dev/sdc:/dev/xvdc:rwm)

  --privileged=true|false
      Give extended privileges to this container. The default is false.

      By default, Docker containers are “unprivileged” (=false) and cannot, for example, run a Docker daemon inside the Docker container. This is because by default  a  container is not allowed to access any devices. A “privileged” container is given access to all devices.

      When  the  operator  executes  docker run --privileged, Docker will enable access to all devices on the host as well as set some configuration in AppArmor to allow the container nearly all the same access to the host as processes running outside of a container on the host.

我在使用 docker-compose 将 nfs 挂载点共享为卷时遇到了类似的问题。我能够通过以下方式解决问题:

    docker-compose up --force-recreate

即使您发现了问题,这也可能对其他人有所帮助。

另一个原因是与 UID/GID 不匹配。这通常表现为能够以 root 身份修改挂载,但不能以容器用户身份修改挂载

您可以设置 UID,因此对于 ubuntu 容器 运行 作为 ubuntu 您可能需要附加 :uid=1000(检查 id -u)或根据您的用例在本地设置 UID。

uid=value and gid=value

Set the owner and group of the files in the filesystem (default: uid=gid=0)

这里有一个很好的博客,里面有这个 tmpfs 示例

docker run \
       --rm \
       --read-only \
       --tmpfs=/var/run/prosody:uid=100 \
       -it learning/tmpfs

http://www.dendeer.com/post/docker-tmpfs/

我从下面的评论中得到了答案:

man docker-run给出正确答案:

Labeling systems like SELinux require that proper labels are placed on volume content mounted into a container. Without a label, the security system might prevent the processes running inside the container from using the content. By default, Docker does not change the labels set by the OS.

To change a label in the container context, you can add either of two suffixes :z or :Z to the volume mount. These suffixes tell Docker to relabel file objects on the shared volumes. The z option tells Docker that two containers share the volume content. As a result, Docker labels the content with a shared content label. Shared volume labels allow all containers to read/write content. The Z option tells Docker to label the content with a private unshared label. Only the current container can use a private volume.

例如:

  docker run -it --name oracle_install -v /root/database:/tmp/install/database:z ...