宝塔环境 PHP-FPM 进程 CPU 飙升排查与配置优化笔记

核心导读:记录一次小内存云服务器上 PHP-FPM 经常吃满 CPU 导致 502 报错的排查过程,包含 1G 至 4G 机器的进程参数配置参考、慢日志定位与预防思路。

这几天在维护一台 2 核 2G 的小云服务器,跑了两个 WordPress 和一个自建的 API 站点。平时负载都在 0.3 左右,但最近只要遇到蜘蛛抓取或者后台更新,CPU 经常瞬间冲到 100%,接着 Nginx 就开始吐 502 Bad Gateway。通过 top 命令一看,全是一堆 php-fpm 的工作进程在抢占计算资源。

顺手把这次的排查逻辑、修改后的配置参数以及后续的排错方式记下来,给有类似困扰的朋友做个参考。

1. 临时应急与排查现状

当 CPU 已经被 php-fpm 打满、网站打不开时,最快恢复服务的方式是在宝塔面板的【软件商店 -> 运行环境 -> PHP 设置】中点击【重载配置】或者【重启】。

如果连宝塔后台都卡死进不去,直接通过 SSH 连上服务器执行重启:

bash
systemctl restart php-fpm-74 # 根据实际 PHP 版本替换数字

恢复后先别急着走开,通过 SSH 查看当前系统中 PHP 进程的平均内存占用:

bash
ps --no-headers -o rss -C php-fpm | awk '{ sum+=$1 } END { printf "平均内存: %.2f MB\n", sum/NR/1024 }'

以常见的 WordPress 站点为例,一个装了几个常规插件的 PHP 7.4/8.0 进程,单进程内存通常在 30MB 到 60MB 之间。如果你的配置允许派生过多进程,内存耗尽后系统会频繁读写 Swap(甚至直接 OOM 杀进程),此时 CPU 就会因为大量的上下文切换和内存换页直接飙到 100%。

2. 关键运行参数调整参考

宝塔默认安装 PHP 时给的性能配置相对比较激进,特别是对于 1G/2G 的小机器,默认的 max_children 很容易设得太高。建议在 PHP 设置中的【性能调整】里改成以下数值:

服务器配置运行模式max_childrenstart_serversmin_spare_serversmax_spare_serversmax_requests建议 Swap 大小
1核 1Gondemand / dynamic102255001024 MB
2核 2Gdynamic20431010002048 MB
2核 4Gdynamic40852015002048 MB
4核 8Gdynamic8015104020004096 MB

这里重点解释两个关键参数:

  • max_children(最大子进程数):这是硬上限。公式一般是 (空闲物理内存 - 系统及 MySQL 保留内存) / 单个 PHP 进程内存。宁可设得稍微保守一点让请求排队,也千万不要让它无节制派生把系统内存吃光。
  • max_requests(单个进程最大处理请求数):默认可能是 0(不限制)或者几千。强烈建议改成 500 到 1000。PHP 代码或插件或多或少存在内存泄漏问题,让进程在处理完一定请求数后自动自杀并释放内存,是防止 CPU 和内存慢性跑满最有效的方法。

3. 定位具体是哪个请求拖慢了进程

参数改完后,如果某个时间段 CPU 依然会突然升高,说明根源在具体某段执行超时的代码或请求上。此时需要打开 PHP 的慢日志(slow log)。

在宝塔【PHP 设置】的【配置文件】中,搜索并确认以下两项开启:

ini
request_slowlog_timeout = 3 slowlog = /www/server/php/74/var/log/slow.log

这里把慢查询超时设为 3 秒(即任何执行时间超过 3 秒的脚本都会被记录下调用堆栈)。改完保存并重载 PHP 配置。

当再次发生高负载时,直接查看该日志:

bash
tail -n 50 /www/server/php/74/var/log/slow.log

日志会精确指出具体是哪个 PHP 文件的哪一行(例如某个主题的远程 API 请求超时、某个复杂的数据库查询、或者某个恶意抓取的 POST 接口),根据堆栈去修复代码或直接禁用异常插件即可。

4. 为什么调完参数还是会被打崩?

如果配置改小了,日志里也没有明显的死循环代码,但 CPU 依然间歇性被拉满,通常是以下外部原因造成的:

  1. 恶意扫描与垃圾蜘蛛疯狂抓取:海外部分采集程序会高频扫你的搜索页(如 /search?s=xxx)或后台登录页,每次搜索都会调用一次完整的 PHP 渲染和数据库查询。检查 Nginx 访问日志,如果看到某些特定 IP 或 User-Agent 几秒钟发出几十次动态请求,直接在 Nginx 层面用 limit_req 模块限流,或者用防火墙规则拦截,不要让这类请求穿透到 PHP-FPM 层。
  2. 缺失对象缓存与静态缓存:动态页面每次都让 PHP 去计算是非常昂贵的开销。可以在宝塔里给 PHP 装上 RedisAPCu 扩展,并为博客配置静态页面缓存(例如 WP Super Cache 或 Nginx fastcgi_cache)。让 90% 的普通访客直接由 Nginx 返回静态 HTML,PHP-FPM 的负载自然就会断崖式下跌。

常见问题与解答 (FAQ)

Q 改完 PHP 性能参数后,必须重启 PHP 才会生效吗?

是的,修改 php-fpm.conf 中的参数后,必须在宝塔面板点击【重载配置】或【重启 PHP】,新派生的子进程才会应用新的参数限制。

Q 小内存机器把 Swap 设大,会不会导致磁盘 IO 变高拖慢系统?

Swap 的主要作用是防崩溃兜底,避免系统直接 OOM 杀掉数据库或 PHP。只要将系统 swappiness 调低(如 10),平时系统依然优先用物理内存,只有在突发流量打满时才会走 Swap,不会对日常磁盘寿命造成明显负面影响。

返回运维知识库