Docker 容器中 PHP 服务内存限制配置如何与宿主机保持一致

文章导读
Docker 容器中 PHP 服务的内存限制需分层配置,Docker 层通过 cgroups 设定容器硬上限,PHP 层通过 `php.ini` 设定应用上限,后者数值应小于前者以预留系统开销,避免触发宿主机 OOM Killer。
📋 目录
  1. 命令速用版
  2. 为什么会这样
  3. 分步处理
  4. 怎么验证是否生效
  5. 常见坑
  6. 常见问题
  7. 参考来源
A A

Docker 容器中 PHP 服务的内存限制需分层配置,Docker 层通过 cgroups 设定容器硬上限,PHP 层通过 `php.ini` 设定应用上限,后者数值应小于前者以预留系统开销,避免触发宿主机 OOM Killer。

先说结论:配置一致性依赖于 Docker 资源约束与 PHP 内部限制的双重管控,且需通过容器化编排固化配置。

  • 适合:生产环境资源隔离、多环境配置同步场景
  • 先准备:确定宿主机可用内存、编写 docker-compose.yml 与自定义 php.ini
  • 验收:通过 `docker stats` 观察容器内存、通过 `phpinfo()` 确认 PHP 限制生效

命令速用版

若使用 Docker CLI 启动,直接添加内存参数;若使用 Docker Compose,需在 deploy 块中声明。

# Docker run 示例:限制容器 512MB,禁用 Swap
docker run -d `--memory`=512m `--memory-swap`=512m php:8.1-fpm

# Docker Compose 示例:resources.limits 下配置
version: '3.8'
services:
  php:
    image: php:8.1-fpm
    deploy:
      resources:
        limits:
          memory: 512M

为什么会这样

内存限制失效通常是因为混淆了容器层限制与应用层限制。Docker 的 `--memory` 限制的是容器进程组能使用的物理内存总和,而 PHP 的 `memory_limit` 仅限制单个脚本进程。

若 PHP 限制大于 Docker 限制,PHP 脚本可能认为还有内存可用,但容器已达到 cgroups 上限,导致容器被宿主机内核强制杀死(OOM Kill)。若 PHP 限制过小,则应用频繁报错 `Allowed memory size exhausted` 但容器资源未用完。两者需配合,且 PHP 限制建议设为 Docker 限制的 70%-80% 左右,预留空间给 PHP-FPM 主进程及其他系统开销。

分步处理

1. 设定 Docker 容器硬限制

在 `docker-compose.yml` 的 `deploy.resources.limits` 下设置内存上限。注意在 Swarm 模式下该配置才完全生效,普通 compose 环境建议使用 `mem_limit` 参数(Compose V2 兼容)。

Docker 容器中 PHP 服务内存限制配置如何与宿主机保持一致
services:
  php:
    image: php:8.1-fpm
    mem_limit: 512m
    volumes:
      - ./php.ini:/usr/local/etc/php/conf.d/custom.ini

2. 配置 PHP 内部限制

不要直接修改容器内文件,应通过挂载卷注入配置。创建 `php.ini` 或 `conf.d` 片段,设置 `memory_limit`。

; custom.ini
memory_limit = 384M

若使用 PHP-FPM,还需检查 `www.conf` 中的 `php_admin_value[memory_limit]`,该配置优先级高于 `php.ini`,会覆盖脚本中的 `ini_set` 设置。

3. 重启并固化配置

修改配置后必须重启容器,仅 reload 配置可能不生效。

docker-compose restart php

怎么验证是否生效

1. 检查 Docker 层限制

使用 `docker stats` 查看实时内存使用与上限,确认 `MEM USAGE / LIMIT` 中的 LIMIT 值符合预期。

Docker 容器中 PHP 服务内存限制配置如何与宿主机保持一致
docker stats `--no-stream` php

2. 检查 PHP 层限制

进入容器执行命令,确认 `memory_limit` 值已更新。

docker-compose exec php php -i | grep memory_limit

输出应显示 `memory_limit => 384M => 384M`,若 Master Value 与 Local Value 不一致,说明被 `php_admin_value` 覆盖。

常见坑

1. php_admin_value 覆盖配置

在 PHP-FPM 的 `www.conf` 中,若设置了 `php_admin_value[memory_limit]`,代码中的 `ini_set('memory_limit', ...)` 将无效。需通过挂载覆盖 `www.conf` 或注释该行。

2. 忽略 PHP-FPM 多进程模型

Docker 容器中 PHP 服务内存限制配置如何与宿主机保持一致

PHP-FPM 通常运行多个子进程,`memory_limit` 是单个进程的限制。若 `pm.max_children` 设置为 10,`memory_limit` 为 384M,理论最大内存可能接近 3.8GB,需确保 Docker 容器限制能容纳峰值。

3. Swap 配置误区

单独设置 `--memory-swap` 而不设 `--memory` 无效。若希望禁用 Swap 以避免性能抖动,应将 `--memory-swap` 设为与 `--memory` 相同的值。

常见问题

为什么设置了 memory_limit 还是报 OOM?

因为 Docker 容器层面的 cgroups 限制先于 PHP 限制生效。若容器总内存超限,内核会直接杀死进程,PHP 内部限制来不及拦截。需调大 Docker 内存限制或优化代码减少占用。

本地和生产环境配置如何保持一致?

将 `docker-compose.yml` 和自定义 `php.ini` 纳入版本控制。通过环境变量区分敏感配置,但内存限制等核心参数应在配置文件中写死,避免依赖宿主机环境。

ini_set('memory_limit') 为什么不生效?

受限于 `php_admin_value` 设置。在 PHP-FPM 池配置中,管理员锁定的参数无法通过脚本动态修改,需修改容器挂载的 `www.conf` 文件。

参考来源

  • 怎么在 Docker 中配置容器的资源限制保障宿主机系统的稳定
  • trae 配置 php 的 memory_limit 细节_trae 内存限制注意点【步骤】
  • 如何用 Docker 限制 PHP 容器资源 PHP 服务内存与 CPU 控制策略
  • PHP 怎样在 Docker 容器中设置 PHP 的内存占用限制 PHP 限制内存占用的容器配置方法
  • 如何用 Docker 保持 PHP 环境一致 PHP 容器化本地与生产部署