Linux OOM 排查實務:如何解決 Apache 記憶體耗盡導致的 WordPress 當機?
重點摘要 (Executive Summary)
- 現象: 伺服器無預警變慢、SSH 卡頓、特定服務(如 Apache/MySQL)反覆崩潰重啟。
- 主因: 通常並非外部攻擊,而是 Linux 核心觸發 OOM (Out of Memory) Killer 機制。
- 關鍵解法:
- 透過 Log 確認 OOM 事件。
- 計算單一 Apache Process 的記憶體消耗。
- 依據物理記憶體上限,精算並調校
MaxRequestWorkers參數。 - 長期建議從 MPM Prefork 遷移至 Event 模式。
當 Linux 主機發生 OOM (Out Of Memory) 時,最常見的症狀是整機回應延遲、Swap 分區瞬間飽和,緊接著某些關鍵服務(例如 Apache httpd 或 MySQL)被系統強制終止。
許多工程師在第一時間會誤判為 DDoS 攻擊,但實際上,這往往是伺服器資源配置不當所致。本文將整理一套標準的實務排查流程(SOP),並以 Apache 為例,深入探討如何進行合理的容量規劃 (Capacity Planning)。
一、 現場鑑識:確認「真的」是 OOM
在懷疑記憶體不足之前,我們必須先從系統日誌中尋找確鑿的證據。
1. 檢查核心日誌 (Kernel Log)
使用以下指令搜尋系統日誌,尋找核心殺手 (Kernel Killer) 的蹤跡:
# 檢查系統訊息
grep -i "Out of memory" /var/log/messages
# 或者使用 dmesg
dmesg | grep -i "killed process"
如果您看到類似下方的輸出,則確認是 OOM 無誤:
Out of memory: Kill process 2345 (httpd) score 950 or sacrifice child
Killed process 2345 (httpd) total-vm:40860kB, anon-rss:2048kB, file-rss:0kB
這代表 Linux 核心為了保護系統不當機,啟動了 OOM Killer,並選擇殺死分數 (Score) 最高、佔用最多記憶體的 httpd 進程。
2. 觀察當前記憶體壓力
使用 free -m 或 vmstat 1 5 觀察。如果發現 Swap used 居高不下,且 CPU 長時間處於 wa (I/O Wait) 狀態,這表示系統正在瘋狂地進行記憶體置換 (Swapping),這是崩潰的前兆。
二、 鎖定嫌疑犯:誰吃光了記憶體?
確認是記憶體問題後,下一步是找出「大胃王」。使用 top 或 ps 進行排序:
ps aux --sort=-%mem | head -n 10
在 WordPress 環境中,通常會發現前幾名都是 httpd (Apache) 或 php-fpm。我們需要進一步分析單一進程的「真實」記憶體佔用:
- VmRSS (Resident Set Size): 該進程實際佔用的物理記憶體。
- 判斷準則:
- 如果單個進程的 VmRSS 異常巨大,可能是程式碼有 Memory Leak。
- 如果單個進程 VmRSS 正常(例如 80MB),但進程數量極多,則屬於「併發參數配置不當」。
三、 架構分析:為什麼多半是 MPM Prefork 出事?
Apache 的運作模式 (MPM) 決定了記憶體的消耗方式。實務上,90% 的 OOM 案例都發生在 Prefork 模式。
🔴 MPM Prefork (高風險)
機制: 一個連線 = 一個獨立 Process。
缺點: 記憶體開銷極大。假設每個 Process 佔 100MB,當有 100 個連線時,就吃掉 10GB 記憶體。在高併發時,實體 RAM 極易被耗盡。
🔵 MPM Event (推薦)
機制: 多進程 + 多執行緒 (Multi-threaded)。
優點: 一個 Process 可以處理多個請求。記憶體利用率高,極少發生單純因為 Event 模式導致的 OOM。
四、 容量規劃:如何計算「合理」的 Apache 配置?
無論使用哪種 MPM,核心邏輯都是數學問題:「你的車庫(記憶體)能停幾輛車(Process)?」
步驟 1:測出平均記憶體消耗
我們可以使用一段腳本,精準計算出當前環境下,平均每個 Apache 子進程佔用的記憶體大小:
ps -ylC httpd | awk '
{ x += $8; y += 1 }
END {
print "Apache Memory Usage (MB): " x/1024;
print "Average Process Size (MB): " x/((y-1)*1024)
}'
假設輸出結果為:平均每個 Process 約 100MB。
步驟 2:計算 MPM Prefork 的 MaxRequestWorkers
這是最關鍵的一步。許多設定檔預設值為 150 或 256,但對於小記憶體主機來說,這是災難性的設定。
MaxRequestWorkers ≈ (總記憶體 - 系統預留記憶體) / 平均 Process 大小範例試算:
- 主機實體記憶體:16 GB
- 預留給 OS / DB / 其他服務:4 GB
- 可用給 Apache 的記憶體:12 GB
$$12,000 \text{ MB} / 100 \text{ MB} = 120$$
結論: 您的 MaxRequestWorkers 理論上限是 120。為了保留緩衝區(避免流量突增),建議設定在 100 左右。如果設定成 400,OOM 只是時間問題。
步驟 3:計算 MPM Event 的配置
Event 模式的計算稍微複雜一點,但邏輯相同。假設平均每個子進程約 10MB(Event 模式下較小),可用記憶體 7GB:
$$7,000 \text{ MB} / 10 \text{ MB} = 700 \text{ (最大並發線程數)}$$
在 Event 模式下,我們需要配置 ServerLimit (進程數) 與 ThreadsPerChild (每進程線程數)。
建議配置範例 (目標 600 並發):
ServerLimit 24ThreadsPerChild 25MaxRequestWorkers 600(24 * 25)
五、 處置建議與總結
當您確認 OOM 發生後,請依循以下步驟處理:
- 緊急止血: 透過
systemctl restart httpd釋放記憶體。若無效,暫時調整防火牆限制連線。 - 參數調校: 依據上述公式,修改
/etc/httpd/conf.modules.d/或apache2.conf中的MaxRequestWorkers數值。不要盲目調大,要「調準」。 - 壓力測試: 設定完成後,建議使用
httperf或類似工具進行壓測,觀察錯誤率與回應時間,驗證設定是否穩定。 - 架構升級 (最終解法): 如果調校後記憶體依然不足,建議升級主機規格,或是參考我們的另一篇案例,導入負載平衡 (Load Balancing) 架構。
還是搞不定伺服器的不穩定問題?
複雜的 Linux 底層優化與參數調校,交給專業的技術團隊來處理。