USB-RH で温湿度を収集しグラフ化(collectd & rrdtool)
USB-RH で遊ぶ - daily dayflower で USB-RH という USB 接続温湿度計を Linux から使えるようにしましたが,このままだとコマンドラインから温湿度を調べることしかできません。
やっぱり見たいのはグラフによる傾向!
てことで,普通なら RRDTool と組み合わせてグラフ化するのですが,ちょっと一手間加えて collectd で温湿度データを収集してみました。
collectd とは
collectd とは,マシンの様々なステータス(ネットワークインタフェースの転送量など)を収集するデーモンです。
似たようなソフトウェアに Nagios, ZABBIX, Munin, Ganglia, Cacti などいろいろありますが,collectd の特徴は,
- データの収集のみ(通知も対応)に重きをおいており,データのプレゼンテーション機能ははっきりいってない
- C で書かれておりデータベースも必要なく軽量*1指向(Inside the RRDtool plugin - collectd Wiki などに片鱗をうかがわせます)
- さまざまなプラグイン がある(まぁこれは先にあげた監視ツールはだいたいそうですが)
- 収集レイヤと出力レイヤ(ファイル,ログ,ネットワーク,RRDTool 等)がプラグインとしてわかれている
です。
個人的な感想は,
- RRDTool のフロントエンドとして考えればいい
みたいな印象をうけました。プレゼンテーションレイヤは自分で好きなように組みたい人におすすめかと。
collectd をインストール
CentOS 5.3 を使っているのですが,標準パッケージにはありませんでした。なのでソースからインストールしてもいいんですが,EPEL レポジトリを使いました。EPEL の collectd パッケージにはいろいろ不満なところもあったんですが,面倒さに負けた。
あと collectd の rrdtool プラグインは別建てなので,collectd-rrdtool パッケージもインストールしておきます。
collectd の初期設定
英語が苦手じゃなければ,First steps - collectd Wiki を読んだ方がいいかもしれません。
EPEL の collectd パッケージを導入した場合,設定ファイルは /etc/collectd.conf
および /etc/collectd.d/
以下になります。
デフォルトでさまざまなプラグインが有効になってますが,ばっさばっさと無効化していきましょう。もちろん,他の監視・収集項目も対象としたい場合は有効にしておいて構いません。
ミニマムとして syslog
プラグインくらいを有効化すればいいかな?必須ではありませんが。
ついでに unixsock
プラグインも有効にしておきましょう。これは UNIX ソケットを通じて collectd デーモンに指令を与えたりするものです。グラフ化の際に必要となることがあります(後述)。
unixsock
標準の設定では SocketGroup
が collectd
になってますので,このグループを追加するか,設定ファイルに
<Plugin unixsock> SocketFile "/var/run/collectd-unixsock" SocketGroup "wheel" SocketPerms "0666" </Plugin>
などのように書き,別のグループを指定してください。わたしは面倒なので permission を 0666
にして適当なグループを指定しました。
あ,あと,後述するように USB-RH のデータを収集するには外部コマンドを利用するための exec
プラグインが必要になるので,これを有効化しておくことも忘れずに。
その他 collectd の tips
今回の件では必要ではありませんが,hddtemp
プラグインを使う場合は,hddtemp
パッケージをインストールして hddtemp
サービスを起動しておく必要があります。
あと,iptables
プラグインを使う場合 libiptc が必要になるそうなんですが,所在がわかりませんでした。libiptcdata パッケージインストールしてみたけどダメでしたし。
collectd で USB-RH のデータを収集する外部コマンドを書く
標準添付されているプラグインではサポートしていないデータを収集するには,外部プロセスを経由する exec
プラグインを利用する必要があります。*2
外部コマンドの書き方は
man collectd-exec
- exec プラグインに関する Wiki ページ
を参照することになります。が,いまいちわかりにくかった。
外部コマンドから標準出力に
PUTVAL Identifier [OptionList] Valuelist
のように出力すると,そのデータを収集してくれます。
Identifier
の部分は,host/plugin-instance/type-instance
のような形式になります(-instance
の部分は省略可能)。plugin
の部分と instance
の部分は重複しない任意のものを指定することができます。
ですから,たとえば USB-RH の温度を記録してもらおうと思ったら,
PUTVAL penguin.example.com/exec-usbrh/temperature interval=30 N:28.51
のような形式になります。
host
の部分は,ホスト名を FQDN で指定します。これは必ずこのようにしなくてはなりません((もし /etc/collectd.conf
の Hostname
を明示的に指定しているのなら,それを設定する必要があるのかな。))。
plugin-instance
の部分は usbrh
としても大丈夫なはずですが,念のために exec-usbrh
のように,プラグイン名は exec
とし,ID として instance
を usbrh
にしました。
type-instance
の部分は temperature
のようにしました。つまり,type == temperature
ですね。もし複数台 USB-RH を接続するなら temperature-usbrh1
などのように instance
も指定すればよいでしょう。
んで,重要なことですが,temperature
は温度なので,(半ば適当に)そのように名づけた……というわけではなく,collectd が認識する値の種別として temperature
を指定したのです。この値の種別は /usr/lib/collectd/types.db
というプレーンテキストファイルに一覧で記述されています(追加することもできます)。
具体的には,
temperature value:GAUGE:-273.15:U
のような記述があります。RRDTool でいう GAUGE
タイプの値(実数)で,変数名は value
,最小値が -273.15 で最大値が undefined という意味です(くわしくは man types.db
)。
温度についてはもともと用意されていたのでよかったですが,湿度は自分で定義せざるをえないなぁ,と思って見てみたら,すでにありました。
humidity value:GAUGE:0:100
だから,湿度を記録する時には,
PUTVAL penguin.example.com/exec-usbrh/humidity interval=30 N:40.44
のように標準出力すればよいわけです。
Identifier
以外の部分の説明を続けます。ValueList
の部分は時刻+生データです。時刻は N
としておくと,現在時刻になってくれるらしい。ま,問題ないでしょう。
interval=30
という部分は,上記説明でいう OptionList
で,値を 30 秒おきに出力するよ,という宣言になります。んで,オプションだから指定しなくてもよしなにやってくれるかと思いきや,これを指定しておかないとうまく収集してくれません。必ず指定するようにしましょう。(ここではまった)
さて,これらの値を(今回の場合)30秒おきに出力する外部コマンドを作ればよいわけです。具体例は以下のようになります。
#!/bin/sh INTERVAL=30 HOST=`hostname -f` USBRH=/usr/bin/usbrh USBRH_OPTIONS="" while true do #TS=`date +%s` VALUES=(`$USBRH $USBRH_OPTIONS 2>/dev/null`) echo "PUTVAL $HOST/exec-usbrh/temperature interval=$INTERVAL N:${VALUES[0]}" echo "PUTVAL $HOST/exec-usbrh/humidity interval=$INTERVAL N:${VALUES[1]}" sleep $INTERVAL done
これみるとわかるとおり,USB-RH から値を読み取って標準出力に出力,そして sleep しているだけです。しかも,無限ループします。
このように collectd の外部コマンドは,一度起動したコマンドをずっと使いつづけてくれます。もし,終了した場合には,/etc/collectd.conf
の Interval
で設定された時間待って,再度実行してくれるので,このように無限ループにする必要はないです。が,そうすると負荷が増えるので,このようにずっと動きつづける監視プログラムの形にしたほうがよいでしょう。そして,collectd が終了する時は SIGTERM をこの外部プロセスに発行するので,その際にはきちんと終了してあげる必要があります(もちろん,今回のようにシェルスクリプトなどの場合 SIGTERM で落ちてくれるので特に対応する必要はありません)。
このようなシェルスクリプトを /usr/lib/collectd/usbrh.sh
などのファイル名で保存しておきます。
外部コマンドの設定を記述する
あとはこの外部コマンドを exec
プラグインの引数として渡してあげればよいです。でも,設定ファイル本体に書くとアレなので,/etc/collectd.d/usbrh.conf
などのファイルを用意してそこに書く方がよいでしょう。
<Plugin exec> Exec "dayflower:dayflower" "/usr/lib/collectd/usbrh.sh" </Plugin>
一番目の設定子は,user:group
の形式です。これを省略すると,collectd の起動しているユーザ権限となります。EPEL の collectd パッケージでは collectd は root 権限で動作するので,root:root
のように設定したことと同じようになります。んが,外部コマンドは,安全のために root 権限で実行することはできません(そのような設定になっていると安全のために exec
外部コマンドは実行されない)。なので,EPEL のパッケージの場合,必然的に非 root ユーザを上記のように明示的に設定する必要がでてきます。
さてさて,いろいろ面倒そうに見えたかと思います。
でも,逆に考えると,このような外部スクリプトを用意してあげるだけで,collectd が適切な RRD データベースファイルを自動生成してくれる((だからこそ interval=
オプションが必須なのでしょうが。))し,あとは定期的に RRD ファイルに値を投げてくれるのです。RRDTool のフロントエンドとして面倒な部分を請け負ってくれるのは楽ですね。
collectd を起動する
$ sudo /sbin/service collectd start
するだけです。
何か設定に問題があれば,syslog (/var/log/messages
)にエラーが出力されるので(逆にいうと,よっぽどクリティカルでなければ標準エラーには出力されません),念のためにログも見ておいてください。
グラフ化する
さて,collectd は最初に述べたように,値を収集することしかしません(繰り返しになりますが,通知機能もありますよ)。この収集した値をヴィジュアライズするのは,自分の責務になります。
rrdtool
プラグインを使っていると,この収集した値が /var/lib/collectd/hostname
以下*3に,RRD ファイルの形で置かれます。
今回は exec-usbrh
というプラグインの temperature
/ humidity
という instance として出力したので,
/var/lib/collectd/hostname/exec-usbrh/temperature.rrd
/var/lib/collectd/hostname/exec-usbrh/humidity.rrd
というファイルが生成されているはずです。
あとはこの RRD ファイルをグラフ化してやればよいだけです。これは普通に rrdtool コマンドを使えばできますね。
シェルスクリプトだけでもやってやれなくはなさそうですが,面倒だったので Perl で書きました。
#!/usr/bin/perl use strict; use warnings; use File::Spec::Functions qw( catfile ); our $RRD_DATADIR = '/var/lib/collectd'; our $HOSTNAME = do { local $_ = `hostname -f`; chomp; $_ }; our $PLUGIN = 'exec-usbrh'; my $rrd_temperature = catfile $RRD_DATADIR, $HOSTNAME, $PLUGIN, 'temperature.rrd'; my $rrd_humidity = catfile $RRD_DATADIR, $HOSTNAME, $PLUGIN, 'humidity.rrd'; my $ts_end = time; my $ts_start = $ts_end - 60*60*12; # 12 hours local $ENV{LANG} = 'C'; system 'rrdtool', 'graph', 'graph.png', '--start', $ts_start, '--end', $ts_end, '--upper-limit', 60, '--lower-limit', 20, '--rigid', '--title', 'USB-RH', '--vertical-label', 'C / %', '--width', 400-97, '--height', 200-79, '--slope-mode', "DEF:temperature=${rrd_temperature}:value:AVERAGE", "DEF:humidity=${rrd_humidity}:value:AVERAGE", "AREA:humidity#AAEEFF", "LINE1:humidity#00AAFF:Humidity", "LINE2:temperature#009900:Temperature\\c", ;
rrdtool コマンドの使い方は,man rrdgraph
や man rrdgraph-graph
してみてください。
上記コマンドを実行すると,以下のようなグラフが出力されます。
ちなみに。
今回はすんなり描けましたが,高負荷のシステムや収集項目が多いシステムでは最新のデータが描かれない可能性があります。これは,Inside the RRDtool plugin - collectd Wiki に書いてあるように,負荷軽減のため rrdtool への値の投入をキャッシングして遅らせる機構が存在するためです。
なので(そのような状況で)最新のデータを得たい場合は,上記で書いた unixsock
プラグインを有効にして,UNIX socket 経由で「RRDtool に値を FLUSH してね」と指示する必要があります。
contrib/collection3 を使う
今回はグラフが1種類だけなのでたいした手間ではありませんでしたが,collectd の収集してくれるたくさんのデータをレンダリングしていくのは骨が折れます。
他のサーバ監視アプリケーションではグラフの一覧を出したりなどの UI がついてきますが,collectd には基本的にはありません。ですが,contrib/
フォルダに一応ついてきます。それが Perl で書かれた collection3 です。こいつは先ほど書いた「RRD データの FLUSH」もやってくれます。
Perl で書かれているので,いくつかのモジュールが必要です。
RRDs
Config::General
HTML::Entities
Regexp::Common
先頭の RRDs
は RRDTool の Perl バインディングです。EPEL の場合,rrdtool-perl パッケージとして別に導入する必要があります。その他のモジュールはわりとメジャーなものが多いので,もともと入っている場合も多いでしょう。
んで,EPEL の collectd パッケージには残念ながら contrib/ フォルダは添付されていません。
などからもってくる必要があります。
わたしは git レポジトリからもってきました。といっても git はレポジトリの部分 clone はできなさそうでしたし,HTTP 経由だと履歴数を指定しての shallow clone もできなかったので,collection3 の snapshot をとってきて展開しました*4。
また,EPEL の collectd パッケージには collectd の Perl バインディングもついてこなかったので,Perl バインディングの snapshot もとってきました。本当であればこれも Perl モジュールとしてインストールする必要があるかと思うのですが,collection3 の lib/
フォルダにつっこみました。
適当な(ウェブから見える)フォルダに展開したあと,環境にあわせた設定を etc/collection.conf
に書く必要があるのですが,見ればわかると思うので省略します*5。
あくまでおまけとしか呼びようのない非常に簡素なインタフェースなのですが,グラフにマウスをオーバーさせると(上図の Humidity のように)ボタンが出てきて,それでグラフのスケールを変えることができます。
やっぱり collectd のデータを目で見たいということであれば,自分なりにコードを書いたほうがよさそうです。Webインタフェースでマシンを監視する4つの方法 2ページ | OSDN Magazine によると Cacti から RRD ファイルだけ利用するということもできそうなのですが,面倒そうだし Cacti 自身が DB を要求するしでちょっとなぁ,と思います。
*1:もちろん Nagios も C で書かれていますし,ZABBIX のエージェントも C で書かれています(はず)。Cacti も Spine(Cactid)なる C で書かれた軽量エージェントが出てきたみたいです。
*2:Version 4 では各プラグインが共有ライブラリ化されたので,あとから外部プラグインをビルドすることもできそうなんですが……ビルドプロセスを見るとそうでもないのかなぁ。ちゃんと調査してないのでなんともわかりません。
*3:しつこいようですが,あくまで EPEL の collectd パッケージの初期設定の場合です。
*4:だってレポジトリ全体とってこようと思ったらすごい時間かかるんだもん。