问题产生原因
最近在使用docker的时候经常遇到权限问题。
通过这个,我明白了问题出现的主要原因:Docker在进行的时候,或者在进行的时候,会保持文件的权限比特位(比如755)、owner ID(比如1000)、group ID(比如1000)与宿主机器上的来源文件相同。而【docker容器主进程的owner ID】(比如www-data(33)
)往往与【宿主机器上的文件的owner ID】(比如xiaoming(1000)
)不一样。如果【docker容器主进程的owner】没有权限读/写这个文件,那么bind-mounts
和COPY
到容器内的文件就不会产生我们预想的效果(比如配置文件无法被容器进程读取、写入)。 如果没有时间查看下面的最佳解决方案,可以试试这个简单粗暴的解决方案:在 宿主机器上,通过 sudo chmod -R 777 /path/to/dir
将 被挂载的目录的权限比特位设为777。这样,这个目录就能被任何用户读写,包括【docker容器主进程的owner】。但是这个方法可能会造成安全隐患,另外,git会将权限改动也视为文件变动(需要 来解决)。
实例:判断问题
查看宿主上的权限信息
使用介绍的查看信息命令,在宿主机器上,检查被bind-mount或者被COPY的文件的权限信息:权限比特位(比如755)、owner ID(比如1000)、group ID(比如1000)。
查看容器内的权限信息
通过以下命令,在指定容器内运行一个bash:
docker exec -it container_name /bin/bash
进入容器的bash以后,你就可以再次使用的查看信息命令,来查看容器中的进程、文件、用户信息了:
- 你可以验证一下被bind-mount、COPY的文件,它们的权限信息是否与宿主上的相同:权限比特位、owner ID、group ID。
-
然后,你应该通过
ps aux
来查看【容器主进程】的owner用户是谁(如果没有ps命令的话按照的指示来安装),这个owner就是需要访问文件的用户,这个用户的权限决定了这个进程是否有权访问【我们bind-mount、COPY到容器中的文件】。如果权限不够,就会出现问题。- 从上图可以看到,主进程的拥有者是
root
,root可以读写任何文件,权限肯定没问题。但是主进程还创建了两个子进程,拥有者是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-data
的ownerId:groupId
是33: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
以及其中的文件。 有两个思路:
- 改变
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了) -
改变容器内
/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
用户的进程可以读写这些文件了!