by Rain Chu | 2 月 11, 2026 | PHP, 程式
最近我的伺服器突然出現網站 502 錯誤,檢查後發現 php8.2-fpm 服務竟然被系統強制終止。透過一系列排查,最終確認是 Linux OOM Killer(Out Of Memory) 觸發導致。
這篇文章整理完整排查過程與解決方案,提供給遇到相同問題的人參考。
📌 問題現象
使用 systemctl status php8.2-fpm 發現:
Active: failed (Result: oom-kill)
進一步檢查 kernel log:
Out of memory: Killed process 7057 (php-fpm8.2)
anon-rss:283560kB
shmem-rss:119848kB
代表:
- 系統記憶體耗盡
- Linux 啟動 OOM Killer
- 強制殺掉 php-fpm worker
🔍 進一步分析
查看當時 PHP worker 記憶體使用情況:
ps -o pid,rss,etime,cmd -C php-fpm8.2 --sort=-rss | head -n 20
當時結果顯示:
RSS 約 250MB ~ 325MB/每個 worker
而之前狀態顯示:
Processes active: 80
idle: 0
🔥 問題核心
如果每個 worker 約 300MB:
但機器只有:
👉 記憶體一定會爆。
🧠 伺服器環境
這代表:
- 沒有 swap 作為緩衝
- 一旦瞬間記憶體尖峰,直接 OOM
🛠 解決方案
✅ 1️⃣ 建立 8GB Swap(防止再次 OOM)
sudo fallocate -l 8G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
確認:
結果:
建議調整 swappiness
echo 'vm.swappiness=10' | sudo tee /etc/sysctl.d/99-swappiness.conf
sudo sysctl -p /etc/sysctl.d/99-swappiness.conf
✅ 2️⃣ 調整 PHP-FPM 設定(關鍵)
編輯:
/etc/php/8.2/fpm/pool.d/www.conf
修改為:
pm = dynamic
pm.max_children = 25
pm.start_servers = 6
pm.min_spare_servers = 4
pm.max_spare_servers = 10
pm.max_requests = 500
為什麼設 25?
假設每個 worker 約 150MB:
在 15GB RAM 環境下非常安全。
✅ 3️⃣ 開啟 Slow Log(抓出慢請求)
在 www.conf 加入:
request_slowlog_timeout = 10s
slowlog = /var/log/php8.2-fpm/www-slow.log
request_terminate_timeout = 60s
建立目錄:
sudo mkdir -p /var/log/php8.2-fpm
sudo chown -R www-data:www-data /var/log/php8.2-fpm
sudo systemctl restart php8.2-fpm
查看慢請求:
sudo tail -f /var/log/php8.2-fpm/www-slow.log
📊 調整後效果
重啟後查看:
ps -o pid,rss,etime,cmd -C php-fpm8.2 --sort=-rss | head -n 20
結果:
RSS 約 100MB ~ 170MB
ELAPSED 僅 20 秒左右
✔ 不再出現 300MB 以上肥 worker
✔ 不再出現 active 80
✔ 系統穩定運作
🎯 問題總結
本次 OOM 原因:
pm.max_children 設定過高
- 每個 worker 記憶體使用偏高
- 沒有 swap 緩衝
- 慢請求導致 worker 堆積
最終解法:
- 限制 max_children
- 建立 swap
- 開 slowlog 抓慢請求
- 設定 max_requests 避免 memory leak
🚀 建議最佳實務
| 設定 | 建議 |
|---|
| Swap | 一定要有(至少 4GB) |
| max_children | 用 RAM / worker RSS 計算 |
| max_requests | 300~1000 |
| slowlog | 必開 |
| request_terminate_timeout | 必設 |
📌 結論
OOM Killer 不是錯誤,它是保護機制。
真正的問題通常是:
- PHP-FPM 設定不合理
- 慢請求堆積
- 記憶體容量與負載不匹配
只要正確限制 worker 數量並建立 swap,伺服器穩定度會大幅提升。
如果你也遇到 php-fpm 被 OOM kill 的問題,希望這篇實戰紀錄能幫助你快速排查與解決。
相關資訊
by Rain Chu | 12 月 31, 2025 | wordpress
目前正在處理的 request
編輯 php-fpm pool(www)
啟用下面的這幾行
重啟服務
檢查服務
curl 127.0.0.1/fpm-status?full
會看到的資訊
慢查詢-外掛定位
查詢
tail -f /var/log/php-fpm/slow.log
# 或是
tail -n 50 /var/log/php-fpm/slow.log
針對慢查詢,精準的找出問題
慢站點 Top:
sudo awk -F'= ' '/^script_filename/ {print $2}' /var/log/www-slow.log \
| awk -F'/' '{print $4}' | sort | uniq -c | sort -nr | head -20
入口 Top:
sudo awk -F'= ' '/^script_filename/ {print $2}' /var/log/www-slow.log \
| awk -F'/' '{print $NF}' | sort | uniq -c | sort -nr | head -20
外掛 Top:
多站的正確架構
每站一個 pool:
Nginx 的對應:
by rainchu | 12 月 19, 2024 | Nginx, SERVER, wordpress
大前提你要先安裝有支援 webp, avif 的外掛,例如EWWW, Convert Media等等
在 Nginx 配置中加 map 指令:
- 在 Nginx 的主配置文件,通常在
/etc/nginx/nginx.conf。
- 在
http {} 内、server {} 之前,加入以下内容:
map $http_accept $avif_suffix {
default "";
"~*avif" ".avif";
}
map $http_accept $webp_suffix {
default "";
"~*webp" ".webp";
}
- 配置客户端的
Accept ,來定義 $avif_suffix 和 $webp_suffix 變量,確認是否支持 AVIF 或 WebP 格式。
Nginx 讀取 AVIF 和 WebP 的 MIME Type:
找到 /etc/nginx/ 的 mime.types
image/avif avif;
image/webp webp;
在站點配置中設定重導向:
- 找到
/etc/nginx/sites-available/yourdomain。
- 在
server {} ,加入以下内容:
location ~* ^/wp-content/.+\.(jpe?g|png|gif)$ {
add_header Vary Accept;
add_header Cache-Control "private";
expires 365d;
try_files $uri$avif_suffix $uri$webp_suffix $uri =404;
}
作用是:
- 匹配所有請求的
.jpg、.jpeg、.png 和 .gif 文件。
-
Vary: Accept ,告訴缓存系統按照 Accept 來區分不同的響應。
- 缓存控制和過期時間。
- 使用
try_files 指令,會先試提供的 .avif 文件,如果不存在,則提供 .webp 文件,若仍不存在,則提供原始文件。
測試並且重新加載 Nginx 配置:
sudo nginx -t
sudo systemctl reload nginx
這些配置假設您的 .avif 和 .webp 文件與原始圖片文件位於相同的目錄,並具有相同的文件名,只是副檔名不同(例如,image.jpg 對應 image.jpg.avif 和 image.jpg.webp)。
此外,請確保您的伺服器上已經存在對應的 .avif 和 .webp 文件,否則 Nginx 將返回 404 錯誤。
有關更多詳細資訊,您可以參考以下資源:
網站圖片無縫兼容 WebP/AVIF
在 Nginx 上提供 WebP 和 AVIF 圖片
圖片優化新選擇!WebP、AVIF 如何提升網站速度
by Rain Chu | 10 月 19, 2024 | Nginx, SERVER
如果你也是 wordpress 愛用 WP Rocket 的使用者,並且WEB SERVER是用 NGINX 架設的,那肯定要參考這個 GitHub 項目,來作為你 Nginx 以及伺服器上面的最佳設定
安裝
先從 github 中下載下來
cd /etc/nginx
git clone https://github.com/satellitewp/rocket-nginx.git
並且利用 PHP 產生預設的配置檔
cd rocket-nginx
cp rocket-nginx.ini.disabled rocket-nginx.ini
php rocket-parser.php
設定 Nginx
找到 nginx 的設定檔,通常在 /etc/nginx/sites-available ,並且加入設定
server {
...
# Rocket-Nginx configuration
include rocket-nginx/conf.d/default.conf;
...
}
項目位置
GitHub Rocket-nginx
by Rain Chu | 4 月 17, 2024 | Nginx, NodeJS, SERVER, 程式
如果你有用 Nginx 的快取服務,無論是 fastcgi or proxy cache,都會遇到很難有一套真正好用的清除工具,這邊示範一套自己開發的清除工具,也支援多伺服器使用
安裝必要的套件
你先需要有nodejs,沒有的人先去官網安裝下,安裝完畢的人,先建立一個目錄,並且輸入以下指令,安裝必要的套件
npm init -y
npm install express
建立一個 Node.js App
可以命名為 app.js,內容如下,會建立一個小型的伺服器,用來處理 http 的請求,並且刪除指定的目錄
const express = require('express');
const { exec } = require('child_process');
const fs = require('fs');
const app = express();
const PORT = 3000;
const logFile = 'application.log';
function log(message) {
const timestamp = new Date().toISOString();
fs.appendFileSync(logFile, `${timestamp} - ${message}\n`);
}
app.get('/delete-dir', (req, res) => {
let dir = req.query.dir;
if (!dir || /[^a-zA-Z0-9_\-\/]/.test(dir)) {
log('提供的路徑不合法!');
return res.status(400).send('提供的路徑不合法!');
}
const command = `sudo find /run/proxy_cache/${dir} -mindepth 1 -type d -exec rm -rf {} +`;
exec(command, (error, stdout, stderr) => {
if (error) {
log(`执行错误: ${error}`);
return res.status(500).send('删除子目錄失敗!');
}
log('子目錄删除成功!');
res.send('子目錄删除成功!');
});
});
app.listen(PORT, () => {
log(`服务器正在运行于 http://localhost:${PORT}`);
});
其中我把能刪除的資料定義在只有這個目錄下的才行 /run/proxy_cache/ ,避免權限過大問題
配置 sudo 權限
打開 sudoers 文件
加入指令,記得把 <username> 改成自己的名字,或是 www-data
<username> sudo find /run/proxy_cache/ -mindepth 1 -type d -exec rm -rf {} +
完成後的測試
可以利用 CURL 呼叫 API ,成功就會看到 “子目錄删除成功!” 的訊息
curl "http://localhost:3000/delete-dir?dir=cache_directory"
結論與注意事項
要記得別讓外部的人可以輕易地存取這項服務,防火牆要記得把port鎖好,執行權限要小心設定,這樣就可以搭配 Nginx Cache 使用,之後再來出對應的刪除功能。
開機時候自動執行
要讓 Node.js 應用在系統開機時由特定使用者(例如 www-data)啟動,需要配置一個 systemd 服務單元。在這個情況中,我們將創建一個服務單元文件來確保 Node.js 應用作為 www-data 使用者運行。
創建 systemd 服務文件
建立一個新的 systemd 服務文件。
sudo nano /etc/systemd/system/nodeapp.service
加入以下配置
[Unit]
Description=Node.js Web Application
After=network.target
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/path/to/your/app
ExecStart=/usr/bin/node /path/to/your/app/app.js
Restart=on-failure
[Install]
WantedBy=multi-user.target
參數意思如下
Description: 服務的描述。
After: 這個單元應該在網絡服務可用之後啟動。
User 和 Group: 指定運行此服務的使用者和組。
WorkingDirectory: 指定 Node.js 應用的工作目錄。
ExecStart: 指定啟動應用的命令。
Restart: 指定何時重新啟動服務,on-failure 表示只在程序異常退出時重啟。
啟用和啟動服務
重新加載 systemd 配置以讀取新的服務文件:
sudo systemctl daemon-reload
啟用剛創建的服務,使其在開機時自動啟動:
sudo systemctl enable nodeapp.service
啟動服務以檢查它是否運行正常:
sudo systemctl start nodeapp.service
檢查服務的狀態來確認一切正常:
sudo systemctl status nodeapp.service
近期留言