擬鯖化Zabbixの実装

qpstudy 1周年記念LT大会!でLTした『Zabbixで人間監視』、けっこう反響があったので実装についてさらしてみます。


ビューとしてはLTで見せたように、2つのスクリーンを作っています。

全体監視画面

デモ画面

何人かのアイコンを並べたマップとイベント履歴を表示しています。(マップのリンクは自分に対するリプライ頻度をあらわします。)
アイコンの追加は「管理」→「一般設定」→「イメージ」で。

個人監視画面

デモ画面

タイムラインのテキストといくつかのグラフ(時間あたりのツイート数と、フォロワー数、リスト数の推移)を表示しています。



人間相手ではZabbixエージェントもSNMPも使えないので、監視は全て外部監視かZabbixトラッパーで行います。
これらはいくつかのスクリプトで成り立ってます。

[zabbix@admin01 ~]$ ls -1 /etc/zabbix/externalscripts/
config
get_tw_replies.pl
twitter.reply.count
twitter.timeline.get
twitter.userinfo.get

[zabbix@admin01 ~]$ cat /etc/zabbix/externalscripts/config/twitter_config.yaml 
access_token:
  access_token: 0000000-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  access_token_secret: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

consumer_token:
  consumer_key: XXXXXXXXXXXXXXXXXXXX
  consumer_secret: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

profile_dir:  /var/data/twitter/profile
reply_dir:    /var/data/twitter/reply
timeline_dir: /var/data/twitter/timeline


ユーザ情報、タイムライン取得部分について簡単に説明してみましょう。

ユーザ情報取得

以下の監視アイテムから成り立ってます。

ユーザ情報取得スクリプトをキックする外部監視アイテム


{HOSTNAME1}マクロを使ってスクリプトにホスト名で指定したTwitterIDを渡しています。
DNS名をTwitterIDにして「DNS名で監視」を選択すれば第一引数でそれが渡ってきますが、ちょっと気持ち悪いのでこの方法)

そのスクリプトからzabbix_senderで情報を突っ込むための複数のZabbixトラッパーアイテム


Zabbixトラッパーを使っているのツイート数、フォロワー数等を1度で取得、Zabbixに送信してTwitterAPI呼び出し回数を減らすためで、
これをcronスクリプトではなく外部監視アイテムでキックしているのは、監視対象、監視間隔をZabbixサーバ上で一元管理するためです。


スクリプトtwitter.userinfo.get)の中身は以下のようになっています。

#!/usr/bin/perl
use strict;
use warnings;
use utf8;

use Net::Twitter::Lite;
use YAML;
use Data::Dumper;
use File::Basename;

my @items = qw/friends_count followers_count statuses_count favourites_count listed_count/;
my $twitter_config_file = dirname($0)."/config/twitter_config.yaml";
my $zabbix_sender = "/usr/bin/zabbix_sender -z 127.0.0.1 >/dev/null ";

my $screen_name = $ARGV[1];
my $twitter_config = YAML::LoadFile $twitter_config_file;

my $consumer_token = $twitter_config->{consumer_token};
my $access_token   = $twitter_config->{access_token};

### get user information
my $t = Net::Twitter::Lite->new(%$consumer_token);
$t->access_token($access_token->{access_token});
$t->access_token_secret($access_token->{access_token_secret});

my $profile = $t->show_user($screen_name);
unless($profile){
  print 255;
  exit;
}

### send data
for my $item (@items){
  system "$zabbix_sender -s $screen_name -k twitter.userinfo.$item -o ".$profile->{$item};
}
print 0;
exit;

タイムライン取得

これもユーザ情報取得と同じように2段構えになっています。

タイムライン取得スクリプトをキックする外部監視アイテム


{HOSTNAME1}マクロを使ってスクリプトにホスト名で指定したTwitterIDを渡しています。

そのスクリプトからzabbix_senderで情報を突っ込むためのZabbixトラッパーアイテム


zabbix_senderは--with-timestampsオプションを用いるとタイムスタンプ付きで監視データを送信できるので、--input-fileオプションと合わせてツイートテキストを一気に突っ込んでいます。
詳しくはzabbix_senderで過去のグラフを作る - IT 東京 楽しいと思うことを参照してください。

スクリプトtwitter.timeline.get)の中身はこんな感じです

#!/usr/bin/perl
use strict;
use warnings;
use utf8;

use Net::Twitter::Lite;
use YAML;
use Data::Dumper;
use File::Basename;

my $twitter_config_file = dirname($0)."/config/twitter_config.yaml";
my $zabbix_sender = "/usr/bin/zabbix_sender -z 127.0.0.1 --input-file - --with-timestamps > /dev/null";

my $screen_name = $ARGV[1];

my $twitter_config    = YAML::LoadFile $twitter_config_file;

my $consumer_token = $twitter_config->{consumer_token};
my $access_token   = $twitter_config->{access_token};
my $timeline_dir   = $twitter_config->{timeline_dir};
my $since_id_file  = "$timeline_dir/$screen_name.since_id";
my $since_id       = load_since_id();

### get tweets
my $t = Net::Twitter::Lite->new(%$consumer_token);
$t->access_token($access_token->{access_token});
$t->access_token_secret($access_token->{access_token_secret});

my %opts = (screen_name => $screen_name);
$opts{since_id} = $since_id if $since_id;

my $tweets = $t->user_timeline(\%opts);
unless(@$tweets){
  print 0;
  exit;
}

### send data
open my $fh, '|-', $zabbix_sender;
binmode($fh, ":utf8");
for my $tweet (reverse @$tweets){
  print $fh "$screen_name twitter.timeline ". get_unixtime($tweet->{created_at}) ." ". $tweet->{text} ."\n";
#  debug_print("$screen_name twitter.timeline ". get_unixtime($tweet->{created_at}) ." ". $tweet->{text} ."\n");
}
close $fh;

save_since_id($tweets->[0]->{id});

print scalar @$tweets;
exit;

### end of main ###

sub debug_print {
  my $msg = shift;
  chomp $msg;
  open my $fh, ">>", "/tmp/zabbix_error_log";
  print $fh localtime(time) . " $msg\n";
  close $fh;
}

sub save_since_id {
  my $since_id = shift;

  open my $fh, ">", $since_id_file;
  print $fh $since_id;
  close $fh;
}

sub load_since_id {
  open my $fh, "<", $since_id_file;
  my $since_id = <$fh>;
  close $fh;

  return $since_id;
}

sub get_unixtime {
    my $created_at = shift;
    if (defined $created_at) {
        $created_at =~ s/\+0000/UTC/;
        $created_at = HTTP::Date::str2time($created_at);
    }
    else {
        $created_at = undef;
    }
    return $created_at;
}

とりあえずここまで!!!
気が向いたら、フォローすると監視登録され、アラートをタイムラインに流すTwitterボットを作ろうかと思います。
あとは特定キーワード(#qpstudyや「ほむほむ」)のツイート速度監視とか、かなぁ。
見た目のインパクトとしてやはりマップはおもしろいので、それももっとうまく使いたいところ。(しかしマップ関連のZabbixAPI、挙動がよくわからん・・・)