Apache-2.2

Httpd程序消耗大量記憶體

  • December 31, 2015

我有一個讓我頭疼的伺服器。它託管了幾個站點:這些站點要麼基於 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。我可以重複幾次,直到它自己停止發生:/

所以,我其實有幾個問題:

  1. 有沒有人對我應該如何追踪這種行為的原因有任何提示?最終,我想看看是什麼請求導致了這些問題。我查看了 httpd 訪問和錯誤日誌,我真的找不到任何不尋常的地方。我對研究這類問題一點也不熟悉,所以即使對你來說似乎很明顯的事情也會有所幫助。
  2. 有沒有辦法限制單個 httpd 子程序可能消耗的資源量?或者當它超過一定數量的記憶體時直接殺死它?
  3. 與上一個問題相關,我是否可以將 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 和記憶體使用量遠不及它們的水平。

所以我認為有兩種可能性:

  1. 問題是由於特定的 HTTP 標頭造成的。我的腳本沒有複製那些,它只是使用純 curl 沒有額外的標題。
  2. 導致問題的請求不會被記錄。據我所知,這不應該是這種情況,因為我告訴 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 分析器。

引用自:https://serverfault.com/questions/415033