WordPress 502 和白屏排障(2026):从 nginx 日志查到 php-fpm 的内存账

今天早上打开自己站点,php-fpm 的日志里连着两条 server reached pm.max_children,往下翻 dmesg,躺着 5 条 mysqld 被 OOM 杀掉的记录,最近那条的时间点跟我前几次遇到的 502 对得上。这颗雷其实埋了几个月,只是以前都被我以为成网络抖动。

502 烦就烦在它不说话,但答案基本都在三份文件里:nginx 的错误日志、php-fpm 的日志、内核的 OOM 记录。顺序别乱,从上往下查最省时间。

一、先分清是谁在报错

502 Bad Gateway 是 nginx 的措辞,意思是它把请求转给后端之后没拿到能用的响应,跟 WordPress 本身没关系。站点前面是 nginx 加 php-fpm 的时候,先看站点自己的错误日志:

tail -n 50 /www/wwwlogs/littlesheep.cc.error.log

里面会写清楚请求被转到了哪:

... while reading response header from upstream, client: x.x.x.x, request: "POST /?rest_route=/batch/v1 HTTP/2.0", upstream: "fastcgi://unix:/tmp/php-cgi-81.sock:", host: "www.littlesheep.cc"

宝塔的站点日志按天轮转,往前翻用 zgrep,前缀是 littlesheep.cc.error.log 那种文件名:

zgrep -h "upstream" /www/wwwlogs/littlesheep.cc.error.log-2026*.gz | tail -20

日志里三种句子值得记住。connect() failed (111: Connection refused) 是 socket 那头没人监听,php-fpm 根本没起来;recv() failed 或者 upstream prematurely closed connection 是进程接了活然后中途死了,多半跟内存有关;upstream timed out 经常配 504 一起出现,是慢,不是断。

白屏和 500 是另一回事,那说明 PHP 已经跑完,是它自己吐出来的东西不对,往下看第三节。

二、php-fpm 活着吗,内存够不够

先看 socket 和进程:

ss -lx | grep php-cgi
ps -eo pid,rss,etime,args | grep "[p]hp-fpm"

我这边 socket 是 /tmp/php-cgi-81.sock,进程列表是 1 个 master 加 6 个子进程,有的已经跑了五个多小时。

内存那笔账才是关键。实测这 6 个子进程单进程 RSS 在 164MB 到 203MB 之间,平均 162MB,加起来 1.1GB,而这台机器总共 1.9GB,mysqld 自己常驻 301MB。RSS 会把共享的 opcache 段重复算进去,所以这是偏高的上限,不过方向不会错:pm.max_children 设成 10,等于向内核许诺 1.6GB,真凑满 10 个并发,就得看内核挑谁下手。

内核的记录在这里:

dmesg -T | grep -i "killed process"

我这边 5 条全是 mysqld。这台机器 vm.swappiness 是 0,内核基本不愿意拿 swap 顶一下,内存一紧就直接开杀。于是表现就是前台 502、数据库被干掉,这个组合能让人查一整天。

php-fpm 自己的抱怨也要看:

grep "max_children" /www/server/php/81/var/log/php-fpm.log | tail -5

我这边累计 31 条 server reached pm.max_children setting (10),最近一条就是今天早上 06:59。这里的 81 是 PHP 版本号,换成你环境里的那个。

三、白屏和 500 去哪里找 PHP 报错

同一份 error log 里,PHP 的报错会带 PHP message 前缀被抄一份进来,直接搜:

zgrep -h "PHP Fatal error\|PHP Warning" /www/wwwlogs/littlesheep.cc.error.log* | tail -20

我这边最大的一条是 REST API 批量请求带出来的 Undefined array key,不影响使用,但这类 warning 攒多了本身就是信号。

想看得更细,把 WordPress 自己的调试日志打开,在 wp-config.php 里加一行 define('WP_DEBUG_LOG', true);,报错会写进 wp-content/debug.log,这个文件要挡住外网访问,顺手确认一下 nginx 规则和 robots。display_errors 在生产环境保持 Off,宝塔默认就是 Off。

怀疑插件冲突的时候,WP-CLI 比手点快,先列清单再全关:

wp plugin list --status=active --field=name
wp plugin deactivate --all

之后逐个启用,盯 debug.log,第一个报错的往往就是元凶。这一步会让站点暂时少功能,安排到低峰期做。

四、慢才是 502 的常态,慢日志用起来

宝塔的 PHP 有个容易找错的地方,池子配置不在 etc/php-fpm.d/ 目录里,就在 /www/server/php/81/etc/php-fpm.conf 顶部。慢日志默认是开着的:

listen = /tmp/php-cgi-81.sock
pm = dynamic
pm.max_children = 10
pm.max_requests = 500
request_terminate_timeout = 100
request_slowlog_timeout = 30
slowlog = var/log/slow.log

翻译一下,单请求超过 30 秒就记一份调用堆栈,超过 100 秒 php-fpm 直接杀进程。而 nginx 那边 fastcgi_read_timeout 是 300,中间那两百秒用户在干等,最后拿到 502。

读慢日志:

tail -n 40 /www/server/php/81/var/log/slow.log

我这份 4.9MB、五万多行,最近一条是文件管理器插件在递归删缩略图,堆栈往下穿了十几层,这种活没人看着就会把子进程占住。

超过 100 秒的任务别走网页。导出、备份、批量改数据都放到终端用 WP-CLI 跑,跑多久都不影响前台。

五、改完怎么验证,怎么退回去

动配置前先备份,改完 reload 而不是 restart:

cp -a /www/server/php/81/etc/php-fpm.conf /root/php-fpm.conf.bak-$(date +%F)
/etc/init.d/php-fpm-81 reload

max_children 按内存倒推,给系统、mysqld、nginx 留出至少 400MB,剩下的除以单进程上限。1.9GB 的机器我放在 6 到 8 之间,不往上加,加之前先把缓存插件和数据库参数理一遍,不然只是把 OOM 的时间点往后推。

验证就并发打一轮,看有没有 5xx,也看耗时有没有越叠越高:

for i in $(seq 1 20); do curl -s -o /dev/null -w "%{http_code} %{time_total}\n" https://你的域名/ & done; wait

全是 200、单次耗时稳定在一秒内,说明池子够用。想看池子内部状态的话,php-fpm 有 pm.status_path 这个配置,不过它要自己在 nginx 里挂一段 location,我这台没挂,直接访问 /phpfpm_81_status 是 404。

回滚就是把备份覆盖回去再 reload 一次,十秒以内的事,这也是别用 restart 的理由,reload 不会掐断正在进行的长请求。

日志不会撒谎,这三种症状基本都能在几份文件里找到答案。我这次的结论不在 WordPress 身上,是内存分配的问题,同一台机器上压图片和备份那两篇可以顺路看:图片瘦身实操全站备份与恢复。监控那块要是还空着,站点挂了可能几个小时都没人知道,Uptime Kuma 这篇能补上。

© 版权声明
THE END
喜欢就支持一下吧
点赞7 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容