へろへろもへじ

(ブログタイトル募集中)

Fluentdを使ってアプリ側で吐いたログをAmazonRedshift上のテーブルに登録する

2014年、明けましたおめでとうございます。
年末年始にfluentdに関してちょこちょこ試作しておりまして、TODOはまだあるものの、ある程度まとまったので書き留めておきます。

目的

Webサーバで吐かれたログ(複数ファイル)をRedshift上のテーブルに登録する
(なるべくスマートに...

要件(希望含む)

  1. Webサーバには負荷をかけたくないので、最低限の仕事のみさせる構成としたい
  2. 取り込み対象のログファイルが増えた場合、設定ファイルを極力いじらない構成としたい
  3. ログファイルの出力形式はアプリ側で変えない
  4. 高可用性、負荷分散を容易にできる構成としたい
  5. Redshiftになるべく簡単に連携...

※1,2,4はあたり前田の(ry...ですね

解決案

1.Webサーバには負荷をかけたくないので、最低限の仕事のみさせる構成としたい

  • Webサーバにfluentd(td-agent)を立て、生ログをフォワードすることだけに徹し、フォワード先のサーバを別立てする。
  • その他の作業(生ログ分解、Redshiftに取り込み)はフォワード先のサーバにて実施。

2.取り込み対象のログファイルが増えた場合、設定ファイルを極力いじらない構成としたい

  • フォワード対象とするファイルに接頭辞を付けるルールとし、@yosisaさん作成のtail_exという素敵プラグインを利用すればいけそう。->いけた

3.ログファイルの出力形式はアプリ側で変えない

json-nest2flatに関する過去エントリーはこちら↓
【Fluentd】ネストされたJSONをフラットにするFluentdのプラグイン(fluent-plugin-json-nest2flat)を公開しました - f.retu.TechLog

4.高可用性、負荷分散を容易にできる構成としたい

  • out_fowardプラグインで複数サーバの指定ができるようなので、フォワード先のサーバを同じ設定で複数台立てればそれでいけそう。->いけた
  • BufferedOutputで転送に失敗しても再送する仕組みを利用する

5.Redshiftになるべく簡単に連携…

  • ハピルス社の方々によって作られたらしい素敵なredshiftプラグインでいけそう -> いけた

サーバ構成

cacoo(Cacoo - Web上でフローチャートやUMLなど様々な図を作成)を使って追々追記します。。。

コンフィグ

td-agent.conf(送信側)

<source>
  type tail_ex
  path /var/log/app/*.log
  tag tail_ex.*
  format none
  pos_file /var/tmp/fluentd.pos
  refresh_interval 300
</source>

<match tail_ex.var.log.**>
  type forward
  send_timeout 60s
  recover_wait 10s
  heartbeat_interval 1s
  phi_threshold 8

  buffer_type file
  buffer_path /var/log/td-agent/myapp.*.buffer
  flush_interval 10s

  <server>
    name server1
    host server1_host
    port 24224
    weight 50
  </server>
  <server>
    name server2
    host server2_host
    port 24224
    weight 50
  </server>
</match>

td-agent.conf(受信側)

## in_forward
<source>
  type forward
  port 24224
  bind 0.0.0.0
</source>

## parser
include conf.d/parser.conf

## json_nest2flat
include conf.d/json_nest2flat.conf  

## jsonbucket
include conf.d/jsonbucket.conf

## redshift
include conf.d/redshift.conf

conf.d/parser.conf(受信側)

<match tail_ex.var.log.app.*.log>
  type forest
  subtype parser 
  <template>
    key_name message
  </template>
  <case **test.log>
    add_prefix to_jsonbucket
    format /(?<column1>[^ ]*),(?<column2>[^ ]*),(?<column3>\d{4}-\d{2}-\d{2} (\d{2}:){2}\d{2}),(?<column4>[^ ]*),(?<column5>[^ ]*)$/
  </case>
  <case **test2.log>
    add_prefix to_json_nest2flat
    format /(?<name>[^ ]*),(?<age>[^ ]*),(?<birth_day>\d{4}-\d{2}-\d{2} (\d{2}:){2}\d{2}),(?<data>.*)$/
  </case>
</match>

conf.d/json_nest2flat.conf(受信側)

<match to_json_nest2flat.**>
  type forest
  subtype json_nest2flat

  <template>
    add_tag_prefix to_jsonbucket
  </template>
  <case **test2.log>
    json_keys data
  </case>
</match>

conf.d/jsonbucket.conf(受信側)

<match to_jsonbucket.**>
  type forest
  subtype jsonbucket

  <template>
      output_tag to_redshift.__TAG__
      json_key log
  </template>
</match>

conf.d/redshift.conf(受信側)

<match to_redshift.**>
  type forest
  subtype redshift 
  <template>
    ## s3
    aws_key_id your_aws_key_id
    aws_sec_key your_aws_sec_key
    s3_endpoint your_s3_endpoint
    s3_bucket your_s3_bucket
    timestamp_key_format year=%Y/month=%m/day=%d/hour=%H/%Y%m%d-%H%M
    ## redshift
    redshift_host your_redshift_host
    redshift_port 5439
    redshift_dbname hoge
    redshift_user your_redshift_user
    redshift_password your_redshift_password
    redshift_schemaname your_redshift_password # publicの場合、省略可
    file_type json
    ## buffer
    buffer_type file
    flush_interval 5m
    buffer_chunk_limit 1g
  </template>
  <case **test.log>
    ## s3
    path your_path/
    ## redshift
    redshift_tablename test
    ## buffer
    buffer_path your_buffer_path
  </case>
  <case **test2.log>
    ## s3
    path your_path/
    ## redshift
    redshift_tablename person
    ## buffer
    buffer_path your_buffer_path
  </case>
</match>

GitHubにアップしました。送信側はsender、受信側はreceiverディレクトリを見て頂ければ。
fukuiretu/fluentd-applog-to-redshift-sample · GitHub

上記設定をした場合のデータフロー

test2.logを例として...

  • ログファイルに以下のログを追記(送信側サーバにて)
$echo 'hoge,17,2014-01-01 01:12:34,{"t":"177","favorite_song”:"pops"}' >> /var/log/app/test2.log

  • tail_ex(in_tail)のformat:noneで、以下の形式となる(送信側サーバにて)
{“message”:”hoge,17,2014-01-01 01:12:34,{"t":"177","favorite_song”:"pops"}”}

  • parserで、以下の形式となる(受信側サーバにて)
“name”:”hoge”,”age”:”17”,”birth_day”:”2014-01-01 01:12:34”,”data”:”{"t":"177","favorite_song”:"pops"}”}

  • json_nest2flatで、以下の形式となる(受信側サーバにて)
{“name”:”hoge”,”age”:”17”,”birth_day”:”2014-01-01 01:12:34”,"t":"177","favorite_song”:"pops"} 

  • jsonbucketで、以下の形式となる(受信側サーバにて)
{“log”:"{“name”:”hoge”,”age”:”17”,”birth_day”:”2014-01-01 01:12:34”,"t":"177","favorite_song”:"pops"} “}

  • Redshift上のテーブルに以下のデータが登録される
hoge=# select * from person;
name | age |     birth_day      | tall | favorite_song
------+-----+---------------------+------+---------------
hoge |  17 | 2014-01-01 01:12:34 |  177 | pops
(1 row)

TODO

  • 例外系の動作検証
  • 監視(リソース状況や転送エラー等をキャッチアップできる仕組み)
  • 各設定値の調整(flush_intervalやbuffer_chunk_limit等)
  • 受信サーバのIPが変わった場合(AWSで運用する場合等)の考慮

所感など

  • fluentd素敵、楽しい!!
  • 使わせて頂いているプラグインのAuthorの方々に感謝!!
  • redshiftプラグインpostgresqlパッケージがインストールされていない(psqlコマンドが使えない)と使えない
  • TODOもいろいろ頑張る!!
  • Rubyの勉強も頑張る!!
  • 次回の『Fluentd Casual Talks』は絶対参加したい!!(#3に仕事で参加できず。。。)
  • ご意見色々頂けると幸いですm(__)m