どうも、cloudpackかっぱ@inokara)です。

はじめに

最近、sensu 周りでやったことを書きます。

sensu-server で管理するクライアントの数が増えてくると純粋な sensu-server のログは目 grep では追いつかないくらいの量に増えてしまいます。このログを Elasticsearch に放り込んでみたりエラーレベルなログは Slack に通知する仕組みを fluentd を利用して作りました。

以下のようなイメージ。

Sensu-server構成図 (fluentd,Elasticsearchで可視化,Slackへ通知)

尚、今回は監視対象で発生する warning や critical なログをどうこうするというお話ではなく、あくまでも sensu-server の動作ログのお話となります。ただ、同じような仕組みで可視化や通知を行うことは可能かと考えています。

実装にあたりましては以下の書籍を参考にさせて頂きました。

fluentd に関する情報がギュッと詰まった一冊となっています!


何が便利になるのかしら?

そもそもエラーが起きないこと、設定ミスらないことが重要なのですが、sensu クライアントの設定で handler の定義に sensu-server 側に存在しない handler の設定を書いてしまうと…以下のようなエラーが sensu-server のログに記録されてしまいます。

{"timestamp":"2014-10-01T22:16:52.781338+0900","level":"error","message":"unknown handler","handler_name":"default"}

このまま放置しておいても sensu-server は動きますが、クライアントからのイベントのログと混ざってしまいイケてないと思いますしウザいですね。こんなログを早めに見つけて対処しましょうというのが今回の試みとなります。


実装と運用

概要

やっていることはシンプルです。

  1. sensu-server のログを fluent-plugin-elasticsearch を利用して Elasticsearch に突っ込む(可視化)
  2. 同時に fluent-plugin-notifierfluent-plugin-slack を利用して slack にポスト(通知)

以下、少し細かく見てみます。

sensu-server のログを Elasticsearch に突っ込む

そもそも sensu-server のログは JSON ですので以下のように in_tail プラグインの format json で tail して Elasticsearch に飛ばしてあげます。

<source>
  type tail
  format json
  path /var/log/sensu/sensu-server.log
  tag sensu.server.log
  pos_file /tmp/sensu.server.log.pos
</source>

細かい話ですが直接は Elasticsearch に飛ばさず、一旦、収集用の fleuntd に飛ばしてあげてから Elasticsearch に飛ばします。以下はログレベルが warn なログのみを抽出して表示させた状態です。

Sensu-serverログをElasticsearchで可視化 (収集用fluentdを介して不要なエラーを除外)

slack への通知

時代は ChatOps!! ということで、以下のような fluentd の設定を行い fluent-plugin-notifier でログを絞りこんで fluent-plugin-slack に飛ばしています。

<match sensu.server.log>
  type copy
  <store>
  (中略)
  </store>
  <store>
  (中略)
  </store>
  <store>
    type notifier
    default_tag_warn notifier.sensu-sevrer.warn.log
    default_tag_crit notifier.sensu-server.crit.log
    default_interval_1st 1
    <def>
      pattern sensu_error
      check string_find
      warn_regexp warn
      crit_regexp error
      target_key_pattern ^level$
    </def>
  </store>
</match>

<match notifier.sensu-sevrer.*.log>
  type copy
  <store>
    type stdout
  </store>
  <store>
    type buffered_slack
    api_key ${API_TOKEN}
    team ${TEAM_NAME}
    channel %23${SLACK_CHANNEL}
    username sensu-logging
    buffer_path /var/log/td-agent/buffer/slack
    flush_interval 60s
    color good
    icon_emoji :kappa:
    timezone Asia/Tokyo
  </store>
</match>

fluent-plugin-notifierfluent-plugin-slack のインストール方法については割愛しますが、連携の方法についてはちょっと工夫が必要でした。

特定の key パターンで通知するログを絞り込む

