Docker container 与宿主机用户关系
前言⌗
最近发现 Docker 容器中的 PHP 项目在写日志的时候提示没有权限,看了一下项目目录下的 storage 目录的权限是这样的:
ll
total 8.0K
drwxr-xr-x 4 www-data www-data 4.0K Feb 7 2021 public
drwxr-xr-x 7 www-data www-data 4.0K Feb 7 2021 storage
排查⌗
在 PHP-FPM 容器中使用的用户和组都是 www-data,这也就说明了为什么权限为 755 的时候无法正常写入。
然后我尝试在宿主机中创建 www-data 用户和组,并将项目下的 storage 目录的所有者和所有组都修改为 www-data。
那么问题来了,重启 PHP-FPM 发现依然无权限向 storage 目录中写入。容器内的用户和组和宿主机的都一样,这是为啥呢?
经过一番排查和面向搜索引擎编程,我发现问题主要可能是出在 UID 和 GID 上。
宿主机的 www-data UID 和 GID 分别是 1001 和 1002(因为 1001 的 GID 已经被占用了),而 PHP-FPM 容器内的 www-data UID 和 GID 都是 1000。
印证想法⌗
为了印证上面的想法,我查阅了 Linux 相关资料。Linux 确实是使用 UID 来作为判断依据的,而不是用户名。
既然确定了问题所在,解决问题就非常简单了,在 PHP-FPM 容器内手动将 www-data 的用户和组的 ID 修改为与宿主机一致。
groupmod -o -g 1002 www-data
usermod -o -u 1001 -g www-data www-data
因为我宿主机的用户 www-data 的ID是 1001,www-data 组的 ID 是 1002.
通过上面的命令可以将容器内的用户和组 ID 修改为与系统一致,然后重启 PHP-FPM 容器,再次测试发现可以正常写入 storage 了。
疑问⌗
虽然我们可以在 Dockerfile 和 Docker Compose 中配置使用什么用户和组来启动服务,但是这严重依赖于约定。比如我遇到的问题就是因为我宿主机系统中没有 www-data 这个用户和组。
就导致了前文所说的权限的问题,虽然 Docker Compose 可以指定 user 以及在 docker run 时 也可以指定,但是对于像 PHP-FPM 这类的服务,这个设置像意义不大,因为负责写入的是由 master 进程 fork 出来的 worker 进程。
而这些 worker 进程使用的用户和组是定义在 PHP-FPM 配置文件中的,默认是 www-data,UID 和组 ID 已经确认。
那么问题来了,我再 A Server 上定义的 UID 1000 和 GID 1000 在 B Server 上未必能用。我们只能在不同服务器上重新 build image,然后根据服务器环境 build 时将容器内的 UID 和 GID 修改成于系统对应。
我的方案⌗
# Configure non-root user.
ARG PUID=1000
ENV PUID ${PUID}
ARG PGID=1000
ENV PGID ${PGID}
RUN groupmod -o -g ${PGID} www-data; \
usermod -o -u ${PUID} -g www-data www-data
这样就可以保证宿主机的用户 ID 和容器中的一致,那么在 build image 之前,如果宿主机不存在用和组的话,还需使用如下命令手动创建:
sudo useradd \
--user-group \
--no-create-home \
--shell /sbin/nologin \
--comment "HTTP web service user" \
www-data
sudo id www-data
(x)uid=1001(www-data) gid=1002(www-data) groups=1002(www-data)
将上面 sudo id www-data
这条命了输出的 UID 和 GID 写入 Docker Compose 的环境变量中,再 build 并启动容器。
对于不想重新构建镜像的同学,可以直接在容器中使用 groupmod 和 usermod 这两个命令来修改容器内用户名对应的 UID 和 GID 以保证于宿主机一致。
I hope this is helpful, Happy hacking…