fluentd自身のログのカウントをdatadogに送る

ログを集めるのにfluentdをよく使っているのだが,fluentd自身も実はログを吐いており,それをfluentdでハンドリングできる.できるというか,何もケアしてやらないと

2020-06-17 03:41:01 +0000 [warn]: #0 no patterns matched tag="fluent.warn"

みたいなログが出ることになる.

もちろん受け取ったログのmatch節がなければこうなるのはわかるのだが,fluent自身のログでもこれがでるので,何かしらハンドリングしてやる必要がある.

ログの内容はS3とかに送ればいいとして,とりあえず件数だけモニタリングしてアラートを飛ばしたいなと思った.

Datadogに送りたい

アラートを飛ばすとなると,送る場所は結構限られてくる.幸い,DatadogはDogStatDというプロセスを持っており,こいつに適当なカスタムメトリクスを送りつければ,それをDatadog側でモニタリングできる.

というわけでfluentdからDogStatsDにメトリクスを送りつける方法を考える.

github.com

これを使う. ちょっと古いプラグインだがちゃんと動く.

# トップレベルにfilterを書くのは非推奨になっている
# fluent自身のログをハンドリングするには@FLUENT_LOGラベルを使う
# https://docs.fluentd.org/deployment/logging#capture-fluentd-logs
<label @FLUENT_LOG>
  <filter fluent.*>
    @type record_transformer
    <record>
      host "#{Socket.gethostname}"
      original_tag ${tag}
    </record>
  </filter>

  <match fluent.*>
    @type route
    add_tag_prefix fluentd

    <route **>
      copy
    </route>
    <route **>
      copy
      @label @dogstatsd
    </route>
  </match>

  <match fluentd.fluent.**>
    @type s3
    # ここはs3にログ本文を保存する処理でも書いておく
    # ...
  </match>
</label>

<label @dogstatsd>
  <match fluentd.fluent.info>
    @type record_reformer
    renew_record true
    tag dogstatsd.increment
    <record>
      @type increment
      key fluent.info
      host ${record["host"]}
      original_tag ${tag}
    </record>
  </match>
  <match fluentd.fluent.warn>
    @type record_reformer
    renew_record true
    tag dogstatsd.increment
    <record>
      @type increment
      key fluent.warn
      host ${record["host"]}
      original_tag ${tag}
    </record>
  </match>
  <match fluentd.fluent.error>
    @type record_reformer
    renew_record true
    tag dogstatsd.increment
    <record>
      @type increment
      key fluent.error
      host ${record["host"]}
      original_tag ${tag}
    </record>
  </match>

  <match dogstatsd.increment>
    @type dogstatsd
    host "#{ENV['DOGSTATSD_HOST']}"
    port "#{ENV['DOGSTATSD_PORT']}"
    use_tag_as_key false
    flat_tags true
    metric_type increment
    value_key Value
  </match>
</label>

こんな感じの設定でいける.

本文はS3に送る前提だったので,routeでcopyしている.

DogStatsDはあくまで,何かしらの件数や数,値を送るものなのでログ本文を送れるわけではない.なので,record_reformerを使ってrecordの数を数えて,それをmetric_type increment でDogStatsDに送っている.

これで無事にDataDog側でfluent.info, fluent.warn, fluent.error というカスタムメトリクスが生成されているので,これを元にDashboardを作ったりアラートを作ったりすれば良い.

DogStatsDはどうやって用意したらいい?

先の設定で,さらっと

  • DOGSTATSD_HOST
  • DOGSTATSD_PORT

という環境変数を使って,送信先を指定していたが,DogStatsDは我々がどこかしらにホストして,そこにメトリクスを送りつけてやる必要がある.

これについては,以前の記事で書いているが,

h3poteto.hatenablog.com

Kubernetesクラスタであるなら,helmで入れたDatadogのDaemonSetにDogStatsDが同居しているので,そこに送りつければ良い.

DownwardAPIを使えば,自身のホスト上にいるDaemonSetと通信することができる.

releases:
  - name: datadog
    namespace: kube-system
    chart: stable/datadog
    values:
      - agents:
          useHostNetwork: true
      - datadog:
          apiKey: hogehoge
          appKey: fugafuga
          dogstatsd:
            nonLocalTraffic: true

こんなhelmfileを書いておいて,

containers:
  - name: fluentd
    image: fluent/fluentd:latest
    env:
      - name: DOGSTATSD_HOST
        valueFrom:
          fieldRef:
            fieldPath: status.hostIP
      - name: DOGSTATSD_PORT
        value: "8125"

fluentdはこういう定義にしておけば疎通できる.

ポート番号8125 はDogStatsDのポート番号としてデフォルトで定義されているので,変更したい場合はchartのvaluesを変えれば良い.

github.com

本当はDatadogLogsを使うのが良い

今回試していないのだが,本来であれば,そもそもfluentdのログは全部DatadogLogsに送ってしまうと楽だ.Logsを使うのであれば,公式で用意されている

github.com

を使うと良い.

これであれば,今回みたいにfluentd側でカウントしたりする必要はなく,DatadogLogs側で受け取ったログをフィルタリングして,カスタムメトリクスを生成できる.

Logsにお金を払えるのであれば,こちらを選択したほうが楽になると思う.

そもそもLog本文もDatadogLogsで見られるようになるしね.