fluentdのプラグインとかユースケースの話

この記事は「ウィークリー Fluentd ユースケースエントリリレー」の一部です。カジュアルに fluentd のプラグイン書いてみた話とリアルタイム監視のよくあるパターンを書いてみます。

groupcounter

モヒカン族で有名な tagomoris さんが書かれたdatacounterというプラグインをパクって書きました。ある key に対する value を一定期間カウントしたい時に使います。ユースケースとしては、access log から src IP ごとの件数を毎分数えて、グラフにすると、どの IP からのリクエストがどういう推移をしてるかが観測できます。

config はこんな感じ。

<match apache.access>
  type groupcounter
  count_interval 5s
  aggregate tag
  output_per_tag true
  tag_prefix groupcounter
  group_by_keys code,method,path
</match>

なんで作ったかいうと、datacounter は予め決めたいくつかのパターンにマッチするものを数えるものなので、IP アドレスのように種類が不定だと使えません。ちなみに、数値に対してレンジごとにカウントするならnumeric-counterがよいと思います。

hash-forward

ちょっと特殊な forward プラグインです。通常 forward は server に並べたやつのどれかに適当にラウンドロビンで送りますが、たまに同じタグは同じ fluentd に必ず送って欲しい場面があります。

例えば先に書いたカウント系の plugin を使って出力元のホスト毎にカウントしつつ、スケールアウトしたいなら、あるホスト A から出たログは必ず一つの fluentd に全て送られる必要があります。こういう時に、ホスト名をタグの suffix につけて hash-forward すると、予め決めた何台かの fluentd に、タグ名のハッシュを使ってほぼ均等に分散してくれます。

ちなみにハッシュアルゴリズムには murmurhash を使ってます。適当にベンチしてみたら最も均等にばらけました。まぁベンチがおかしい可能性も多いにありますが。。。

ちなみに自分で書いといて hash-forward はあんま使い道ないかもと思ってます。ホスト側は一台の fluentd にしか送らないようにして、そこでカウントもしちゃえばいいかなと。

よくありそうな config のパターン

出力元でタグの suffix にホスト名をつけてあげるとして(fluent-agent-lite なら数行のパッチでいけます)、こんな感じの定義がログの種類毎に並ぶのかなという例。酔っぱらいながら書いたので間違ってたら直しますので言ってくだしあ!

<match raw.game1.accesslog.**> # 生データをパースして構造化
  type parser
  remove_prefix raw
  key_name message
</match>

<match game1.accesslog.**>
  type copy
  <store>
    type groupcounter # srcipでグループカウント
    group_by_keys srcip
    count_interval 60s
    aggregate tag
    output_per_tag yes
    tag_prefix srciptcount
  </store>
</match>

<match srcipcount.game1.accesslog.**>
  type copy
  <store>
    type notifier # グループカウントの結果に対して閾値超えたら通知
    default_tag_warn warn.srcipcount.game1.accesslog
    default_tag_crit crit.srcipcount.game1.accesslog
    input_tag_remove_prefix srcipcount.game1.accesslog
    <def>
      check numeric_upward
      warn_threshold 10
      crit_threshold 20
      target_key_pattern ^.+_count$
      pattern srcipcount.game1.accesslog
    </def>
  </store>

  <store>
    type growthforecast # グループカウントの結果をgrowthforecastへ
    remove_prefix srcipcount.game1.accesslog
    gfapi_url http://localhost:5000
    service srcipcount.game1.accesslog
    tag_for section
    name_key_pattern .+
  </store>

  <store>
    type mongo # グループカウントの結果をmongodbへ
    host localhost
    flush_interval 1s
    remove_tag_prefix srcipcount.game1.accesslog.
    database srcipcount_game1_accesslog
    tag_mapped
    capped
    capped_size 100m
  </store>
</match>

<match warn.srcipcount.game1.accesslog> # warnの通知先
  type copy
  <store>
    type ikachan
    host localhost
    port 4979
    channel game1-warn
    message [%s][%s]%s: %s
    out_keys pattern,target_tag,target_key,value
  </store>

  <store>
    type mail
    host localhost
    from warn@example.com
    to warn@example.com
    subject [%s][%s]
    subject_out_keys pattern,target_tag
    out_keys pattern,message_time,target_tag,target_key,value
  </store>
</match>

<match crit.srcipcount.game1.accesslog> # critの通知先
  type copy
  <store>
    type ikachan
    host localhost
    port 4979
    channel game1-crit
    message [%s][%s]%s: %s
    out_keys pattern,target_tag,target_key,value
  </store>

  <store>
    type mail
    host localhost
    from crit@example.com
    to crit@example.com
    subject [%s][%s]
    subject_out_keys pattern,target_tag
    out_keys pattern,message_time,target_tag,target_key,value
  </store>
</match>

というわけで、みなさん fluentd を楽しみましょう!