一定期間CPU負荷が高かった場合の、負荷が高いプロセスを記録する

サーバ運用していて、たまにMRTGの結果からCPU負荷が高くなる時間や日があったりします。
最近では、SSHの総当たり辞書攻撃を中国などから受けてて高くなってました。/var/log/secureファイルのサイズが大きくなってて、中を見たらsshの認証が失敗しているログが大量にたまってた。うちのSSHサーバは証書認証なので別に問題ないけど、負荷が上がって嫌な感じだったので、firewallでフィルタしました。

今回のようにログファイルなどで、負荷が高くなっている原因が分かればいいのですが、分からない場合は負荷が高いプロセスを記録しておいて、後からそれが分かるようにしておきたいと思い、簡単なスクリプトを書きました。linux CentOS4.4で動かしてます。いくつかのコマンドを利用しているのでlinuxしか動かない可能性が高いです。

今回は、SNMPとか入ってない環境でも動くように、負荷が高いというのをvmstatコマンドでCPUアイドルの数値をチェックし、負荷が高い状態が何回か続いた場合のみ、cpu, memoryの利用率が高い順にプロセスをソートして、上位n件をファイルに記録します。ついでにvmstatの結果も記録します。
vmstatは1秒間隔に3回実行し、3回目の実行結果の値を見ています(vmstatを1回のみ実行するだけだとうまくcpu負荷が取れなかったので)。


利用方法は、下記のスクリプトをサーバに置いて、実行権限を与えて実行するだけです。rootじゃなくてもかまいません。cronで5分間隔ぐらいで実行すれば良いと思います。
実行すると、そのディレクトリにvmstatのログと、負荷が高い状態のカウントファイルと、ログを出力するdataディレクトリを作成します。
下記のスクリプトのconfigの箇所を変えて好きな条件に設定可能です。ログファイルは、ログファイル出力毎にファイルが分かれており、ファイル名に日時分秒が入ります。


では、実際のスクリプトです。

#!/bin/sh

#This file Directory(この実行ファイルのパスを取得)
PROG_PATH=`dirname $0`



#----------------- config ---------------------------

#CPU Idole % Limit(CPUアイドルの%がこの値を下回ったら記録対象とする)
CPU_IDOLE_LIMIT=61

#Counter Limit(CPU負荷が高い状態が連続n回続いたらログを記録)
COUNTER_LIMIT=5

#number of process which is recorded(記録する負荷が高いプロセスの数)
CPU_PROC_NUM=7

#Counter file(カウントファイルの名前)
COUNTER_FILE=$PROG_PATH/count.txt

#Log directory path(ログデータ出力ディレクトリ)
LOG_DIR=$PROG_PATH/data

#vmstat temp log(vmstatの結果記録テンポラリファイル)
VMSTAT_LOG=$PROG_PATH/vmstat.log

#----------------- end config -----------------------

#vmstatを1秒間隔に3回実行し、それをファイルに記録
vmstat 1 3 > $VMSTAT_LOG


#vmstatの結果ファイルから、最後の行を抽出して、そこからcpuアイドルの値を抽出
Idole=`tail -1 $VMSTAT_LOG | gawk '{print $15;}'`


#記録するプロセスの数を1つ増やす(ヘッダ行が入るため)
CPU_PROC_NUM=`expr $CPU_PROC_NUM + 1`

#カウンタファイルがなければ作成する
if [ ! -e $COUNTER_FILE ] ; then
	
	echo "1" > $COUNTER_FILE
fi


#現在のカウントを取得(負荷が高い状態が続いた数)
COUNTER=`cat $COUNTER_FILE`


#CPUアイドルの値が規定値を下回れば記録対象とする
if [ $Idole -lt $CPU_IDOLE_LIMIT ] ; then

    #CPU負荷が高い状態が規定値回数以上続いたらログに記録する
	if [ $COUNTER -gt $COUNTER_LIMIT ] ; then

                #ログ出力ディレクトリがなければ作成
		if [ ! -d $LOG_DIR ] ; then
			mkdir $LOG_DIR
		fi

                #ログファイル名を作成 cpu_20081224123456.logというような名前
		LOG_FILE=$LOG_DIR/cpu_`date +%Y%m%d%H%M%S`.log

		#ログファイル出力 psコマンドの結果から、cpu負荷、メモリ使用率が高い順にソートし、上位n件をログに記録
		ps aux | sort -r -b +2 | head -$CPU_PROC_NUM > $LOG_FILE 

                #改行を2個入れて、ついでにvmstatの結果もログに記録
		echo "" >> $LOG_FILE 
		echo "" >> $LOG_FILE 
		cat $VMSTAT_LOG >> $LOG_FILE 
		
		
	
		#カウンタの値を初期化
		echo "1" > $COUNTER_FILE
		
	else
	
         	#カウンタの値を1つ上げる
		COUNTER=`expr $COUNTER + 1`
		echo $COUNTER > $COUNTER_FILE
	
	fi

fi

負荷が高かった日時が分かれば、それからログファイルのファイル名を見て該当ログファイルを見つけて、負荷が高いプロセスが発見できるかと思います。
ログファイルが出力されていれば、メールを送信するシェルと組み合わせれば、アラートメールみたいになりますね。


上記のプログラムは解説のために日本語コメントをいれていますが、それが原因で動かないこともあるので、日本語を含めていないプログラムを下記に記載します(うちではこっちを実行して動かしてます)。

#!/bin/sh

#This file Directory
PROG_PATH=`dirname $0`



#----------------- config ---------------------------

#CPU Idole % Limit
CPU_IDOLE_LIMIT=61

#Counter Limit
COUNTER_LIMIT=5

#number of process which is recorded
CPU_PROC_NUM=7

#Counter file
COUNTER_FILE=$PROG_PATH/count.txt

#Log directory path
LOG_DIR=$PROG_PATH/data

#vmstat temp log
VMSTAT_LOG=$PROG_PATH/vmstat.log

#----------------- end config -----------------------


vmstat 1 3 > $VMSTAT_LOG


#get CPU Idole %
Idole=`tail -1 $VMSTAT_LOG | gawk '{print $15;}'`



CPU_PROC_NUM=`expr $CPU_PROC_NUM + 1`


if [ ! -e $COUNTER_FILE ] ; then
	
	echo "1" > $COUNTER_FILE
fi


COUNTER=`cat $COUNTER_FILE`



if [ $Idole -lt $CPU_IDOLE_LIMIT ] ; then

	if [ $COUNTER -gt $COUNTER_LIMIT ] ; then

		if [ ! -d $LOG_DIR ] ; then
			mkdir $LOG_DIR
		fi

		LOG_FILE=$LOG_DIR/cpu_`date +%Y%m%d%H%M%S`.log

		#output Process info to log text.
		ps aux | sort -r -b +2 | head -$CPU_PROC_NUM > $LOG_FILE 
		echo "" >> $LOG_FILE 
		echo "" >> $LOG_FILE 
		cat $VMSTAT_LOG >> $LOG_FILE 
		
		
	
		#clear counter	
		echo "1" > $COUNTER_FILE
	else
		COUNTER=`expr $COUNTER + 1`
		echo $COUNTER > $COUNTER_FILE
	fi

fi