Docker中php-fpm以www-data身份运行

16

最近我一直在学习使用Docker构建镜像和容器。在Mac上使用时,我对此已经有了相当的信心,但是最近我换成了Ubuntu,我在这方面的开发经验比较新。

我正在使用一个标准的新Laravel项目作为我的“代码”,目前只是使用了一个php容器和一个nginx容器。

我使用一个docker-compose.yml文件来创建我的容器:

version: "3.1"

services:
    nginx:
        image: nginx:latest
        volumes:
            - ./code:/var/www
            - ./nginx_conf.conf:/etc/nginx/conf.d/default.conf
        ports:
            - "80:80"
    php:
        image: php:7.3-fpm
        ports:
            - 9000
        volumes:
            - ./code:/var/www

以上代码中可能存在错误,因为我只是打字而不是复制粘贴,但它在我的机器上可以工作。

问题是:

  • php-fpm 使用 --with-fpm-user=www-data--with-fpm-group=www-data 进行配置,并将其设置在 php:7.3-fpm Dockerfile 中 (在此处查看)

  • 我的主机上的文件以我的用户名和组作为所有者/组保存。

  • 当我进入容器时,文件的所有者和组为 1000 和 1000(我假设是映射到我的用户帐户和主机机器上的组?)

然而,当我通过浏览器访问应用程序时,在启动时会出现权限被拒绝的错误(当 Laravel 尝试在存储中创建错误日志文件时)。我认为这是因为 php-fpm 作为 www-data 运行,但存储目录对于所有者/组 phil:phil 具有权限 drwxr-xr-x - 即我的主机所有者和组。

经过数小时的谷歌和尝试后,我尝试了以下方法:

  • 递归更改主机上的代码目录的所有者和组为 www-data:www-data。这允许 Laravel 应用程序工作,但我现在无法使用 PHPStorm 创建或编辑等文件,因为该目录是只读的(我猜测因为 phpstorm 以我的用户身份运行,并且目录由不同的用户/组拥有)。

  • 我将我的主机用户帐户添加到 www-data 组中,并使用 sudo chmod -R g+w ./code 授予组写权限,现在可以运行应用程序,并让 phpstorm 编写、执行等文件,但当我创建或编辑文件时,文件的所有权和组会再次变为我的主机 phil:phil,我想这可能会再次破坏应用程序。

  • 我尝试创建一个 PHP 映像,并设置 env(如上面的链接所述)以配置为 --with-fpm-user=phil --with-fpm-group=phil,但构建后并没有改变任何东西 - 它仍然在使用 www-data 运行(从 GitHub 问题中读取,我认为这是因为 envs 无法更改,直到稍后,在此时点 PHP 已经配置好?)(在此处查看 GitHub 问题)

我正在尽力想要尝试的唯一其他事情是将我的主机代码目录的所有者和组递归设置为 www-data,并尝试以 www-data 身份运行 phpstorm,但这感觉很奇怪(更新:我尝试以 www-data 用户身份打开 phpstorm,使用 sudo -u www-data phpstorm.sh,但我收到一个与图形有关的 Java 异常 - 因此,此方法也不可行)

现在我能想到的唯一的事情就是从 alpine 基础镜像创建一个新的 PHP 映像,并完全绕过 PHP 的映像 - 这似乎非常麻烦,仅因为维护者想使用 ENV


请注意,在 Docker 中,用户名几乎没有意义,它使用用户 ID 来识别用户,因此如果 www-data 的 UID/GID 为 1000:1000,则用于访问文件的用户名无关紧要,只要它具有相同的 UID 即可。 - Coded Monkey
5个回答

11

几周前我也遇到了同样的问题。

实际上发生的情况是,您的主机和容器通过卷共享相同的文件,因此它们也共享权限。

在生产中,一切都很好 - 您的服务器(www-data用户)应该是文件的所有者,因此这里没有问题。在开发过程中,当您尝试从主机访问这些文件时,情况变得更加复杂。

我知道几种解决方法,其中最hacky的一种似乎是将容器中的www-data uid设置为1000,这样它将与主机中的uid匹配。

另一个简单的方法是在共享目录上打开777完全权限,因为它仅在开发构建中需要 - (但绝不能在生产中执行,但如我之前所述,在生产中您没有任何问题,因此必须将这两个进程分开,只能在开发模式下执行)

对我来说,最优雅的解决方案似乎是允许所有组成员访问文件(设置770权限),并将www-data添加到您的组中:

usermod www-data -a -G phill #// add it to your group

chown -r phill ./code #// make yourself the owner. might need sudo.

chmod 770 ./code #//grunt permissions to all group members

