blog移行しました
こっちに移行しました。やっぱりreStructuredTextで書けるほうがいいね。tinkererもかなり便利。
fluentdとfluent-plugin-pghstoreとpandorafmsでログ収集、可視化、監視を行う
前回の記事で報告したように、fluent-plugin-pghstoreでログをPostgreSQLに貯めることができました。
次は可視化と監視を行います。ここで、最近使ってみているPandora FMSを使います。
pluginを準備
まずは以下のスクリプトを保存し、pandora/etc/pandora/plugins以下に置きます。DBやTABLEは適宜書き換えてください。また、hostnameやportも適宜変更でお願いします。
上の方にあるSQLは過去5分間のcodeが2XXや3XXなどの割合を出してくれます。その後、PandraFMSでのplugin形式のXMLにするように整形します。
ちなみに、一つのSQLで複数を同時にcount()する方法については 複数同時にcount() をどうぞ。
#!/usr/bin/env sh DB=logdb TABLE=apache_log SQL=`cat <<EOT SELECT \ ROUND(100 * (s.C_2XX::numeric / s.all::numeric), 1) AS C_2, \ ROUND(100 * (s.C_3XX::numeric / s.all::numeric), 1) AS C_3, \ ROUND(100 * (s.C_4XX::numeric / s.all::numeric), 1) AS C_4, \ ROUND(100 * (s.C_5XX::numeric / s.all::numeric), 1) AS C_5, \ s.all AS count \ FROM( \ SELECT \ count(*) AS all, \ count(CASE WHEN record->'code' LIKE '2__' THEN 1 END) AS C_2XX, \ count(CASE WHEN record->'code' LIKE '3__' THEN 1 END) AS C_3XX, \ count(CASE WHEN record->'code' LIKE '4__' THEN 1 END) AS C_4XX, \ count(CASE WHEN record->'code' LIKE '5__' THEN 1 END) AS C_5XX \ FROM $TABLE WHERE time > (CURRENT_TIMESTAMP - interval '5 min') \ )s EOT` RESULT=`psql -At -F " " $DB -c "$SQL"` count=0 for p in $RESULT do case $count in 0) name="http_status_2XX" desc="HTTP Status Code percentage" ;; 1) name="http_status_3XX" desc="HTTP Status Code percentage" ;; 2) name="http_status_4XX" desc="HTTP Status Code percentage" ;; 3) name="http_status_5XX" desc="HTTP Status Code percentage" ;; 4) name="http_access_count" desc="access count" ;; esac echo "<module>" echo " <name><![CDATA[${name}]]></name>" echo " <type><![CDATA[generic_data]]></type>" echo " <data><![CDATA[${p}]]></data>" echo " <description><![CDATA[$desc]]></description>" echo "</module>" count=`expr $count + 1` done
あとは、pandora_agent.conf に以下の一行を足してください。
module_plugin http_access_status.sh
はい、終わりです。module作成は簡単ですね。
グラフ化
ここまで出来ればあとはPandoraFMS側でできます。
レポート管理 -> グラフビルダー で各ステータスコードの塗り潰しの積み上げグラフを作成します。
そうすると、こんな感じのグラフがリアルタイムで出てきます。
途中紫色が増えているのはアラートのテストを兼ねて試しに入れてみたものです。
監視
PandoraFMSは監視システムです。ですので、 4XXや5XXが25%を越えたら通知を出す、なんてことも簡単に出来ます。
ここでは詳しく述べませんので、 http://www.openideas.info/wiki/index.php?title=Pandora_3.0:Documentation_ja:Alerts こちらをご覧ください。
まとめ
fluentd + fluent-plugin-pghstore + PostgreSQL + Pandora FMSで
- ログ収集
- 可視化
- 監視 + アラート
が一気通貫で行えるようになりました。
今回試したものは、fluent-plugin-datacounter + out_growthforecast + growthforecast + nagiosで可能です。しかし、fluent-plugin-pghstoreを使った場合、SQLでいろいろな処理ができますので、アイデア次第で今回のStatus Code以外にも使えると思います。
fluent-plugin-pghstoreを書きました
fluentdをPostgreSQLのhstoreに書き出せるようにした、 fluent-plugin-pghstore というpluginを作成しました。
- github: https://github.com/shirou/fluent-plugin-pghstore
- rubygems: https://rubygems.org/gems/fluent-plugin-pghstore
hstoreについては 前記事 を参照してください。
install
gem install fluent-plugin-pghstore
apache_log
例えば、tail pluginを使ってapacheのaccess logをhstoreに出すようにする場合、こんなコンフィグを書きます。
<source> type tail path /var/log/apache/access_log_sym tag apache.access format apache </source> <match apache.*> type pghstore database test </match>
そして、こんな感じになります。
tag | time | record ----------------+------------------------+--------------------------------------- {apache,access} | 2012-04-01 22:55:15+09 | "code"=>"200", "host"=>"XXX.XXX.XXX.XXX", "path"=>"/", "size"=>"2608", "user"=>"-", "agent"=>"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.83 Safari/535.11", "method"=>"GET", "referer"=>"-"
tagは"."でsplitして、配列として格納します。はい、PostgreSQLは配列を扱えますので。
hstore型のrecordカラムには、ちょっと見にくいですが、各種keyが値とともに入っています。
http
さらに、こんなコンフィグを追加して、
<source> type http port 9880 </source>
curlで叩いてみると、
curl -F 'json={"log":"hoge"}' "http://localhost:9880/apache.curl"
tag | time | record ----------------+------------------------+--------------------------------------- {apache,access} | 2012-04-01 22:55:15+09 | "code"=>"200", "host"=>"XXX.XXX.XXX.XXX", {apache,curl} | 2012-04-01 23:28:44+09 | "log"=>"hoge"
同じテーブルに追加されましたね。 hstoreではkeyは動的に追加できますので、どんな形式のinput pluginでも大丈夫です。
例えば
一度PostgreSQLに入ってしまえばあとはSQLでいろいろなことができます。
UserAgentの数:
SELECT COUNT(*) AS c, record->'agent' FROM apache_log GROUP BY record->'agent' ORDER BY c;
過去10分間のアクセス数:
SELECT count(*) FROM apache_log WHERE time > (CURRENT_TIMESTAMP - interval '10 min')
過去10分間のstatus codeの数:
SELECT count(CASE WHEN record->'code' = '200' THEN 1 ELSE NULL END) AS OK_200, count(CASE WHEN record->'code' = '301' THEN 1 ELSE NULL END) AS MOVED_301, count(CASE WHEN record->'code' = '302' THEN 1 ELSE NULL END) AS FOUND_302, count(CASE WHEN record->'code' = '304' THEN 1 ELSE NULL END) AS NOTMODIFIED_304, count(CASE WHEN record->'code' = '401' THEN 1 ELSE NULL END) AS UNAUTHORIZED_401 FROM apache_log WHERE time > (CURRENT_TIMESTAMP - interval '10 min')
制限
ただし、こんな感じの多段のJSONを入れようとすると、だめです。
'json={"log":"hoge", "nest":{"a":"hoge", "b":"hige"}}'
hstoreが対応していないので難しいです。これは9.2のJSON型を待つのがいいのなあぁと思っているところです。
また、コネクションを一つだけしか使っていないので、おそらく高負荷環境では取りこぼしなどが発生する可能性があります。コネクションプールなどを使えばいいと思うので、patch大歓迎です!
こういう感じで
mongodbなどもいいですが、PostgreSQLもいいですよ。
PostgreSQL hstoreでKVS
それPostgreSQLでできるよ、第二弾。
PostgreSQLにはhstoreという拡張があります。
これはkeyとvalueの対の集合を単一のレコードに格納することが出来るものです。 つまり、Key-Value-Storeですね。 これを使うと通常のテーブルのようにキーを事前に定義しておく必要がありません。
hstoreについては以下の資料をみてください。特に後者は今回書いていない、いろいろな演算子・関数を紹介していますのですごく参考になります。
そして、今回の記事はherokuのこの記事を元にしています。
herokuはpostgresユーザなんですよ。
hstoreを入れる
hstoreはcontribに入っています。今回はFreeBSDを使用したので、以下のように入れます。
% sudo portinstall databases/postgresql91-contrib
続いてDBにhstoreを入れます。9.1から簡単に拡張を入れられるようになりました。
% psql test -c "CREATE EXTENSION hstore;"
さて、これでhstoreを使う準備は出来ました。
hstoreのテーブルを定義
さっき定義する必要ないって言ってたじゃん、という声もありますが、hstoreという部分だけは定義する必要があります。 といっても、hstoreの中身を定義する必要はありません。
CREATE TABLE products ( id serial PRIMARY KEY, name varchar, attributes hstore );
今回はこうしてみました。herokuのblogのとおりですね。
データを入れる
INSERT INTO products (name, attributes) VALUES ( 'Geek Love: A Novel', 'author => "Katherine Dunn", pages => 368, category => fiction' );
データを入れるには key => value という構文を使 います。文字列は"で囲むと空白、=、>という記号も入れられます。
検索する
SELECT name as device FROM products WHERE attributes->'category' = 'fiction'
検索時はkeyを -> で指定します。
また、"?"を使うことで続く値がキーとしてあれば、という意味になります。
SELECT name, attributes->'pages' FROM products WHERE attributes ? 'pages'
その他
indexも作れます。
CREATE INDEX product_manufacturer ON products ((products.attributes->'manufacturer'));
ただ、汎用転置インデックスのGINを使ったほうがいいという話もあります。
joinもできます。
SELECT manufacturers.country, products.name FROM products, manufacturers WHERE products.attributes -> 'manufacturer' = manufacturers.name;
それPostgreSQLで出来るよ - twitter_fdw
pyfesで「それPostgreSQLでできるよ」ってつぶやいた手前、ちゃんと試さなければなりません。
PostgreSQL 9.1から外部データラッパ(FDW) という規格がサポートされました。 またさらに、このFDWを使ってTwitterのAPIを叩いて結果をテーブルとして出 してくれる twiter_fdw という拡張が 公開されています。
準備
% sudo apt-get install libcurl4-openssl-dev (libjsonもいるかも) % curl -O http://api.pgxn.org/dist/twitter_fdw/1.0.0/twitter_fdw-1.0.0.zip % unzip twitter_fdw % cd twitter_fdw % make % sudo su # export USE_PGXS=1 # make install
DBの作成して、twitter拡張をDBに入れます。
% createdb twitter % psql -c "CREATE EXTENSION twitter_fdw" twitter
さて、これで準備は終わりです。
使ってみる
% psql twitter twitter=# SELECT from_user, created_at, text FROM twitter WHERE q = '#pyfes'; from_user | created_at | text ---------------------------------------------------- tw_ox | 2012-03-19 13:43:58 | RT @zusaar: 【新着イベント】Python Developers Festa (一般枠) #pyfes #pyspa http://t.co/X9SyQ0hg #zusaar #イベント #eventjp inoshiro | 2012-03-19 10:46:58 | RT @shomah4a: 少ないけど写真上げました https://t.co/aqvDAjrC #pyfes shomah4a | 2012-03-19 10:32:03 | 少ないけど写真上げました https://t.co/aqvDAjrC #pyfes tcsh | 2012-03-19 10:14:06 | RT @tk0miya: Sphinx ハンズオンの資料(サンプル)です。 http://t.co/TzokaIJ2 #sphinxjp #pyfes
中身はTwitter APIを叩いているだけなので、残念ながら WHERE from_user ='' とは書けません。投稿者を探すにはqの中にfrom:をつけます。 詳しくは https://dev.twitter.com/docs/using-search を見てください。
twitter=# SELECT from_user, text FROM twitter WHERE q = 'from:voluntas' limit 5; from_user | text ----------------------------------------------- voluntas | @Surgo お、一口書いとくね voluntas | @turky や一口とか提示しなかったので、再度確認します。イメージは一口 1000 円で問題ないです。 voluntas | @turky ちょw ブログにまとめます。 voluntas | 支援は一人1000円として 15000 円位か。 voluntas | 姉に Twitter Bot の作り方を聞かれている
ちなみにdefaultでは15件までしか出ませんが以下のようにしてあげると100件まで出るようになります。
- appendStringInfo(&buf, "q=%s", + appendStringInfo(&buf, "q=%s&rpp=100",
いろいろしてみる
# SELECT to_user, count(to_user) FROM twitter WHERE q = 'from:voluntas' GROUP BY to_user ORDER BY count(to_user) desc limit 10 to_user | count ----------------+------- tokoroten | 8 Seasons | 7 heavenshell | 6 sawonya | 4 turky | 3 tokibito | 3 yokatsuki | 3 mkouhei | 3 mopemope | 3 Surgo | 2
fdwいいよfdw
pyfes 2012.3に参加してきました。
pyfes 2012.3に参加してきました。pyfesは初めてで緊張しました。
Mercurial ハンズオン
とりあえず朝から来たら最初はハンズオンなんですねー。知りませんでした。なにに行こうか迷いましたが、Mercurialハンズオンに参加しました。
実は藤原さんがもういろいろ公開されているのですが、その時に取ったメモを公開します。
tortoisehg
TortoiseHG便利です。というか、HG Workbenchですね。UNIX/Windows/Macどれでも動きます。Workbench使い始めてから、Linuxでもはっきり言ってコマンドラインをほぼ使わなくなりました。(必要であればHG Workbenchからコマンドシェルを開けるのでそこで叩きます。)
社内でgitとmercurialどちらを導入すべきかという議論になった時に決め手になったのはこのHG Workbenchが素晴らしく、かつ、Windowsで問題なく動くから、ということと言っても過言ではありません。ぜひ一度使ってみるといいと思います。
というか、むしろ「HG Workbenchの使い方」という資料を作ったほうがMercurialを使う人が増えるかもしれませんね。
Mergeのdiffを見る
A -+---- A1 -- A2 -- M -- | | +- B1 -- B2 -------+ % hg update A2 % hg merge B2
というmergeをした時に綺麗にmergeされた場合、mergeコミット自体にはdiffが表示されない。
でも、ここでMで起きたmergeを知りたい場合、MとA2のdiffを取れば、それはすなわちBの変更全部とのdiffになる。
具体的にはこうやる感じ。
hg diff -r M^1 -r M
"^1" というのは第一親を指定する方式。Mの第一親はA2、第二親はB2となる。 仮に
hg diff -r M^2 -r M
とした場合、これはAのチェンジセット全体との差分となる。
qfinishした後にパッチに戻したい
hg qimport -r <リビジョン番号> 例: hg qimport -r 1:2 <-- 1と2がパッチに戻る
mqの更新を履歴管理する
qfoldとかするとパッチ領域(mq管理領域)自体を
- hg init --mq で履歴管理開始
- hg commit --mq でmq領域のcommit
- hg push --mq でmq領域をpush
- hg pull --mq でmq領域をpull
- hg log --mq でmq領域のlogを見れる
- hg qqueue で複数のmqも持てる
mqの履歴部分を消す場合は、 .hg/patches 自体を消す。やばいもうだめだと思ったら消してしまえばいい。
push/pullでmq領域自体をpush/pullできる。.hg/patches/.hg/hgrcを作って、mq領域の設定ができる。
mq領域は通常のレポジトリと完全に別なものとして意識することが重要。
作業領域の概念
commitしていない状態とは、「次にcommitするであろう候補」であると考えると分かりやすい。これを作業領域と呼ぶ。
agentのmoduleを書いてみる
Pandora FMSは監視対象のサーバにAgentを置くこともできます。このAgentはmoduleを実行して、その結果をServerに対して送ります。あ、なんせ使い出して日が浅いものでこの"module"という用語が適切かどうかはわかりませんが、まあ許してください。
このmoduleは普通のスクリプトです。標準出力に出した数字や文字列がそのままserverに送られます。
なお、スクリプトの内部にwarningやcritialの判定を入れる必要はありません。その判定はserver側で行います。
ウェブモニタリング
なんかウェブモニタリングはエンタープライズ版だけだそうなので、軽く書いてみました。
curlには --write-out という、かかった時間を出してくれるオプションがあります。
module_begin module_name http_time_total module_type generic_data module_exec curl --output /dev/null --write-out "%{time_total}" --silent http://somewhere.example.co.jp/path/ module_description time_total to somewhere in milli sec module_end
manをみれば、time_total以外にもいろいろありますので、必要に応じて変えるといいと思います。
PostgreSQLのアクセス統計
PostgreSQLは pg_stat_tables で各種統計情報が取れます。これを使ってPostgreSQLへのアクセスの統計を取ります。
pg_stat_user_tables から取れる情報は積算値なので、軽く適当なスクリプトを書いて単位時間(デフォルトでは300秒)あたりのアクセスにならします。
リモートのインスタンスから取る場合はpsqlの引数を適当に変えてください。
#!/usr/bin/env sh DB=database TABLE=target_table HISTFILE=/tmp/pandora_pg_idx_scan_count.tmp if [ ! -e $HISTFILE ] then echo "0" > $HISTFILE fi prev=`cat $HISTFILE` now=`psql -tA -c "select idx_scan from pg_stat_user_tables WHERE relname = '$TABLE';" $DB` value=`expr $now - $prev` if [ $value -le 0 ] then echo "0" else echo $value fi echo $now > $HISTFILE
pandora_agent.confにはこんな感じで書きます。
module_begin module_name scan_count module_type generic_data module_exec /path/to/pandora/etc/pandora/plugins/pg_scan_count.sh module_max 100 # ここは好きに変えてください module_min 0 module_description Table index scan count module_end
これは idx_scan の数ですが、pg_stat_user_tables にはcommit数とかロールバック数とかも格納されているので適宜変更するといいと思います。
なお、module_maxは最大値です。自動的に判定してくれるので指定する必要は本当はないのですが、いろいろ試してたら指定しないといけなくなったのでしています。なお、 module_postprocess という設定を入れると、数値を指定した倍率でかけてくれます。 1024 なら1024倍、0.000976563なら 1/1024 にしてくれますので、適宜調整するといいと思います。
tomcatのfree memoryを取る
tomcatのmanagerを使ってメモリの空き容量を%表示で出します。tomcatが出す情報はXMLかHTMLのようなので、shellで書くのはあきらめてpythonを使いました。minidomを使っているので、試してませんが python 2.4 でも動くはずです。
なお、当たり前ですが事前にmanagerの認証設定をしておく必要があります。
#!/usr/bin/env python # -*- coding: utf-8 -*- import urllib import urllib2 from xml.dom.minidom import parseString # for 2.4, use minidom URL = "http://localhost:8080/manager/status?XML=true" USER = "user" PASS = "password" def get_xml(url, user, passwd): passman = urllib2.HTTPPasswordMgrWithDefaultRealm() passman.add_password(None, url, user, passwd) auth_handler = urllib2.HTTPBasicAuthHandler(passman) opener = urllib2.build_opener(auth_handler) urllib2.install_opener(opener) f = urllib2.urlopen(url) xml = f.read() return xml def get_jvm_memory(dom): for n in dom.getElementsByTagName("memory"): return {"max": float(n.attributes["max"].value), "free": float(n.attributes["free"].value)} if __name__ == '__main__': xml = get_xml(URL, USER, PASS) dom = parseString(xml) mem = get_jvm_memory(dom) print((mem["free"] / mem["max"]) * 100 )
pandora_agent.confにはこんな感じで書きます。
module_begin module_name tomcat_free_mem module_type generic_data module_exec python /path/to/pandora/etc/pandora/plugins/tomcat_free_mem.py module_description Tomcat free memory in MB module_end
これだけです。
Agent側で動作
module_condition <評価式> <コマンド>
Pandora FMSのAgentは、module_conditionという値を設定することで、モジュールが特定の値を返す場合に指定したコマンドを実行できます。
- > [値]: モジュールの値が指定された値よりも大きい場合
- < [値]: モジュールの値が指定された値よりも小さい場合
- = [値]: モジュールの値が指定された値と同じ場合
- != [値]: モジュールの値が指定された値と異なる場合
- =~ [正規表現]: モジュールの値が指定された正規表現にマッチする場合
- (値, 値): モジュールの値が指定された値の範囲の場合
また、以下のように、同一のモジュールに複数の条件を設定することも可能です。
module_begin module_name condition_test module_type generic_data module_exec echo 2.5 module_condition (1, 3) script_1.sh module_condition > 5.5 script_2.sh module_end
module_conditionの例を示します。
重複プロセスがあったらkillする
module_begin module_name MyProcess module_type generic_data module_exec tasklist | grep MyProcess | wc -l module_condition > 2 taskkill /IM MyProcess* /F module_end
Logが大きくなったら削除する
module_begin module_name PandoraLogSize module_type generic_data module_exec ls -la "c:\Archivos de programa\pandora_agent\pandora_agent.log" | gawk "{ print $5 }" module_condition > 10000 del "c:\Archivos de programa\pandora_agent\pandora_agent.log" module_end
Spoolerが落ちてたら再起動する
module_begin module_name Service_Spooler module_type generic_proc module_service Spooler module_condition = 0 net start Spooler module_end
こんな感じで
なにか異常があったらメールでアラートを飛ばしつつ、自動的に出来る範囲であれば自力で復旧する、ということが簡単に書けるようです。