Httpd程序消耗大量記憶體
我有一個讓我頭疼的伺服器。它託管了幾個站點:這些站點要麼基於 php,要麼基於 java。我有一個設置,將 apache2 + suPhp 用於 php 站點,將 apache2 + mod_proxy + apache tomcat 用於 java 應用程序。
在過去的幾周里,我看到了一些奇怪的行為。有時,我會得到一個峰值達到 30-40% cpu 和 70% 以上記憶體的 httpd 程序。我沒有看到任何 php 或 java 程序佔用額外資源,所以我(天真地)假設問題與 php 或 java 程式碼無關。這些尖峰似乎發生在隨機時間和隨機間隔;有時每天多次,有時一整週都沒有發生任何事情。
我注意到的另一件奇怪的事情是,當我手動
kill -9
啟動正在執行的 httpd 程序時,另一個 httpd 程序會在幾秒鐘內彈出,並開始佔用大量記憶體和 cpu。我可以重複幾次,直到它自己停止發生:/所以,我其實有幾個問題:
- 有沒有人對我應該如何追踪這種行為的原因有任何提示?最終,我想看看是什麼請求導致了這些問題。我查看了 httpd 訪問和錯誤日誌,我真的找不到任何不尋常的地方。我對研究這類問題一點也不熟悉,所以即使對你來說似乎很明顯的事情也會有所幫助。
- 有沒有辦法限制單個 httpd 子程序可能消耗的資源量?或者當它超過一定數量的記憶體時直接殺死它?
- 與上一個問題相關,我是否可以將 oom-daemon 配置為在 httpd 程序上更觸發快樂,而在其他程序上更少?我問是因為,在我找到一個體面的解決方案之前,我想確保它在 httpd 程序再次開始執行時停止殺死我的 java 程序。
更新
最近我發現導致這個混亂的請求來自googlebot。
lsof
這是消耗所有可用記憶體和 cpu 的程序的輸出的摘錄:httpd 18588 nobody 37u IPv6 96675092 TCP myhost.com:http->crawl-66-249-76-96.googlebot.com:56730 (ESTABLISHED)
我設置了 mod_security 來記錄來自 googlebots 似乎在我的以下規則中使用的 IP 範圍的所有請求
<VirtualHost>
:SecRule REMOTE_ADDR "@ipMatch 66.249.76.0/24" phase:1,id:1,auditlog,allow
我就這樣離開了伺服器一段時間。在此期間,httpd 程序數次飆升,以至於 OOM 守護程序開始殺死程序(httpd、java,現在它甚至不時關閉 mysql)。然後我提取了 googlebot 命中的所有 url,並創建了一個小腳本來捲曲所有這些 url,希望我可以使 httpd 程序達到峰值,從而找到導致這些問題的請求。
不幸的是,這並沒有發生——所有的請求都很快返回,而且當 googlebot 訪問伺服器時,cpu 和記憶體使用量遠不及它們的水平。
所以我認為有兩種可能性:
- 問題是由於特定的 HTTP 標頭造成的。我的腳本沒有複製那些,它只是使用純 curl 沒有額外的標題。
- 導致問題的請求不會被記錄。據我所知,這不應該是這種情況,因為我告訴 mod_security 在階段 1 中記錄請求,這是在 apache 實際處理請求之前。
有沒有人有任何其他想法?
更新 2:
程序的strace輸出:
brk(0x3568c000) = 0x3568c000 brk(0x356ca000) = 0x356ca000 brk(0x35708000) = 0x35708000 brk(0x35746000) = 0x35746000 brk(0x35784000) = 0x35784000 brk(0x357c2000) = 0x357c2000 brk(0x35800000) = 0x35800000 brk(0x3583e000) = 0x3583e000 ... brk(0x3587c000) = 0x3587c000 brk(0x358ba000) = 0x358ba000 brk(0x358f8000) = 0x358f8000 brk(0x35936000) = 0x35936000 brk(0x35974000) = 0x35974000 brk(0x359b2000) = 0x359b2000 brk(0x359f0000) = 0x359f0000 brk(0x35a2e000) = 0x35a2e000 brk(0x35a6c000) = 0x35a6c000 brk(0x35aaa000) = 0x35aaa000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f2028000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f2005000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1fe2000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1fbf000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1f9c000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1f79000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1f56000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1f33000 ... mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1f10000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1eed000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1eca000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1ea7000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1e84000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1e61000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1e3e000 +++ killed by SIGKILL +++
提前致謝。
我沒有看到任何 php 或 java 程序佔用額外資源
您認為完全值得檢查,這表明您要麼非常了解 CGI 漏洞,要麼對 Web 服務/程序管理知之甚少。
最終,我想看看是什麼請求導致了這些問題
這是一個明智的起點。最簡單的解決方案是安裝 mod_security 並將其配置為記錄傳入的請求(Apache 僅在發送響應的點記錄)。還有其他方法,例如嗅探流量(pastMon、Wireshark)或登錄反向代理。
有沒有辦法限制單個 httpd 子程序可能消耗的資源量
不是直接的,但您應該將 LimitInternalRecursion、LimitRequestBody、LimitRequestFields、LimitRequestFieldSize、LimitRequestLine、MaxKeepAliveRequests、MaxRequestsPerChild 和 Timeout 設置為合理的值。
我可以將 oom-daemon 配置為在 httpd 程序上更觸發快樂嗎
弄亂 OOM Killer 幾乎總是一個壞主意。即使你認為你知道自己在做什麼。在沒有網路伺服器的情況下,您的 java 是否會做任何有用的事情?如果是這樣,那麼也許您應該考慮在單獨的機器上執行它們。
第一件事:您必須限制可以啟動的 apache 程序的最大數量。這可以通過降低來完成
MaxSpareServers
。從低位開始(3-10 並稍微增加一點,直到性能開始下降)。MinSpareServers
應該是2。PHP 通常作為 apache 模組 (mod_php) 執行,這意味著它與 apache 執行在相同的地址空間中,我的意思是您只會看到 apache 程序並且內部也會執行 php。您可以
gdb
在這些程序上執行並在backtrace
內部執行gdb
以查看它們在做什麼。你也可以用pstack
這個。如果您注意到哪個 URL 發生了這種情況(檢查 access.log 以找出 URL),那麼您可以開始調試 php 程式碼。搜尋 php 調試器或/和 php 分析器。