谢谢您的回复!很高兴看到有人遇到类似的问题!我会尝试您的建议并与您联系。谢谢! - Phil Cross
我首先尝试了第一种解决方法——将www-data的UID更改为我的宿主机UID,并且我相信这已经奏效了!这只是用于本地开发——还在努力学习Docker,但是这篇帖子非常有价值,节省了我数不清的时间!非常感谢 :) - Phil Cross
很高兴能够帮助,谢谢。 - Efrat Levitan
@EfratLevitan @PhilCross:我已经为这个问题苦思冥想了好几天,刚看到你们的帖子,突然想知道:是否有一种方法可以在docker-composer.yml中操作www-data UID ...或者通过设置entrypoint:并通过脚本更改它? - bnoeafk
嘿@bnoeafk,你可以在Dockerfile或入口脚本中更改www-data uid,命令大致如下:usermod -u UID www-data,如果有效,请告诉我,我会编辑我的答案。 - Efrat Levitan

9

@bnoeafk,虽然已经有人提到了这个方法,但我会将其作为一个新的答案发表。我认为这并不是一种取巧的方法,它的工作原理基本上类似于ntfsusermap,肯定比更改所有文件权限更优雅。

对于Dockerfile:

FROM php:7.4-apache

# do stuff...

ARG UNAME=www-data
ARG UGROUP=www-data
ARG UID=1000
ARG GID=1001
RUN usermod  --uid $UID $UNAME
RUN groupmod --gid $GID $UGROUP

每个使用这个镜像的用户在构建时都可以将自己传递进去:docker-compose build --build-arg UID=$(id -u) --build-arg GID=$(id -g)


如果我们需要在从镜像启动容器时传递用户/组ID,该怎么办? - Anton Duzenko
@AntonDuzenko 请查看docker文档中的--build-argARG指令,以及关于ARG和ENV指令的这个很棒的答案 - Rodrigo
@Rodrigo 我们在生产环境中没有 root 权限,只能使用 Plesk,并且仅支持环境变量。 - Anton Duzenko

2
您的系统有很多选项来完成这个任务,但请注意您可能需要重新启动正在运行的进程(例如php-fpm)。
以下是一些实现此目标的示例:(您可以在容器外部运行命令:docker container exec ...)
示例1: usermod -g 1007 www-data 它会将用户www-data的uid更新为1007。
示例2: deluser www-data adduser -u 1007 -D -S -G www-data www-data 它将删除用户www-data并使用uid 1007重新创建。
获取pid并重启进程
要重新启动正在运行的进程(例如php-fpm),可以这样做:
首先,使用以下任意一种命令获取pid: pidof php-fpm ps -ef | grep -v grep | grep php-fpm | awk '{print $2}' find /proc -mindepth 2 -maxdepth 2 -name exe -lname '*/php-fpm' -printf %h\\n 2>/dev/null | sed s+^/proc/++ 然后,使用刚才获取的pid重新启动进程(如果您的进程支持USR2信号): kill -USR2 pid <--用刚才获得的数字替换pid
我发现最简单的方法是更新主机或构建您的容器以知道正确的pid(如果您使用不同的环境,则不总是可行)。

0
假设您想将PHP容器的用户和项目文件的所有者设置为www-data。这可以在Dockerfile中完成:
FROM php
.
.
.
RUN chown -R www-data:www-data /var/www
USER www-data # next instruction might face permission error if this line is not at the end of the dockerfile

重要的事实在于,Docker 主机中的原始权限与容器内的权限相对应。因此,如果您现在将当前用户添加到 www-data 组(可能需要注销/重新启动才能生效),则将具有足够的权限来编辑容器外的文件(例如在 IDE 中)。
sudo usermod -aG www-data your_user

通过这种方式,PHP 代码被允许运行可执行文件或写入新文件,而您可以在主机环境中编辑文件。


0
[免责声明:虽然在开发中这个方法有效,但我不确定在生产环境中是否是良好的实践。我在生产环境中手动更改了一些设置,因为我还没有时间自动化它。]
[另一个解决方案是:]
  1. /etc/passwd/etc/group添加为只读卷(它们存储系统的用户和组信息)
services: 
    ...
    php:
        ...
        volumes:
            - ./code:/var/www/html
            - /etc/passwd:/etc/passwd:ro
            - /etc/group:/etc/group:ro
        ...

  • usermod -a -Gchmod可以在主机或容器中使用。
  • usermod -a -G www-data <your_user>

    chmod -R 775 ./code

    我们将用户添加到www-data组,并在chmod中更改文件权限,以允许www-data组的成员对文件具有读取、写入和执行权限。


    这应该足够在UNIX上进行开发了。在生产环境中,我通常会从compose.yml文件中删除卷:/etc/passwd/etc/group,然后恢复到chmod -R 755 /var/www/html,因为我还没有研究过让它在生产环境中存在的安全影响。
    这将会对在Windows上开发的人的docker compose up造成混乱,所以如果你有在Windows上开发的团队成员,请告诉他们,他们可以将项目迁移到WSL或者解决这个问题(我在Windows上尝试了一下,但是我没有时间去寻找解决方法 ¯\(ツ)/¯ )。

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接