fluentdでつくる監視系

  • 265
    いいね
  • 3
    コメント
この記事は最終更新日から1年以上が経過しています。

いつもアプリケーションの開発ばかりで、まじめに監視系を考えたことがなかったので、
fluentdを中心にした監視系を作ってみた。

ツッコミ大歓迎です。アチラコチラの資料を参考にしつつ作ったので、オカシイところがあれば是非ともコメント下さいませ。

前提

  1. 複数台のアプリケーションサーバ
  2. 一台のログ収集サーバ
  3. ログにはエラーログとアクセスログの大きく2種類を用意する
  4. エラーログは更に複数のレベルでファイル単位にわかれている
    1. fatal
    2. error
    3. warn
  5. アプリケーションサーバとログ収集サーバは同一ネットワーク上にある

やりたいこと

メールで来ても絶対に気がつかない自信がある。
異常の側から教えてくれる仕組みを目指す。

  1. fatalログが出た場合は、電話による通知を行う
  2. 全てのエラーログはchatツールに出力する
  3. ログのバックアップ
  4. ログの分析・可視化

この記事では1, 2, 3についてまとめる。

構築

fluentdのインストール

公式のドキュメントが一番わかり易い。
Installation | Fluentd

ログの送信側

既にログをJSONでファイルに吐き出していたのでtailする。

サンプルログ。仮にこういうログだったとする。

{"name":"applicationX","hostname":"localhost","req":{"method":"GET","url":"/","ua":"Mozilla/5.0 ... "},"res":{"statusCode":200},"error":"", "time":"2015-02-26T08:23:28.998Z"}

/etc/td-agent/td-agent.conf

<source>
  type tail
  path <PATH/TO/LOG>
  pos_file <PATH/TO/POSFILE>
  tag app.access
  format json
  include_time_key true
  include_tag_key true
  time_key sacrifice            # avoid overwrite <time> field
</source>

<source>
  type tail
  path <PATH/TO/LOG>
  pos_file <PATH/TO/POSFILE>
  tag app.fatal
  format json
  include_time_key true
  include_tag_key true
  time_key sacrifice            # avoid overwrite <time> field
</source>

# 以下、error, warn, debugそれぞれ<source> ... </source>

<match *.**>
  type forward
  heartbeat_type tcp
  <server>
    host <YOUR LOG SERVER>
  </server>
</match>

これで

2015-02-26 17:23:28 +0900 app.access: {"name":"applicationX","hostname":"localhost","req":{"method":"GET","url":"/","ua":"Mozilla/5.0 ... "},"res":{"statusCode":200},"time":"2015-02-26T08:23:28.998Z"}
# 構造:time tag record

という内容のログが送信される

time_key

ログにtimeという要素があるので、これでアクセス時刻別に処理しようと思っていたが、
fluentdを通した時点でなくなっていた。なので、time_keyで存在しない要素を指定して要素の削除を回避している。

heartbeat_type

デフォルトではUDPの24224を使って死活管理をしている。
udbポートを開けたくなかったので、ハートビートもtcpで行うように変更。

tagについて

fluentdはピリオドで区切ると扱うのが楽なのでピリオド区切り。

ログの受信側

$ sudo /opt/td-agent/embedded/bin/fluent-gem install fluent-plugin-forest  # td-agent.confを構造化して書くため
$ sudo /opt/td-agent/embedded/bin/fluent-gem install fluent-plugin-twilio  # 電話
$ sudo /opt/td-agent/embedded/bin/fluent-gem install fluent-plugin-hipchat # chat通知
$ sudo /opt/td-agent/embedded/bin/fluent-gem install fluent-plugin-s3      # S3にバックアップ

/etc/td-agent/td-agent.conf

<source>
  type forward
  port 24224
</source>

<match app.access>
  type copy
  <store>
    type stdout # debug用 /var/log/td-agent/td-agent.logに流れる
  </store>
  <store>
    type s3
    aws_key_id "#{ENV['AWS_ACCESS_KEY_ID']}"
    aws_sec_key "#{ENV['AWS_SECRET_KEY']}"
    s3_bucket <AWS S3 BUCKET NAME>
    s3_region ap-northeast-1
    path app/access/
    buffer_path /var/log/td-agent/buffer/s3_app_access
    time_slice_format %Y-%m-%d/%H
    time_slice_wait 10m
  </store>
</match>

<match app.*>
  type forest
  subtype copy
  <template>
    <store>
      type s3
      aws_key_id "#{ENV['AWS_ACCESS_KEY_ID']}"
      aws_sec_key "#{ENV['AWS_SECRET_KEY']}"
      s3_bucket <AWS S3 BUCKET NAME>
      s3_region ap-northeast-1
      path ${tag_parts[0]}/${tag_parts[1]}/ # app/fatal/, app/error/, ...
      buffer_path /var/log/td-agent/buffer/s3_${tag_parts[0]}_${tag_parts[1]}
      time_slice_format %Y-%m-%d/%H
      time_slice_wait 10m
    </store>
    <store>
      type hipchat
      api_token "#{ENV['HIPCHAT_API_TOKEN']}"
      default_room <YOUR CHAT ROOM>
      default_from fluentd
      default_color yellow
      default_notify 1
      default_format text  # メンションを含めたい場合はtextである必要がある。HipChatのAPI仕様
      default_timeout 3
      key_name error
    </store>
  </template>
  <case app.fatal>
    <store>
     type twilio
     account_sid    "#{ENV['TWILIO_ACCOUNT_SID']}"
     auth_token     "#{ENV['TWILIO_AUTH_TOKEN']}"
     from_number    +810000000000                # twilioで取得した番号
     default_number +811111111111, +812222222222 # 電話を受け取りたい番号
    </store>
  </case>
</match>

アクセスログとエラーログでmatchを分けたがまとめられるような気もする・・・。
S3の記述が重複しておりもやもや。

=> 続・fluentdでつくる監視系 - Qiitaで対応

AWSのアクセストークン

S3のフルパーミッションが付与されているトークン。
EC2インスタンスかつIAM Roleが付与されている場合は省略可

hipchatのkey_name

ここで指定した要素をJSONから取り出して通知する。
なお、渡せるのは文字列のみ。構造を持った要素(例えば、res)を渡すと無視される。

エラーの詳細を全部流したいので、対策を考え中。
続・fluentdでつくる監視系 - Qiitaで解決

twilio

messageという要素があると、読み上げてくれる。
今はmessageという要素が無いため電話をとった瞬間に切れる。
続・fluentdでつくる監視系 - Qiitaで解決

その他

  • 同一ネットワーク上にあるのでforwardで転送したが、間にインターネットを挟む場合はsecure_forwardがいいと思う。
  • アプリケーションログ以外、サーバのシステム監視はまた今度
  • fluentd側でlog_levelを設定したらもっとスッキリかけるかもしれない

まとめ

最低限、fatalは電話、エラー系のログはchatにポスト、ログはバックアップ、ができた。

参考

fluentdの簡単な使い方、設定方法一覧 - Hive Color
fluentd で json に time を残したい - Qiita
日本語読み上げ対応。FluentdからTwilioの電話APIを操作する「fluent-plugin-twilio」を使って電話を掛けてみた - Y-Ken Studio
fluent-plugin-s3
fluent-plugin-hipchat
fluent-plugin-forest