You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Docker容器中PHP file_put_contents无法写入Ubuntu宿主机的权限问题

Docker部署PHP应用的权限问题排查与解决

问题背景

我正在学习用Docker部署PHP应用,写了个简单的index.php,想用file_put_contents往Ubuntu宿主机的test.txt写入‘hello world’,但一直碰到权限问题。

初始配置文件

三个核心文件内容如下:

# ~/docker/docker-compose.yml
version: "3.8"
services:
  website:
    container_name: website
    build:
      context: ./
      dockerfile: Dockerfile
    ports:
      - "80:80"
    volumes:
      - ../public_html:/var/www/html
    logging:
      options:
        max-file: "10"
        max-size: 10m
# ~/docker/Dockerfile
FROM php:5.4-apache

RUN chown -R www-data:www-data /var/www/html

CMD ["/usr/sbin/apachectl", "-D", "FOREGROUND"]
// ~/public_html/index.php
<?php file_put_contents('test.txt', 'hello world');

执行的启动命令

用户john拥有sudo权限,执行了以下命令:

john@localhost: sudo su
root@localhost: systemctl start docker
root@localhost: exit
john@localhost: sudo chown -R john:john ~/docker
john@localhost: sudo chown -R john:john ~/public_html
john@localhost: sudo chmod -R 755 ~/public_html
john@localhost: cd ~/docker
john@localhost: sudo docker-compose up --build -d;

初始错误

访问http://192.168.0.10/index.php时出现错误:

Warning: file_put_contents(test.txt): failed to open stream: Permission denied in /var/www/html/index.php on line 1

手动在宿主机执行touch ~/public_html/test.txt && chmod 777 ~/public_html/test.txt能临时解决,但我想保持权限为755。

尝试UID对齐后的问题

了解到要对齐宿主机和容器的用户ID(UID),于是添加了.env文件设置CURRENT_UID=1000(通过id -u查到john的UID是1000),并修改了docker-compose.yml和Dockerfile:

# ~/docker/.env
CURRENT_UID=1000
# ~/docker/docker-compose.yml
version: "3.8"
services:
  website:
    container_name: website
    user: ${CURRENT_UID}
    environment:
      CURRENT_UID: ${CURRENT_UID}
    build:
      context: ./
      dockerfile: Dockerfile
    ports:
      - "80:80"
    volumes:
      - ../public_html:/var/www/html
    logging:
      options:
        max-file: "10"
        max-size: 10m
# ~/docker/Dockerfile
FROM php:5.4-apache

USER ${CURRENT_UID}:${CURRENT_UID}
RUN chown -R ${CURRENT_UID}:${CURRENT_UID} /var/www/html
RUN chown -R ${CURRENT_UID}:${CURRENT_UID} /etc/apache2
RUN chown -R ${CURRENT_UID}:${CURRENT_UID} /var/log/apache2
RUN chown -R ${CURRENT_UID}:${CURRENT_UID} /var/run/apache2

CMD ["/usr/sbin/apachectl", "-D", "FOREGROUND"]

修改后容器无法启动,查看docker logs website显示:

[Wed Jun 28 23:01:18.623744 2023] [core:error] [pid 7] (13)Permission denied: AH00099: could not create /var/run/apache2/apache2.pid
[Wed Jun 28 23:01:18.623918 2023] [core:error] [pid 7] AH00100: apache2: could not log pid to file /var/run/apache2/apache2.pid
Action '-D FOREGROUND' failed.
The Apache error log may have more information

用户疑问

  1. 我哪里操作有误?
  2. 容器启动失败后如何查看Apache错误日志?

问题排查与解决方案

1. 操作错误分析

核心问题出在镜像构建阶段环境变量未定义和错误切换用户修改系统目录权限:

  • Dockerfile构建时,${CURRENT_UID}是在docker-compose运行阶段注入的变量,构建镜像时根本不存在,导致USER ${CURRENT_UID}:${CURRENT_UID}执行失败,后续RUN chown命令也无法按预期生效。
  • Apache依赖www-data系统用户运行,直接切换到宿主机UID用户,该用户在容器内可能不存在,且就算存在,也没有权限操作Apache的系统目录(如/var/run/apache2、/etc/apache2),这些目录默认属于www-data或root用户。

2. 正确的权限对齐方案

不需要切换容器运行用户,而是把容器内的www-data用户UID改成和宿主机john用户一致(即1000):

修改后的Dockerfile

FROM php:5.4-apache

# 将容器内www-data用户的UID/GID改为宿主机用户的UID/GID
ARG CURRENT_UID
RUN usermod -u ${CURRENT_UID} www-data && groupmod -g ${CURRENT_UID} www-data
# 确保网站目录权限正确
RUN chown -R www-data:www-data /var/www/html

CMD ["/usr/sbin/apachectl", "-D", "FOREGROUND"]

修改后的docker-compose.yml

version: "3.8"
services:
  website:
    container_name: website
    build:
      context: ./
      dockerfile: Dockerfile
      args:
        CURRENT_UID: ${CURRENT_UID}  # 将环境变量传递到构建阶段
    ports:
      - "80:80"
    volumes:
      - ../public_html:/var/www/html
    logging:
      options:
        max-file: "10"
        max-size: 10m

.env文件保持不变

CURRENT_UID=1000

宿主机权限调整

确保宿主机public_html目录属于UID 1000(john用户),权限保持755即可:

sudo chown -R john:john ~/public_html
sudo chmod -R 755 ~/public_html

这样容器内的www-data用户UID与宿主机john一致,既能正常读写挂载目录文件,Apache也能以默认的www-data用户正常运行,不会出现系统目录权限问题。

3. 容器启动失败后查看Apache日志的方法

  • 容器未完全退出时:执行docker exec -it website /bin/bash进入容器,查看默认错误日志:cat /var/log/apache2/error.log
  • 容器已退出时:在docker-compose.yml中添加日志目录挂载,把容器内的Apache日志同步到宿主机:
    volumes:
      - ../public_html:/var/www/html
      - ../apache_logs:/var/log/apache2  # 新增日志挂载
    
    之后可以直接在宿主机~/apache_logs/error.log查看日志,即使容器启动失败也能获取完整日志。
  • 直接查看容器日志:docker logs website会输出容器的标准输出/错误,大部分Apache启动错误都会在这里显示,比如你之前看到的pid文件创建失败日志。

内容的提问来源于stack exchange,提问作者learningtech

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 10:47:02