阅读 132

【docker】 bind-mount或者COPY时需要注意 用户、文件权限 的问题

【docker】 bind-mount或者COPY时需要注意 用户、文件权限 的问题

问题产生原因

最近在使用docker的时候经常遇到权限问题。
通过这个stackoverflow回答,我明白了问题出现的主要原因:Docker在进行bind mount的时候,或者在进行COPY的时候,会保持文件的权限比特位(比如755)、owner ID(比如1000)、group ID(比如1000)与宿主机器上的来源文件相同。而【docker容器主进程的owner ID】(比如www-data(33))往往与【宿主机器上的文件的owner ID】(比如xiaoming(1000))不一样。如果【docker容器主进程的owner】没有权限读/写这个文件,那么bind-mountsCOPY到容器内的文件就不会产生我们预想的效果(比如配置文件无法被容器进程读取、写入)。

如果没有时间查看下面的最佳解决方案,可以试试这个简单粗暴的解决方案:在 宿主机器上,通过 sudo chmod -R 777 /path/to/dir将 被挂载的目录的权限比特位设为777。这样,这个目录就能被任何用户读写,包括【docker容器主进程的owner】。但是这个方法可能会造成安全隐患,另外,git会将权限改动也视为文件变动(需要 设置一下git来解决)。

实例:判断问题

查看宿主上的权限信息

使用上一篇博客介绍的查看信息命令,在宿主机器上,检查被bind-mount或者被COPY的文件的权限信息:权限比特位(比如755)、owner ID(比如1000)、group ID(比如1000)。

查看容器内的权限信息

通过以下命令,在指定容器内运行一个bash:

docker exec -it container_name /bin/bash

进入容器的bash以后,你就可以再次使用上一篇博客的查看信息命令,来查看容器中的进程、文件、用户信息了:

  1. 你可以验证一下被bind-mount、COPY的文件,它们的权限信息是否与宿主上的相同:权限比特位、owner ID、group ID。

  2. 然后,你应该通过ps aux来查看【容器主进程】的owner用户是谁(如果没有ps命令的话按照上一篇博客的指示来安装),这个owner就是需要访问文件的用户,这个用户的权限决定了这个进程是否有权访问【我们bind-mount、COPY到容器中的文件】。如果权限不够,就会出现问题。

    • 从上图可以看到,主进程的拥有者是rootroot可以读写任何文件,权限肯定没问题。但是主进程还创建了两个子进程,拥有者是www-data,这个用户有可能无法读写【被bind-mount或者COPY到容器内的文件】!

    • 因此,现在我们需要检查www-data是否存在权限不足的问题,首先通过id username来查看这个用户的 user id、primary group id、加入的所有群组 的信息:

    • 然后,使用stat或者ls -na命令,查看【被bind-mount或者COPY到容器内的文件】的权限信息:

    • 上图的/codeigniter文件夹是我通过bind-mount挂载到容器内的。可以看到这个文件夹以及其中的文件的ownerId:groupId都是1000:1000,而www-dataownerId:groupId33:33。再结合这些文件的权限信息(比如-rw-rw-r--drwx------),www-data只能读取其中一些文件,无法写入任何文件,/codeigniter/application/这个文件夹内的内容连访问都不行!

    • 如上图,用stat也能看到同样的文件信息,只不过一次只能看到一个文件或文件夹的信息。

【容器主进程】有可能会创建其他的进程一起工作(比如上面的例子),如果是这样的话,你需要检查所有子进程都有权访问自己所需的文件。

上面的例子中,/codeigniter以及其中内容的权限比特位、owner ID、group ID都与我的宿主机器上的codeigniter文件夹相同(我登陆ubuntu的账户的userid就是1000)!保持文件的元数据(filesystem metadata)是docker的一个特点,也是一个坑点。

实例:解决问题

既然知道了问题的成因,那么我们就可以想办法解决它了。
要解决这个问题,其实就是www-data用户有权读写/codeigniter以及其中的文件

有两个思路:

  1. 改变www-data的uid和gid,从33:33变成1000:1000。这样www-data就是这些文件的owner了,自然就有需要的权限了。改变uid和gid可以通过我上篇文章介绍的usermod命令。具体来说,就是在Dockerfile中加入这样一句:RUN usermod -u 1000 www-data && usermod -G 1000 www-data。(其实不需要改gid的,改uid就可以成为owner了)

  2. 改变容器内/codeigniter的权限信息,使得www-data(33:33)有权读写它:

    • 通过chmod命令,在宿主机器上改变codeigniter文件夹的权限比特为777。这样,在bind-mount到容器里面以后它的权限也是777,因此任何用户都可以读写它的内容。这个方法要求修改开发环境,不太优雅。

    • 在构建image时,将所有需要的文件COPY到镜像内,然后通过Run chmod 777 -R /codeigniter使得www-data获得读写权限,或者Run chown www-data:www-data -R /codeigniter使得www-data成为owner。

      实际上 COPY --chown=www-data:www-data ./codeigniter /codeigniter/就能一步将【被COPY的文件】的owner设置为容器内的某个用户。

我认为Run chmod 777 -R /codeigniter这个解决方案是最方便省事的。

但是我不太想chmod 777,我采用的是COPY --chown=www-data:www-data ./codeigniter /codeigniter/的方案,我喜欢这种方案,因为它只做恰到好处的修改,不多不少。要用好这个方案,我们需要知道被COPY的文件会被哪些进程访问、这些进程的onwer分别是谁

使用这个方案以后的结果:


www-data用户的进程可以读写这些文件了!


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