ThinkPHP 项目部署到 Docker 如何配置权限写入日志目录

文章导读
ThinkPHP 容器化后写不出日志,通常是容器内用户身份与挂载目录权限不匹配,最稳妥的做法是在构建镜像时固定用户 ID 或在启动时修正挂载目录归属。
📋 目录
  1. 命令速用版
  2. 为什么会这样
  3. 分步处理
  4. 怎么验证是否生效
  5. 常见坑
A A

ThinkPHP 容器化后写不出日志,通常是容器内用户身份与挂载目录权限不匹配,最稳妥的做法是在构建镜像时固定用户 ID 或在启动时修正挂载目录归属。

先说结论:优先在 Dockerfile 中设定正确的用户权限,或在容器启动入口脚本中动态修正 runtime 目录归属,避免直接使用 777 权限。

  • 适合:使用 Docker 或 Docker Compose 部署 ThinkPHP 5/6/8 等项目
  • 先准备:确认容器内运行用户(如 www-data)及主机挂载路径
  • 验收:容器内能正常生成日志文件且无 Permission denied 报错

命令速用版

如果急需临时解决,可在容器启动后执行以下命令修正权限(假设用户为 www-data):

docker exec -it <容器 ID> chown -R www-data:www-data /var/www/html/runtime
chmod -R 775 /var/www/html/runtime

注意:容器重启后若未持久化修改,权限可能还原,建议写入 Dockerfile 或 entrypoint 脚本。

为什么会这样

Linux 系统通过用户 ID(UID)和组 ID(GID)控制文件读写。Docker 容器内的进程默认可能是 root,但为了安全通常切换到 www-data 等低权限用户。当主机目录挂载到容器时,如果主机侧目录归属与容器内用户 ID 不一致,容器内进程就没有写入权限。ThinkPHP 默认需要将日志写入 runtime/log 目录,权限不对就会报错。

分步处理

1. 确认容器运行用户

查看 Dockerfile 中是否有 USER 指令,或进入容器执行 id 命令。常见的是 www-data(UID 33)或自定义用户。

ThinkPHP 项目部署到 Docker 如何配置权限写入日志目录

2. 方案 A:修改 Dockerfile(推荐)

在复制代码后、切换用户前,修正目录权限。示例片段:

COPY . /var/www/html
RUN chown -R www-data:www-data /var/www/html/runtime
USER www-data

3. 方案 B:Docker Compose 挂载配置

使用 Docker Compose 时,需在 docker-compose.yml 中正确配置 volumes,并配合入口脚本使用。

version: '3'
services:
  web:
    image: php:7.4-fpm
    volumes:
      - ./runtime:/var/www/html/runtime
    entrypoint: /var/www/html/entrypoint.sh
    command: php-fpm

4. 方案 C:入口脚本动态修正

编写 entrypoint.sh,在容器启动时执行 chown 命令,确保每次启动都修正权限。脚本内容如下:

ThinkPHP 项目部署到 Docker 如何配置权限写入日志目录
#!/bin/sh
chown -R www-data:www-data /var/www/html/runtime
exec "$@"

记得给脚本执行权限:chmod +x entrypoint.sh

5. 方案 D:主机侧预授权(需谨慎)

在主机上运行 docker-compose up 之前,先修改主机挂载目录的权限。注意不要硬编码 UID,建议先确认主机是否存在对应用户。

# 如果主机有 www-data 用户
chown -R $(id -u www-data):$(id -g www-data) ./runtime

# 如果主机没有该用户,需确认容器内 UID(通常为 33)
chown -R 33:33 ./runtime

怎么验证是否生效

1. 触发一个报错或日志记录行为(如访问一个不存在的页面)。

2. 进入容器查看日志文件是否生成:

ThinkPHP 项目部署到 Docker 如何配置权限写入日志目录
docker exec -it <容器 ID> ls -l /var/www/html/runtime/log

3. 查看文件归属是否为容器运行用户,且最新修改时间是否为当前。

4. 实时查看日志更新:

docker exec -it <容器 ID> tail -f /var/www/html/runtime/log/error.log

常见坑

1. 直接使用 777 权限:虽然能解决问题,但在生产环境存在安全风险,任何用户都可写入,不建议长期使用。

2. UID 不一致:主机上的 UID 33 可能不对应 www-data,容器内却对应,挂载后权限混乱。建议统一规划 UID,或使用入口脚本在容器内修正。

3. SELinux 限制:如果主机开启 SELinux,即使 chmod 了也可能被拦截,可能需要设置 selinux 上下文或暂时关闭。

4. 重启失效:如果在容器内手动 chown 但未固化到镜像或启动脚本,容器重建后权限会还原。