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 のフロントエンドとして考えればいい
    • GUI フロントエンドという意味ではないよ
    • RRDTool を単体で使うと,はじめに RRD データベースを作るのがおっくうだったり cron を自力で回さなきゃいけなかったりするけど,そのへんを自動的にやってくれる

みたいな印象をうけました。プレゼンテーションレイヤは自分で好きなように組みたい人におすすめかと。

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 標準の設定では SocketGroupcollectd になってますので,このグループを追加するか,設定ファイルに

<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

外部コマンドの書き方は

を参照することになります。が,いまいちわかりにくかった。


外部コマンドから標準出力に

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.confHostname を明示的に指定しているのなら,それを設定する必要があるのかな。))。

plugin-instance の部分は usbrh としても大丈夫なはずですが,念のために exec-usbrh のように,プラグイン名は exec とし,ID として instanceusbrh にしました。

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.confInterval で設定された時間待って,再度実行してくれるので,このように無限ループにする必要はないです。が,そうすると負荷が増えるので,このようにずっと動きつづける監視プログラムの形にしたほうがよいでしょう。そして,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 rrdgraphman 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

先頭の RRDsRRDToolPerl バインディングです。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 で書かれています(はず)。CactiSpine(Cactid)なる C で書かれた軽量エージェントが出てきたみたいです。

*2:Version 4 では各プラグインが共有ライブラリ化されたので,あとから外部プラグインをビルドすることもできそうなんですが……ビルドプロセスを見るとそうでもないのかなぁ。ちゃんと調査してないのでなんともわかりません。

*3:しつこいようですが,あくまで EPEL の collectd パッケージの初期設定の場合です。

*4:だってレポジトリ全体とってこようと思ったらすごい時間かかるんだもん。

*5:本筋とは関係ないですが vim のモードラインで fileencoding が指定されているのがつらかった。