这几天在维护一台 2G 内存的轻量云服务器,上面挂着两个 WordPress 博客。前天下午手机突然收到云监控报警,CPU 使用率直接飙到了 98%,前台页面也偶尔开始弹 502 Bad Gateway。SSH 登录服务器用 top 看了一眼,好几个 php-fpm: pool www 进程排在前面,每个都吃掉 30% 到 50% 的 CPU。
这类问题平时建站很常见,但如果不把排查链路理清楚,往往改完配置过几天又会崩。这里把处理过程和目前调校好的参数备忘下来,给遇到同样情况的朋友一个参考。
1. 临时恢复与紧急处置
排查前最重要的是让线上站点先恢复访问。如果此时 CPU 已经卡死、后台进不去,可以通过 SSH 终端快速平滑重载 PHP-FPM:
bash# 查看具体运行的 PHP 版本,以 php 7.4 为例 systemctl reload php-fpm-74 # 或者在宝塔环境直接执行脚本重启 /etc/init.d/php-fpm-74 reload
重载之后,挂起僵死的 worker 进程会被强制释放,CPU 会立刻回落。但这只是指标不治本,接下来需要排查配置和具体触发原因。
2. 关键参数设置参考
宝塔安装 PHP 时给的默认进程配置往往比较通用,如果服务器配置本身就不高(1G-2G 内存),默认参数很容易在小并发下就把内存吃满,进而导致频繁换页或 CPU 暴涨。
在宝塔面板打开 软件商店 -> 对应 PHP 版本 -> 设置 -> 性能调整,这里重点调整几个参数:
- pm(运行模式):建议选择
ondemand(按需分配)或dynamic(动态进程)。低配小内存机型推荐ondemand,空闲时自动回收内存。 - pm.max_children(最大子进程数):单个 PHP-FPM 进程平均占用 20MB~40MB 内存,如果机器只剩 1G 内存给 PHP,最多开 20-30 个。设置过大会直接导致物理内存耗尽触发 Swap 甚至 OOM 杀进程。
- pm.max_requests(每个进程最大请求数):建议设为 500 到 1000。PHP 代码或扩展难免有内存泄露,这个参数可以让 worker 进程处理满一定数量后自动退出重建,防止单个进程越来越大。
根据日常折腾经验,低配机器可以参考以下配置基准:
| 服务器配置 | pm 模式 | pm.max_children | pm.start_servers | pm.max_requests | 建议 Swap 大小 |
|---|---|---|---|---|---|
| 1核 1G | ondemand | 10 | 2 | 500 | 1024 MB |
| 2核 2G | dynamic | 20 | 5 | 800 | 2048 MB |
| 2核 4G | dynamic | 40 | 10 | 1024 | 2048 MB |
修改完后点击保存,配置会自动重载生效。
3. 查看日志确认具体原因
调整了进程数只能保证系统不轻易被耗尽,如果 CPU 依旧高频居高不下,多半是代码里有死循环、慢查询,或者有恶意脚本在扫站。
开启 PHP 慢日志(Slow Log)
在 PHP 配置文件中确认开启慢日志,定位具体卡在哪个文件的哪一行代码:
inirequest_slowlog_timeout = 3s slowlog = /www/server/php/74/var/log/slow.log
保存后观察 slow.log,如果某个 SQL 查询没有加索引,或者某个插件在远程拉取数据超时,日志里会把函数调用栈写得清清楚楚,直接针对性排查对应代码即可。
检查 Nginx 访问日志
打开 /www/wwwlogs/你的域名.log,用命令看下最近的高频请求 IP:
bashtail -n 5000 /www/wwwlogs/your_domain.log | awk '{print $1}' | sort | uniq -c | sort -nr | head -n 10
如果发现某个 IP 在短时间内有上千次 POST /xmlrpc.php 或者暴力尝试后台登录,说明被扫了。直接在 Nginx 配置中把恶意路径禁掉,或者在宝塔自带的防火墙/安全规则里拉黑该 IP 即可。
4. 为什么改完参数后还是会被打崩?
有时候参数设得很保守,日志里也没有慢查询,但 CPU 依然每隔一阵子就爆满,这通常是因为缓存机制缺失或者后台任务积压:
- 未启用 OPcache:在 PHP 扩展中务必安装
opcache,能显著减少脚本重复解析的 CPU 开销,静态页面和重复代码直接从内存读取执行。 - WordPress 的 WP-Cron 机制:每次有用户访问,WordPress 都会尝试检查定时任务,并发一高就会造成重复执行。可以在
wp-config.php中加入define('DISABLE_WP_CRON', true);,改用系统 Crontab 每隔半小时通过命令行静默触发一次。 - 突发爬虫抓取:很多非主流搜索引擎爬虫(如 Bytespider、SemrushBot 等)频率极高,可以直接在 robots.txt 或 Nginx 层通过
user_agent拦截掉,没必要浪费服务器算力给它们跑动态解析。