サーバ運用していて、たまに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