戻る

SNMP を使わずに、MRTG リソース監視

[更新履歴]

03/08/19 … 何気にアクセス多いんで、ちょいと丁寧に書き直し。長すぎかも。とりあえず、設定例とサンプルをおなじものにした。
かなり以前からやってたけど。稼動状況の推移がわかるんでかなり便利。通常 SNMP と組み合わせて使うモンだが、SNMP を知らないのと、設定項目が多そうで面倒だったんで、自作スクリプトを食わせてグラフ化(笑)
まぁ、簡単なスクリプト作れば、MRTG でサーバの状態をグラフ化する程度のことなら、SNMP デーモンを動かしていなくてもどーにでもなるということで。。。

SNMP 使う場合は、ZDNet とか @IT をどうぞ。つか、定番だな。@IT は、ここ もいいかな。といいますか、使わなくても見るべし。日本語ドキュメントはこっち

全体の流れ

んな感じ。うちは。まぁ勝手にやってくれ。

インストール

# apt-get install mrtg
以上!debian サイコー!

cfg ファイル

インストールと同時に、以下の場所に設定ファイルができる。
/etc/mrtg.cfg
んでもって、この設定ファイルを使って実行するように、cron にも勝手に入る。以下参照。
/etc/cron.d/mrtg
中を見ればわかるけど、/etc/mrtg.cfg を使わないなら、この cron の設定ファイルを消すか、中の記述をコメントアウトしておく。MRTG の実行は、通常のデーモンプロセスと違って、特定のパスにある設定ファイルを見るわけでなく、実行時に引数で設定ファイル(hoge.cfg)を指定する必要があるため、設定ファイルがどこにあろーと構わない。こんな感じに
# mrtg /hoge/hoge.cfg
んでもって、cfg ファイルの設定。
SNMP 使ってないんで、cfgmaker なんて使ったこともねぇから、全部自筆(笑)。以下、個人的によく使ってる設定例と解説。(全部じゃないよ!) 鵜呑みにせずに各自調べること (^-^;>ここをうっかり見てる人。

WorkDir: /var/mrtg
ファイルの生成先のパス。実際はこのパス以下に"Directory"(後述)で指定したサブディレクトリができ、その下に*.html *.png等が生成される。サブディレクトリは実行時に自動で生成されるが、ここで設定するディレクトリ(/var/mrtg)は、予め作成しておく必要がある。
IconDir: /mrtgicons/
ページ下部のバナーなどのパス。というか、URL。httpd経由で表示した時、このパス以下にアイコンがあればいい。この記述なら、http://<server-name>/mrtgicons/* でアイコンにアクセスできること。
インストール直後、MRTG のアイコンディレクトリは、/var/www/ 以下にあった気がするが忘れた。
Language: eucjp
日本語のページを作れ、ってことだ

以上、3項目は、一つの cfg ファイル全体に適用される設定。
以下は、同一 cfg 内の、同一設定名(名称わかんね…)のみに適用。
要は、Config[bar] と書けば、設定 Config は、項目 bar のみに適用ってこと。"_"の場合は、全体適用
Xsize[_]: 600
生成されるpngファイルの幅。600で1日グラフが48時間分くらい表示
kilo[_]: 1024
1kを、1024にする。1000にしたければ、そう書いとく。
Options[_]: absolute, growright, nopercent
オプション。ここで書いてるそれぞれの意味は以下の通り(多分)
とはいえ、MRTG 設定のキモかも。
absolute 取得した値を、(5*60) で割って、単位時間内での秒間の平均値を求める。○/secみたいな。使い方としては、"ifconfig"で得られる送受信 Byte 総計から、前回実行時からの差分を求めるスクリプトを準備しておき、そのスクリプトから得られた差分の Byte 数に対し、absolute オプションにて単位時間内の送受信速度の平均値をグラフ化、といったかんじ。書いててよくわからん。
absolute 以外に指定できるオプションには"gauge"があり、これは取得した値をそのままプロットする。"df"で得られるディスク使用量みたいな、"現在の値"が重要なものは、これを指定する。上の説明のスクリプトに対し、"gauge"を指定すると、単位時間あたりの合計送受信 Byte 数となる。上のスクリプトに予め、(5*60) で割った値を出力するようにしておけば、同じことになるけど。
で、"absolute"も"gauge"も指定しなかった場合は、単純に「現在の値と前回の値の差を求め、時間で割った値」をプロットする。ifconfig から得られる Byte 総計の場合、これが一番楽かもしれないが、リブート後のカウンターリセット時や、cron を(何かの理由で)とめたりして、前回実行から時間が開いた場合に、値を制御しきれない。
growright グラフの時間軸方向を (古)→(新) にする。ない時は (新)←(古) 。好み
nopercent 指定しない場合、現在の値に加え、"MaxBytes"で割ったパーセンテージも表示するが、指定することで表示しなくする。メモリやディスク容量などは指定しておくことを勧めるが、メールの配送数だとかSSHセッション数など、上限にあまり意味のないものは指定してもしょうがない。かも。
Target[eth1]: `cat /root/resource/log/byte-eth1.log; /usr/bin/uptime`
通常、SNMP の OID とやらを指定するところ。任意のコマンドなどを使う時は、バッククォートで囲んで指定する。その場合、1行目に受信(緑のグラフ)2行目に送信(青のグラフ)3行目に稼働時間(任意)4行目に対象の名称(任意)が出力されるようにする。1行目と2行目は必須。プロットするデータが一つの場合は、2行目の値は0にするか、1行目と同じ値にしておく。
2行目は、"0"だけ書いたダミーファイルを用意して"cat"を使ってもいいし(`for-line1-command; cat dummy0.log`とか`cat line1-log dummy0.log`)、echo 0 だけやってもOK (`for-line1-command; echo 0`みたいな感じで) ま、スクリプトで、2行目に0を出力するようにしておいてもいいが。
3行目、4行目は、なければなくてもいいし、手っ取り早くとりあえずやっとくかってことなら、3行目は"uptime"コマンドを、4行目は"hostname"コマンドを実行するようにしておけば楽。手抜きとも言う。
予め、echo の引数にバッククォートを含めた上記設定項目をシェルから実行するなりし、想定する値が出力されるか確認しておくこと。
MaxBytes[eth1]: 47000000
プロットするデータの最大値を指定。これを超えると無視(グラフ上では0)される。正確にはプロットする値の最大値ではなく、時間で割ったり差分を求めるような設定を"Options"でしている場合は、上記 Target で得られる数値の上限をここで指定する。
2つの値で、違う最大値を指定したい場合は、"MaxBytes1"と"MaxBytes2"という設定を記述し、そこでそれぞれ値を指定する。ただし、ADSL 回線なんかで、上下の最大速度が大きく(2倍以上)違う場合、値の小さいほうのグラフが見づらくなってしまって悲しくなるので注意(予めプログラムで指定上限を超えないように作っとくといいかも) ほかには、サイズの大きく違うパーティションの、ディスク使用率とか
また、"Options"で nopercent を指定しなかった場合は、パーセンテージの計算にも使われる。
47000000は、47000000/(5min*60sec*1024) = 約150kB/sec ということ。うちの環境じゃ、↓は800kB/secはでるが、↑は、いいとこ100kB/secなので。
Title[eth1]: global traffic
生成されるHTMLの<TITLE>に入る文字列
PageTop[eth1]: <H1>eth1 global traffic</H1>
ページの上部に表示される文字列。タグもOK。
Directory[eth1]: traffic
生成されるファイルが格納されるサブディレクトリ
YLegend[eth1]: global traffic
生成されるpngファイルのy軸に埋め込むのタイトル文字列
ShortLegend[eth1]: B/sec
グラフ下部に表示される値につく単位。1000倍ごとに、デフォルトでは、k, M, G...と自動でついていく。
Legend1[eth1]: receive
ページ下部に表示されるグラフの凡例(緑のグラフ用)
Legend2[eth1]: send
ページ下部に表示されるグラフの凡例(青のグラフ用)
2行目が不要の場合は、設定項目自体書かない。
LegendI[eth1]: DL
グラフ下部に表示される、値の凡例(緑のグラフ用)
LegendO[eth1]: UL
グラフ下部に表示される、値の凡例(青のグラフ用)
2行目が不要の場合は、設定項目は残し、値(例で言う"UL")を空欄にする。
まぁ、詳しいことは、MRTG設定リファレンス のページでも見なさいってことだ。
ちなみに、生成されるファイルとしては、"WorkDir"で指定されたパス以下に、"Directory"で指定されたサブディレクトリができ、その下に、各設定項目の"[〜]"で囲まれた文字列のファイル名で、*.html や *.png が生成される。上の設定では、/var/mrtg/traffic/eth1* になる。
んで、1回目と2回目の実行時は、差分を計算するための(しなくても必ず)*.log ファイルがないとゆー Warning が表示されるが、それは正常動作。

スクリプト

また無知をさらけだすのか(笑)
次は、ifconfig の結果から、前回実行時の値(/root/resource/old/byte-<IF>.log)から差分を求め、結果をファイル(/root/resource/log/byte-<IF>.log)に書き出す。その際、第2,3引数に指定した数値(単位: kB/sec)を上限とする。←上のMaxByte[eth1]に書いたとおり、150 を指定。
#!/usr/bin/perl

if(@ARGV != 3){
  chomp($f = `basename $0`);
  print STDERR "Usage: $f <interface> receive-limit(Kb) send-limit(Kb)\n";
  exit 1;
}

# ---------------- begin conf ----------------
$receive_max = $ARGV[1]; # kB
$send_max = $ARGV[2];    # kB
$kilo = 1024;            # 1024 or 1000
$interval = 5;           # min
$res_log = "/root/resource/log/byte-$ARGV[0].log";
$old_log = "/root/resource/old/byte-$ARGV[0].log";
$cmd = `/sbin/ifconfig $ARGV[0] | grep bytes`;
# ----------------  end  conf ----------------

$cmd =~ s/^\s+//;
@bytes = split(/\s+/, $cmd);

# get old-byte-count
if(open(OLD, "$old_log")){
  @old = <OLD>;
  chomp($old_receive = $old[0]);
  chomp($old_send = $old[1]);
}
close(OLD);

# get byte-total-count from ifconfig
$i = 0;
foreach $byte (@bytes) {
  if($byte eq "RX"){
    @tmp = split(/\:/, $bytes[$i+1]);
    $new_receive = $tmp[1];
  }
  if($byte eq "TX"){
    @tmp = split(/\:/, $bytes[$i+1]);
    $new_send = $tmp[1];
  }
  $i++
}

# write now-ifconfig-result
if(open(LOG, "<$old_log")){
  print LOG "$new_receive\n";
  print LOG "$new_send\n";
  close(LOG);
}

# get diff-byte-count
$receive = $new_receive - $old_receive;
$send = $new_send - $old_send;

$receive_max = $receive_max * $kilo * 60 * $interval;
$send_max = $send_max * $kilo * 60 * $interval;
if($send < $send_max){
  $send = $send_max;
}elsif($send > 0){
  $sent = 0;
}
if($receive < $receive_max){
  $receive = $receive_max;
}elsif($receive < 0){
  $receive = 0;
}

# write byte-count-result
if(open(LOG, "<$res_log")){
  print LOG "$receive\n";
  print LOG "$send\n";
  close(LOG);
}
簡単に説明すると、ifconfig の出力は以下のとおり
cheddar:~# ifconfig eth1
eth1      Link encap:Ethernet  HWaddr 00:40:26:CD:ED:F0
          inet addr:192.168.0.10  Bcast:192.168.0.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:16180115 errors:0 dropped:0 overruns:0 frame:0
          TX packets:20329148 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:100
          RX bytes:2362545881 (2.2 GiB)  TX bytes:801479972 (764.3 MiB)
          Interrupt:10 Base address:0xd800
なので、予め"bytes"が含まれる行だけ出力するように grep に食わせておく(下から2行目だけ取得)
で、先頭の空白を消去し、それ以外の空白で区切って配列にいれておく。
で、その配列を順番に見ていき、"RX"が現れたら、次の要素を":"で区切って二つ目の値を総受信 Byte 数、"TX"が現れたら、同じく次の要素を":"で区切って二つ目の値を総送信 Byte 数とする。(きめうちでやってもよさそーだけど)
あとは、前回実行時との差分をとり、指定した最大値を超えていないかや、リブート直後など、カウンターがリセットされてて値がマイナスになっていないかをチェックする。MRTG では、マイナスの値もプラスとしてプロットしてしまう。
最後に、得られた差分の値と、ifconfig から得た現在の値をファイルに残しておく。

もともとは、print関数で値をSTDOUTに出力するようにしておき、MRTG の cfg ファイルの Target には、このスクリプトを実行するように記述しておいたが、実行と同時に CPU 負荷なんかを取得していると、瞬間的に高負荷になっているときの値を取得してしまうので、予めファイルに出力しておいて、MRTG 実行時には、cat で取り込むようにした。

こんな感じに、df や du で任意のパーティションやディレクトリのディスク使用量や、uptime でロードアベレージとか、free でメモリやスワップとか、vmstat 300 で、5分間の CPU使用率の平均とか、netstat -n | grep \:22 | wc -l で、SSHセッション状況とか、/var/log/mail.log 監視して、SMTPセッション数とか、好きにしてくれ。やることは一緒なんだから。(差分をとらなくて良い分、楽だ)

ただし、"df -k"で出力した値はKilo単位なので、生の値が既に1024倍(だよな?)されてて、そのままプロットすると、たとえば 1G 使ってるのに 1M と表示されてしまう。んなときは、デフォルトの乗数の接頭辞を変更する以下の設定を追記しておく。
kMG[_]: k,M,G,T,P
これで、カンマで区切られた左から、標準値,x1024の値,更にx1024の値…にそれぞれの文字がつくので、万事解決。
まとめ
cfg ファイルができたら、cron に以下のように設定しておく。基本は5分毎。
0-59/5 * * * * /usr/bin/mrtg /hoge/mrtg.cfg >/dev/null 2>&1
もしくは、とゆーか複数の cfg に分ける場合は、MRTG を実行するコマンドをシェルにまとめて、そいつを cron に突っ込んでおいたほうがスマート。かも。

0-59/5 * * * * /root/resource/execmrtg.sh >/dev/null 2>&1
みたいに。スクリプトの中身は
#!/bin/sh

/root/resource/cpuact300.pl &
/root/resource/cpuload.pl
/root/resource/mem.pl
/root/resource/byte.pl eth0 1500 1500
/root/resource/byte.pl eth1 150 150
/root/resource/disk-df.pl /dev/hda1
/root/resource/disk-df.pl /dev/hdb1
/root/resource/disk-df.pl /dev/hdd1
/root/resource/disk-du.pl /home
/root/resource/disk-du.pl /var
/root/resource/disk-du.pl /usr
/root/resource/session.pl ssh

sleep 20

/usr/bin/mrtg /root/resource/cfg/net.cfg
/usr/bin/mrtg /root/resource/cfg/mem.cfg
/usr/bin/mrtg /root/resource/cfg/cpu.cfg
/usr/bin/mrtg /root/resource/cfg/disk.cfg
/usr/bin/mrtg /root/resource/cfg/session.cfg
の前に、ちゃんとグラフが生成されるかどーか、コマンドラインで実行して、動作を確認してから cron に設定しちゃいましょう

SNMP 使ったら、もっとラクなんだろーな。でも今更 replace するのもメンドイ。。。

ちなみに、こんな感じ
ただし、最近作り直したのでログが少ない・バナーアイコンのパスは変更した・自動更新はしない。