CUEBiC TEC BLOG

キュービックTECチームの技術ネタを投稿しております。

Datadogに送るログをフィルタリングする

背景

こんにちは、キュービックでSREをやっているYuhta28です。キュービック内のテック技術について発信します。
Datadogに移行してから数ヶ月が経ちました。メディアサーバーへのエージェント導入や外形監視設定など一通り完了し今の所運用できていますが、しばらくして課題が出てきました。

ログストレージコスト増加

Datadogのログ料金はログの保持期間、量によって変わる従量課金制1です。弊社は15日間の保持期間で契約していて、それより古いログはAmazon S3アーカイブされる運用にしています。

アーキテクチャ

アーキテクチャ

以前の記事でも書いたとおりDatadogへのログ転送はOSSのFluentd2を採用しており、対象ログの中身すべてをDatadogに転送する設定にしています。
結果としてDatadogが取り込むログの量が膨大に膨れ上がれ、ログ周りのコストが高くなってしまいました。なのでログコスト削減を目的にDatadogで収集するログの中身をフィルタリングすることにしました。

Fluentdフィルタリング

Datadogへのログの転送に使用しているFluentdには、不要なログの除外や必要なログのみの抽出が可能なfilterプラグイン3があります。LTSV形式のログならキーを指定することでそのキー内の特定の条件にマッチしたログの抽出もしくは除外設定が可能となります。
例えば今回ようにELBからのヘルスチェックのログと画像ファイルやcssファイルなどの静的コンテンツファイルを除外したい場合は以下のように記述しました。

<filter nginx>
  @type grep
  <or>
    <exclude>
      key path
      pattern /\.(jpg|jpeg|gif|png|css|js|swf|ico|pdf|svg|eot|ttf|woff)/
    </exclude>
    <exclude>
      key agent
      pattern ^.*ELB-HealthChecker\/2\.0$
    </exclude>
  </or>
</filter>

この設定を反映した結果がこちらです。

メディアアクセスログ
11:30を境にアクセス量が半分近くまで減らせることができました。これで当初のやりたかったことは達成できましたが一つ課題が出てきました。

生ログの保存

画像が表示されていないなど画像起因のサイト表示エラーが起きた場合に画像ファイルのログがないと調査が困難になりますので、生ログは別に保存しておきたいという要望がありました。そこでDatadogのアーカイブ保存用S3とは別にFluentdから直接生ログを転送するためのS3を用意して保存することにしました。

生ログ保存用S3を追加したアーキテクチャ

複数保存先のへの転送失敗

最初複数の転送先へログを送るときはこう書きました。

<match nginx>
  @type s3
  s3_bucket cuebic-mediaXXXXXXXXXXXXX
  s3_region ap-northeast-1
  path media/nginx/
  time_slice_format year=%Y/month=%m/day=%d/hour=%-H/
  s3_object_key_format %{path}%{time_slice}%{uuid_flush}.json.%{file_extension}
  <format>
    @type json
  </format>
</match>

<filter nginx>
  @type grep
  <or>
    <exclude>
      key path
      pattern /\.(jpg|jpeg|gif|png|css|js|swf|ico|pdf|svg|eot|ttf|woff)/
    </exclude>
    <exclude>
      key agent
      pattern ^.*ELB-HealthChecker\/2\.0$
    </exclude>
  </or>
</filter>
<match nginx>
  @type copy
  <store>
    @type datadog
    @id nginx-access
    api_key "#{ENV['ddapikey']}"

    include_tag_key true
    tag_key @log_name

    dd_source 'nginx'
    service   'access-log'
  </store>
</match>

Fluentdに詳しい人ならピンと来ると思いますが、matchに一致したイベントは次のmatchセクションを参照しません。つまりnginxイベントは最初のmatchセクションに拾われS3へログは送られますが、次のmatchセクションで定義しているDatadogへのログ転送が反映されなくなります。 解決策としてcopyプラグイン4を使って2つのmatch内に含まれているログ転送情報をまとめましたが、copyプラグイン内でfilterプラグインは使えないらしくFluentdの再起動時に警告が出ました。
どうしようかと悩みましたが、詳しい人からrelabelプラグインを使えば解決するというアドバイスをいただいたことで解決まで持っていくことができました。

relabelプラグインの使用

docs.fluentd.org

relabelプラグインは一致したイベントに再度ラベリングをつけてくれるプラグインです。まずnginxイベント内にS3へ送るラベルとDatadogへ送るラベルを付与します。

<match nginx>
  @type copy
  <store>
    @type relabel
    @label @s3lognginx
  </store>
  <store>
    @type relabel
    @label @filternginx
  </store>
</match>

その後各ラベルがついたログイベントに対して生ログのままS3へ送る処理とフィルタリングした状態でDatadogへ送る処理を定義しました。

<label @s3lognginx>
  <match nginx>
    @type s3
    s3_bucket cuebic-mediaXXXXXXXXXXXXX
    s3_region ap-northeast-1
    path media/nginx/year=%Y/month=%m/day=%d/hour=%H/
    s3_object_key_format %{path}%{time_slice}%{uuid_flush}.json.%{file_extension}
    <format>
      @type json
    </format>
    <buffer tag,time>
      timekey 1m
    </buffer>
  </match>
</label>

<label @filternginx>
  <filter nginx>
    @type grep
    <or>
      <exclude>
        key path
        pattern /\.(jpg|jpeg|gif|png|css|js|swf|ico|pdf|svg|eot|ttf|woff)/
      </exclude>
      <exclude>
        key agent
        pattern ^.*ELB-HealthChecker\/2\.0$
      </exclude>
    </or>
  </filter>
  <match nginx>
    @type datadog
    @id nginx-access
    api_key "#{ENV['ddapikey']}"
    include_tag_key true
    tag_key @log_name
    dd_source 'nginx'
    service   'access-log'
  </match>
</label>

これによりS3には完全な生ログが保存され、Datadogにはフィルタリングされた状態のログが転送されるようになりました。

所感

Datadogへ送るログのフィルタリング方法について紹介しました。単純にフィルタリングしたログをDatadogへ送るだけでしたがそんなに難しくありませんでしたが、複数の転送先にログを送るときにcopyプラグインが使えなくてどうすればいいのか解決に苦労しました。
このことをTwitterでつぶやくと親切な人からrelabelを使った解決方法について教えていただきました。
解決できましたので私もこの記事を通して同じように悩んでいる人への手助けになれればと思います。