技術支持分析師們常常接到用戶對服務器高負載的控訴。事實上cPanel軟件及其安裝的應用很少引發服務器高負載情況。服務器擁有者、系統管理員或者服務器供應商應當對高負載狀況進行初步調查,並在確認情況復雜後再向分析人士求助。
服務器高負載因何而起?
下列項目的過度使用會直接導致高負載問題:
該如何檢查這些項目?
這取決於大家是要審查當前資源使用情況還是歷史資源使用情況。當然,在本文中我們將從這兩方面進行探討。
關於sar的簡要說明
歷史資源使用情況可通過sar工具查看,該工具在默認情況下應該通過sysstat軟件包安裝在所有cPanel服務器當中。只要通過cron命令 對sysstat進行周期性執行(/etc/cron.d/sysstat),服務器的運行狀態數據就會被收集起來。如果cron沒有運 行,sysstat將無法收集歷史統計結果。
要在sar中查看歷史資源使用情況,我們必須為文件提供與統計數據相符的路徑。
舉例來說,如果大家打算查看本月23號以來服務器的平均負載狀況,可以運行以下命令:
sar -q -f /var/log/sa/sa23
以上命令中的-q用於獲取平均負載信息,而-f則用於指定sar從哪個文件中獲取信息。請注意,sar可能無法使用一周之前乃至更早的運行信息。
如果大家打算查看當前日期的統計信息,則不必為其指令具體時間。輸入以下命令即可顯示今天的平均負載情況:
$ sar -q
我們強烈建議大家閱讀sar說明文檔:
$ man sar
它所提供的統計信息能夠幫助我們確切掌握服務器的運行狀態。
當前CPU使用情況
運行
top,並在Cpu(s)一行中檢查%id部分所顯示的閒置CPU百分比。該數字越高結果越好,說明CPU的工作負載不強。處於99%閒置狀態下的CPU幾乎沒有處理任何實際任務,而處於1%閒置狀態下的CPU則意味著接近滿載。
$ top c
提示:可加寫P根據消耗CPU資源的多少對進程加以分類。
歷史CPU使用情況
查看“%idle”列:
$ sar -p
當前內存使用情況
$ free -m
提示:運行top c並加寫M可查看哪個進程占用的內存量最大。
歷史內存使用情況
根據sar版本的不同,命令內容也有所區別。早期版本通過添加“-r”參數顯示內存使用百分比與虛擬內存使用百分比,但新版本則改用“-s”參數顯示虛擬內存使用百分比。
Check %memused and %swpused:
$ sar -r
或者:
$ sar -S
內存使用情況提示:服務器內存占用量較高的情況其實非常正常。這是因為內存的讀寫速度及效率遠高於服務器磁盤,因此操作系統傾向於將內存作為緩沖機制預先載入數據,從而提高數據讀取速度。
同樣,內存使用百分比也並不是什麼大問題(除非大家沒有設置虛擬內存分區,但這也與內存本身無關)。大家真正需要關注的是虛擬內存使用百分比,因為只有在服務器的物理內存被全部占用後、虛擬內存才會接替而上發揮作用。這一數字越低,就說明服務器的運行狀態越好。如果虛擬內存使用率為0%,則意味著我們的服務器能夠完全利用物理內存執行任務。
那麼虛擬內存使用率達到多少才算過高?這取決於大家自己的感覺。一般來說,如果虛擬內存使用率一直不高、那麼我們的服務器的運行狀態還是比較理想的。如果大家發現虛擬內存使用率隨時間不斷提升(例如由1%到7%再到32%),這就代表服務器上的某些進程正在瘋狂吞噬內存,我們需要及時展開調查以了解具體情況(而不該直接安裝更多內存)。一旦服務器用盡了所有物理內存與虛擬內存,那麼整套系統的運行將變得極為緩慢,需要經過重啟才能暫時恢復正常。
當前磁盤I/O使用情況
注意:這一項對於OpenVZ/Virtuozzo容器不起作用。
以下命令將以每秒一次的頻率連續顯示十次磁盤使用率統計。請大家關注顯示結果中的%util列:
$ iostat -x 1 10
歷史磁盤I/O使用情況
$ sar -d
優秀的系統管理員能夠准確把握服務器負載的基准線,並在當前負載超出基准時立即做出判斷。這樣做的主要目的(除了防止服務器陷入半癱瘓並不得不重新啟動之外)是為了及時了解負載高企時服務器正在運行哪些項目。快速反應能幫助大家在發現問題後第一時間進行故障排查。
如果服務器負載過高的狀況出現在凌晨兩點到四點之間,那麼正在熟睡中的我們肯定無法馬上展開調查。雖然sar會一直守護在服務器身邊,幫我們收集這段時間內到底哪些資源的使用率居高不下,但卻無法揭示問題出現的實際原因。引發負載過高的原因多種多樣,其中包括DoS攻擊、垃圾郵件攻擊、php腳本設計不當、網絡蜘蛛在繪制網絡圖譜時太過積極、硬件故障、針對用戶MySQL
數據庫的磁盤寫入量暴增等等。
好消息是,大家可以利用工具收集這些信息,並在負載過高後將結果自動發送過來。如何實現?從進程列表入手:
$ ps auxwwwf
我創建了一個shell腳本,以我曾經管理過的服務器上的一套perl腳本為基礎。這套腳本與其它服務器監控工具(例如
Nagios)配合起來給我的工作帶來諸多便利。它能檢查六種不同項目(下面將詳細介紹),並在進程列表中的條目超出阈值時向我發送郵件通知。
注意:cPanel公司對該腳本的開發、維護或技術支持不承擔責任。請不要就這款腳本提出服務申請。如果您在使用中遇到任何問題,請到相關論壇上發帖或請教有經驗的系統管理員。cPanel不提供與此腳本相關的任何支持。
它所檢查的具體資源對象如下:
-
一分鐘平均負載
-
虛擬內存使用數量(單位為KB)
-
內存使用數量(單位為KB)
-
每秒接收數據包數量
-
每秒發出數據包數量
-
進程總數
如何使用腳本
要自動運行此腳本,大家需要設置一項cron任務並根據實際情況設定運行頻率。我發現每五分鐘運行一次是個不錯的選擇。該腳本無需使用root身份運行,既然如此我們也就不必為其分配高權限。
如果上述監控資源對象中的某一項超過用戶自定義的阈值,腳本會自動發送一封電子郵件,其中包含當前進程列表內容。
電子郵件的主題行如下所示:
server.example.com [L: 35] [P: 237] [Swap Use: 1% ] [pps in: 54 pps out: 289]
下面我們一一解釋其中的條目:
-
L代表一分鐘平均負載
-
P代表當前進程列表中的進程數量
-
Swap Usage代表虛擬內存使用百分比
-
pps in代表每秒接收數據包數量
-
pps out代表每秒發出數據包數量
腳本使用前的注意事項
重要事項:大家需要根據自己的理解來調整腳本中的數值。完美的默認值設定並不存在,因為不同的服務器環境在實際運行中所應遵循的標准也不一樣。舉例來說,擁有十六個CPU核心的服務器在一分鐘平均負載方面肯定要高於只擁有一個CPU核心的服務器。
注意:大家需要將自己的電子郵箱地址添加到EMAIL變量當中,如下所示:
[email protected]
以下五項也需要根據實際情況加以調整:
-
MAX_LOAD
-
MAX_SWAP_USED
-
MAX_MEM_USED
-
MAX_PPS_OUT
-
MAX_PPS_IN
#!/bin/sh
export PATH=/bin:/usr/bin
##########################################################################
# #
# Copyright Jeff Petersen, 2009 - 2013 #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
##########################################################################
###############################################################################
# START USER CONFIGURABLE VARIABLES
###############################################################################
EMAIL="[email protected]"
# 1 minute load avg
MAX_LOAD=3
# kB
MAX_SWAP_USED=1000
# kB
MAX_MEM_USED=500000
# packets per second inbound
MAX_PPS_IN=2000
# packets per second outbound
MAX_PPS_OUT=2000
# max processes in the process list
MAX_PROCS=400
###############################################################################
# END USER CONFIGURABLE VARIABLES
###############################################################################
IFACE=`grep ETHDEV /etc/wwwacct.conf | awk '{print $2}'`
if [[ "$IFACE" =~ "venet" ]] ; then
IFACE=venet0
fi
IFACE=${IFACE}:
###############################################################################
# 1 min load avg
###############################################################################
ONE_MIN_LOADAVG=`cut -d . -f 1 /proc/loadavg`
echo "1 minute load avg: $ONE_MIN_LOADAVG"
###############################################################################
# swap used
###############################################################################
SWAP_TOTAL=`grep ^SwapTotal: /proc/meminfo | awk '{print $2}'`
SWAP_FREE=`grep ^SwapFree: /proc/meminfo | awk '{print $2}'`
let "SWAP_USED = (SWAP_TOTAL - SWAP_FREE)"
echo "Swap used: $SWAP_USED kB"
###############################################################################
# mem used
###############################################################################
MEM_TOTAL=`grep ^MemTotal: /proc/meminfo | awk '{print $2}'`
MEM_FREE=`grep ^MemFree: /proc/meminfo | awk '{print $2}'`
let "MEM_USED = (MEM_TOTAL - MEM_FREE)"
echo "Mem used: $MEM_USED kB"
###############################################################################
# packets received
###############################################################################
PACKETS_RX_1=`grep $IFACE /proc/net/dev | awk '{print $2}'`
sleep 2;
PACKETS_RX_2=`grep $IFACE /proc/net/dev | awk '{print $2}'`
let "PACKETS_RX = (PACKETS_RX_2 - PACKETS_RX_1) / 2"
echo "packets received (2 secs): $PACKETS_RX"
###############################################################################
# packets sent
###############################################################################
PACKETS_TX_1=`grep $IFACE /proc/net/dev | awk '{print $10}'`
sleep 2;
PACKETS_TX_2=`grep $IFACE /proc/net/dev | awk '{print $10}'`
let "PACKETS_TX = (PACKETS_TX_2 - PACKETS_TX_1) / 2"
echo "packets sent (2 secs): $PACKETS_TX"
let "SWAP_USED = SWAP_TOTAL - SWAP_FREE"
if [ ! "$SWAP_USED" == 0 ] ; then
PERCENTAGE_SWAP_USED=`echo $SWAP_USED / $SWAP_TOTAL | bc -l`
TOTAL_PERCENTAGE=`echo ${PERCENTAGE_SWAP_USED:1:2}%`
else
TOTAL_PERCENTAGE='0%'
fi
###############################################################################
# number of processes
###############################################################################
MAX_PROCS_CHECK=`ps ax | wc -l`
send_alert()
{
SUBJECTLINE="`hostname` [L: $ONE_MIN_LOADAVG] [P: $MAX_PROCS_CHECK] [Swap Use: $TOTAL_PERCENTAGE ] [pps in: $PACKETS_RX pps out: $PACKETS_TX]"
ps auxwwwf | mail -s "$SUBJECTLINE" $EMAIL
exit
}
if [ $ONE_MIN_LOADAVG -gt $MAX_LOAD ] ; then send_alert
elif [ $SWAP_USED -gt $MAX_SWAP_USED ] ; then send_alert
elif [ $MEM_USED -gt $MAX_MEM_USED ] ; then send_alert
elif [ $PACKETS_RX -gt $MAX_PPS_IN ] ; then send_alert
elif [ $PACKETS_TX -gt $MAX_PPS_OUT ] ; then send_alert
elif [ $MAX_PROCS_CHECK -gt $MAX_PROCS ] ; then send_alert
fi
需要注意的是,進程列表的輸出內容中包含一些有用的數列,涉及各個進程的CPU與內存使用情況:
-
%CPU
-
%MEM
-
VSZ
-
RSS
-
TIME (顯示一個進程的存在時間)
我們可以通過多種方式剖析服務器負載高企的原因。下面我們列出幾項常用方案--僅供參考,並不全面:
-
利用mysqladmin processlist (或者簡寫為'mysqladmin pr')檢查MySQL進程列表
-
利用mytop檢查MySQL進程列表
-
查閱日志文件。了解服務器自身的反饋意見也很重要。您的服務器是否遭遇暴力破解?
-
運行dmesg以檢查可能存在的硬件故障
-
利用netstat查看服務器連接
下面則是值得關注的日志文件及其保存路徑:
-
系統日志: /var/log/messages, /var/log/secure
-
SMTP日志: /var/log/exim_mainlog, /var/log/exim_rejectlog, /var/log/exim_paniclog
-
POP3/IMAP日志: /var/log/maillog
-
Apache日志: /usr/local/apache/logs/access_log, /usr/local/apache/logs/error_log, /usr/local/apache/logs/suexec_log, /usr/local/apache/logs/suphp_log
-
網站日志: /usr/local/apache/domlogs/ (use this to find sites with traffic in the last 60 seconds: find -maxdepth 1 -type f -mmin -1 | egrep -v 'offset|_log$')
-
Cron日志: /var/log/cron