2011年5月24日火曜日

rrdtoolを利用して傾向線、予測線を描く

rrdtoolを利用してグラフを描くことがあるのだが、傾向と予測を表示できるようにさせたい。

調べてみるとrrdtool-1.3以降から意図したことができそうである。


早速、目的としたグラフが描けるか試してみた。





rrdtoolでグラフを描画させる定義はこのように記載した。

rrdtool graph /var/www/html/memory_stack.png \
--imgformat PNG \
--title processor \
--start now-6months \
--end now+6months \
--width=800 \
--height=300 \
--lower-limit=0 \
--upper-limit=100 \
--base=1024 \
--rigid \
\
\
HRULE:100#FF000044 \
HRULE:99.5#FF000044 \
HRULE:99#FF000044 \
HRULE:98.5#FF000044 \
HRULE:98#FF000044 \
HRULE:97.5#FF000044 \
HRULE:97#FF000044 \
HRULE:96.5#FF000044 \
HRULE:96#FF000044 \
HRULE:95.5#FF000044 \
HRULE:95#FF000044 \
HRULE:94.5#FF000022 \
HRULE:94#FF000022 \
HRULE:93.5#FF000022 \
HRULE:93#FF000022 \
HRULE:92.5#FF000022 \
HRULE:92#FF000022 \
HRULE:91.5#FF000022 \
HRULE:91#FF000022 \
HRULE:90.5#FF000022 \
HRULE:90#FF000022 \
\
\
DEF:alias1=node.rrd:utils64:MAX \
DEF:alias2=node.rrd:utils64:MAX:start=-6months \
\
AREA:alias1#00880077:data_past \
\
VDEF:alias2_slope=alias2,LSLSLOPE \
VDEF:alias2_intercept=alias2,LSLINT \
CDEF:alias2_predict=alias2,POP,alias2_slope,COUNT,*,alias2_intercept,+ \
CDEF:alias2_limit=alias2_predict,90,100,LIMIT \
VDEF:alias2_limit_min=alias2_limit,FIRST \
VDEF:alias2_limit_max=alias2_limit,LAST \
\
\
GPRINT:alias1:LAST:"last\: %2.1lf%s" \
GPRINT:alias1:MIN:"min\: %2.1lf%s" \
GPRINT:alias1:AVERAGE:"avg\:  %2.1lf%s" \
GPRINT:alias1:MAX:"max\: %2.1lf%s \n" \
\
LINE2:alias2_predict#FFBB00:data_predict:dashes=10 \
AREA:alias2_limit#FFBB0077 \
\
\
GPRINT:alias2_limit_min:"Reach 90% \: %c :strftime" \
GPRINT:alias2_limit_max:"Reach 100% \: %c :strftime"



グラフの終わりは現在時刻ではない。予測線を描くため、未来時刻を指定する。
ここでは6ヶ月先まで予測したいため、--end now+6months とした。


今回描画しているのはある機器のプロセッサの利用率である。
それが90%から100%の間を危険域とし、横軸に赤い線を引いておく。
HRULE:100#FF000044
から
HRULE:90#FF000022
のところである。


DEFにてalias1とalias2の二つの仮想名を定義した。
alias1は過去から現在値、つまり普通のグラフ描画用である。
alias2は予測線のグラフを描くためのものである。
統合関数をAVERAGEではなくMAXにしているのは、
リソースをもっとも多く使う値を元に予測線を描きたかったためである。
状況によってはAVERAGEでもよいだろう。
alias2の開始時刻を6ヶ月前としている。
DEF:alias1=node.rrd:utils64:MAX
DEF:alias2=node.rrd:utils64:MAX:start=-6months

ここを1ヶ月前にすれば、直近1ヶ月の傾向を予測することになる。
長期と短気な傾向を把握するために、alias3を定義してもよいだろう。
DEF:alias3=node.rrd:utils64:MAX:start=-1months


緑のエリア(山谷)グラフを描く。
AREA:alias1#00880077:data_past


続いて、予測線を描くための前処理を施す。
まず、予測線の傾きを得る。
VDEF:alias2_slope=alias2,LSLSLOPE


次に予測線の切片を得る。
6ヶ月前からの予測ということでstart=-6monthsにしたと説明したが、
そこのx軸上のy値を求めればいい。
VDEF:alias2_intercept=alias2,LSLINT
start=-1monthsとすれば、1ヶ月前のx軸のy値が切片となる。


傾きと切片は出た。よって、y = ax + b の計算ができる。
最初の"alias2,POP"が分かりにくいのが、スタックにあるalias2を
POPし捨て去っているだけだろうか。
書かなければ良いように思うが、なければ処理できない。
CDEFの定義に先立ち必要なのだろう。
書式は、逆ポーランド記法、RPN (Reverse Polish Notation)である。
CDEF:alias2_predict=alias2,POP,alias2_slope,COUNT,*,alias2_intercept,+


予測線が90%以上100%未満の領域に達した場合に、
ラインの予測線をエリア化するための前処理を下で行う。
オレンジで塗りつぶす箇所である。
見ればだいたい意味は分かるだろう。
CDEF:alias2_limit=alias2_predict,90,100,LIMIT
VDEF:alias2_limit_min=alias2_limit,FIRST
VDEF:alias2_limit_max=alias2_limit,LAST


グラフ配下に記載する文字なり値はGPRINTの行で指定する。
GPRINT:alias1:LAST:"last\: %2.1lf%s"
GPRINT:alias1:MIN:"min\: %2.1lf%s"
GPRINT:alias1:AVERAGE:"avg\:  %2.1lf%s"
GPRINT:alias1:MAX:"max\: %2.1lf%s \n"


いよいよ予測線だ。少し線を太くしたいためLINE2で描く。
dashesというのは破線という意味である。
LINE2:alias2_predict#FFBB00:data_predict:dashes=10


予測線が90%以上100%以下の領域は別途エリア化して描画する。
AREA:alias2_limit#FFBB0077


予測線がリミットに達する際の正確な日時は数字で
読み取れるようにしておいたほうがよいだろう。
GPRINT:alias2_limit_min:"Reach 90% \: %c :strftime"
GPRINT:alias2_limit_max:"Reach 100% \: %c :strftime"


以上である。



参考
http://oss.oetiker.ch/rrdtool/doc/rrdgraph_rpn.en.html
http://hints.jeb.be/2009/12/04/trend-prediction-with-rrdtool/