在 Docker 中使用 Apache HTTP 服务器开发时如何解决文件权限问题?
Posted
技术标签:
【中文标题】在 Docker 中使用 Apache HTTP 服务器开发时如何解决文件权限问题?【英文标题】:How to solve file permission issues when developing with Apache HTTP server in Docker? 【发布时间】:2022-01-16 20:47:49 【问题描述】:我的 Dockerfile 扩展自 php:8.1-apache
。开发过程中会发生以下情况:
www-data
、33:33
)
我在容器中创建文件(作为图像的默认用户root
、0:0
)
这些文件安装在我作为user
(1000:1000
) 的主机上。当然,我现在遇到了文件权限问题。我想更新/删除在我主机上的容器中创建的文件,反之亦然。
我目前的解决方案是将图像的用户设置为www-data
。这样,所有创建的文件都属于它。然后,我将其用户和组 ID 从 33
更改为 1000
。这解决了我的文件权限问题。
但是,这会导致另一个问题:
我在入口点和命令前面加上sudo -E
。我这样做是因为它们通常以 root 身份运行,而我的自定义入口点需要 root 权限。 但是这样停止信号就会停止工作,并且当我希望它停止时必须将容器杀死:
~$ time docker-compose down
Stopping test_app ... done
Removing test_app ... done
Removing network test_default
real 0m10,645s
user 0m0,167s
sys 0m0,004s
这是我的 Dockerfile:
FROM php:8.1-apache AS base
FROM base AS dev
COPY entrypoint.dev.sh /usr/local/bin/custom-entrypoint.sh
ARG user_id=1000
ARG group_id=1000
RUN set -xe \
# Create a home directory for www-data
&& mkdir --parents /home/www-data \
&& chown --recursive www-data:www-data /home/www-data \
# Make www-data's user and group id match my host user's ones (1000 and 1000)
&& usermod --home /home/www-data --uid $user_id www-data \
&& groupmod --gid $group_id www-data \
# Add sudo and let www-data execute it without asking for a password
&& apt-get update \
&& apt-get install --yes --no-install-recommends sudo \
&& rm --recursive --force /var/lib/apt/lists/* \
&& echo "www-data ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/www-data
USER www-data
# Run entrypoint and command as sudo, as my entrypoint does some config substitution and both normally run as root
ENTRYPOINT [ "sudo", "-E", "custom-entrypoint.sh" ]
CMD [ "sudo", "-E", "apache2-foreground" ]
这是我的 custom-entrypoint.sh
#!/bin/sh
set -e
sed --in-place 's@^RemoteIPTrustedProxy.*@RemoteIPTrustedProxy '"$REMOTEIP_TRUSTED_PROXY"'@' $APACHE_CONFDIR/conf-available/remoteip.conf
exec docker-php-entrypoint "$@"
我需要做什么才能让容器再次捕获停止信号(对于 Apache 服务器是 SIGWINCH
)?或者有没有更好的方法来处理文件权限问题,所以我不需要用sudo -E
运行入口点和命令?
【问题讨论】:
你在 Docker 中根本不需要sudo
;甚至不要安装它。如果您希望主容器进程以 root 身份运行,请在末尾指定 USER root
而不是其他用户。
root
是默认用户,所以 USER root
甚至都不是必需的。但是当我进入容器(docker-compose exec app bash
)并创建一个文件(touch test
)时,该文件属于root
,我无法在主机上修改/删除它。
您可以直接在主机上编辑文件,而无需在容器内间接获取调试外壳。或者您可以docker-compose exec -u
替代用户。
我在容器中创建文件的实际方法是使用 Laravel 的 make
命令:php artisan make:model User
。这将创建一个带有蓝图的新类文件。我无法在主机上运行该命令,因为没有安装 PHP。如果我使用 docker-compose exec -u www-data
而不是我当前的解决方案,www-data
的用户和组 id 仍然是 33
,因此我的主机用户的权限仍然不匹配。此外,我一定不要忘记每次都输入-u www-data
。
【参考方案1】:
我需要做什么才能让容器再次捕捉到停止信号(对于 Apache 服务器是 SIGWINCH)?
首先,去掉sudo
,如果你需要在你的容器中以root身份运行它,在你的Dockerfile中使用USER root
以root身份运行它。容器中的sudo
几乎没有什么价值,因为它应该是一个运行一个应用程序的环境,而不是一个多用户通用 Linux 主机。
或者有没有更好的方法来处理文件权限问题,所以我不需要使用 sudo -E 运行入口点和命令?
我采用的模式是让开发人员以 root 身份启动容器,并让入口点检测已安装卷的 uid/gid,并在运行前调整容器中用户的 uid/gid 以匹配该 id gosu
删除权限并以该用户身份运行。我在base image example 中包含了很多这种逻辑(注意调整uid/gid 的fix-perms script)。该模式的另一个例子是我的jenkins-docker 图像。
您仍然需要将 root 的登录 shell 配置为在容器内自动运行 gosu
,或者记住在执行到映像时始终传递 -u www-data
,但现在 uid/gid 将匹配您的主机。
这主要是为了开发。在生产中,您可能不想要主机卷,而是使用命名卷,或者至少硬编码映像中用户的 uid/gid 以匹配生产主机上所需的 id。这意味着 Dockerfile 仍将具有 USER www-data
,但开发人员的 docker-compose.yml 将具有在生产中的 compose 文件中不存在的 user: root
。您可以在我的DockerCon 2019 talk (video here) 中找到更多相关信息。
【讨论】:
谢谢,我看了你的演讲并阅读了一些关于gosu
的帖子(其中一些是你写的)并拉入了你的修复烫发脚本!我剩下两个问题:1.) Composer/Maven 等工具在用户的主目录中创建缓存。所以我仍然需要创建/home/www-data
并把它交给www-data
,对吧?或者你怎么处理这个? 2.) 在运行docker-compose exec app bash
时,我找不到将root
的登录shell 配置为www-data
的方法。你有什么提示吗?
@user10517872 1 - 是的,您需要容器中的主目录,当 fix-perms 修改容器 /etc/passwd 中的 uid 时,它还会调整该 uid 拥有的文件。 2 - 这应该可以通过容器中的 /root/.bashrc 中的条目来执行类似exec gosu www-data bash
的操作。我会检查文件中通常存在的“交互式”检查,并在检查后添加您的命令。
1.) /var/www
是 www-data
的默认主页,但归 root
所有。我正在将我的文件挂载到/var/www/html
。一个简单的chown
就解决了问题,我不用在/home/www-data
下新建一个家了! 2.)我一直试图将其添加到/root/.profile
!谢谢!【参考方案2】:
您可以使用user namespace 将 docker 中的不同用户/组映射到主机上的您。
例如,容器中的组www-data
/33
可以是主机上的组docker-www-data
/100033
,您只需在该组中访问日志文件即可。
【讨论】:
谢谢!似乎需要进行很多配置才能开始使用它。当同事需要对我的项目进行更改时,他不太可能阅读文档并设置重新映射。不过我一定会试试的!以上是关于在 Docker 中使用 Apache HTTP 服务器开发时如何解决文件权限问题?的主要内容,如果未能解决你的问题,请参考以下文章
利用Ansible部署运行Apache(http)的Docker容器
Docker-compose apache guacamole 卡住了