工夫という程ではなくデフォルトの機能ですが、fluent-plugin-notifier で record 内の特定の key パターンを利用したい場合には以下のように check string_find を利用します。

    <def>
      pattern sensu_error
      check string_find
      warn_regexp warn
      crit_regexp error
      target_key_pattern ^level$
    </def>

上記では正規表現を利用して level から始まる key を指定していて、以下のようなログにマッチすることになります。

{"timestamp":"2014-10-01T22:16:52.781338+0900","level":"warn","message":"unknown handler","handler_name":"default"}

又は

{"timestamp":"2014-10-01T22:16:52.781338+0900","level":"error","message":"unknown handler","handler_name":"default"}
slack への通知に詳細を載せたい

fluent-plugin-notifier から出力された通知は以下のように出力されますが、当初はこの内容を slack にも送れずに悩んでおりました…(通知だけは送れていました)

2014-10-04 05:37:12 +0900 notifier.sensu-sevrer.warn.log: {"pattern":"sensu_error","target_tag":"sensu.server.log","target_key":"level","check_type":"string_find","level":"warn","regexp":"/info/","value":"info","message_time":"2014-10-04 05:36:17 +0900"}

以下のように fluent-plugin-slack に手を入れさせて頂いて送れるようになりました。

--- out_buffered_slack.rb.bk    2014-10-04 05:53:35.993848035 +0900
+++ out_buffered_slack.rb       2014-10-04 05:53:40.945725720 +0900
@@ -21,6 +21,8 @@
       chunk.msgpack_each do |tag, time, record|
         messages[tag] = '' if messages[tag].nil?
         messages[tag] << "[#{Time.at(time).in_time_zone(@timezone).strftime("%H:%M:%S")}] #{record['message']}n"
+        messages[tag] << "#{record}n"
       end
       begin
         payload = {

fluent-plugin-slack は入ってくる recordmessage キーの値を詳細として slack に飛ばす実装になっているようですが、今回は message キーが入っていないので record そのままを出力させています。

ということで、以下はテスト的に info レベルのログを飛ばした結果です。

sensu-serverログをslackへ通知(収集用fluentdで不要なエラー除外)

fluent-plugin-notifier で絞り込む target_key_pattern を細かく指定してあげることで、各クライアントで発生している event の結果も slack に飛ばしてあげることが出来そうですね。

運用

実際の運用については…

  1. slack で通知を受け取る
  2. Elasticsearch でログの詳細を確認する
  3. 対応する

という流れです。

通知の部分が従来はメール通知だったりするわけですが、ちゃんと振り分けしていなければ見落としてしまったり、一部の担当者にしか届いていない等の運用上の問題とログを解析してメールで送信するという実装の手間を少しでも楽に出来るのではないかなと期待しています。

また、通知を受け取った後でサーバーにログインして目 grep してという作業を止めて Elasticsearch に置き換えることで該当のログを一瞬で確認することが出来るのもかなり嬉しいかなと思っています。さらに月末等の締めの時期に視覚的に確認しながらレビューすることが出来るも運用する上では嬉しいのではと考えています。


締め

実は

このネタは自分が設定ミスったのを気づかず放置してしまったことを発端に考えました。(本当にすいませんでした)

ミスはあきませんが誰にでもミスは付き物ですので、ミスっても「速く」「効率良く」「最小限の手間」でミスをリカバリ出来るような環境を作っていくことが個人的な課題の一つかなと考えていますし、sensu や fluentd や Elasticsearch 等のツールを組み合わせることでかなりお手軽に可視化や通知の環境が構築出来ることにかなり興奮して眠れません。

今後の課題

ということで、今後の課題としては…

  • Elasticsearch の運用(インデックスのメンテナンス等)をどうするか(←キメの問題だけかと)
  • 監視のイベント自体も同じ運用してみたい(特に通知)

こんな事考えてます。

お疲れ様でした。

元記事はこちらです。
sensu-server のログを fluentd を使って Elasticsearch で可視化したり Slack に通知したりする