読者です 読者をやめる 読者になる 読者になる

検証中のtd-agent(fluentd)の設定とか負荷とか

せっかくなのでアクセスログ関連のところだけ抜き出してみます。

構成

概要

Fluentdを使ってWEBサーバ(APPサーバ)のアクセスログを集約サーバに集約、いくつかの処理をやってます。
要するにこの3つです。

  • まとめてファイルに保存する(とりあえずやってみてるだけ)
  • TreasureDataプラットフォームにデータを送信して集計可能にする
  • Zabbixでサービスの稼働状況を可視化する

TreasureDataプラットフォームに関しては前の記事で書いたように、簡単な管理画面を作って集計テストをしています。自社フレームワーク用のライブラリも作成するつもりです。
Zabbixを使った可視化はこんな感じです。

プラグインの構成図

td-agentはサーバごとに1プロセス、できる限りシンプルでFluentdっぽい使い方を心がけてます。
https://cacoo.com/diagrams/zHAnCB2kKhvk9bPY-D6D85.png

負荷

2億/dayくらいのログを突っ込んでみたところ、集約サーバのCPU使用率はこんな感じでした。

4コアなのでusrが25%いったら限界、白いところはIO Waitです。
メモリは500Mくらい(OSこみで800M)しか食ってません。

サーバは4年くらい前ので、スペックしょぼめです。

  • CPU:Xeon E5430 2.66GHz 4コア
  • ディスク:SAS 15krpm、ライトキャッシュ無し

最近のサーバであれば、ローエンドマシンでも1プロセスで充分さばけそうです。

WEBサーバ側の負荷は特に気にならないレベルでした。


集約サーバは事前検証して「このくらいでいけそう」と選定したのですが、いろいろ機能追加してるうちにだんだん重くなっちゃいました。
負荷はけっきょく『何をするか(どのプラグインをどう使うか)』に強依存なので、事前に充分検証する+余裕をもったスケーリングをしましょう。



以下は設定についてです。

設定

WEBサーバ

複数サイト共有だったりするので、include用のディレクトリにサイトごとの設定をいれてます。

  • ファイル構成
|-- conf.d
|   |-- site1.conf
|   `-- site2.conf
|-- plugin
|-- prelink.conf.d
|   `-- td-agent.conf
`-- td-agent.conf
  • /etc/td-agent/td-agent.conf

ベース設定です

# サイトごとの設定をinclude
include conf.d/*.conf

# forward.**は集約サーバに転送
<match forward.**>
  type forward

  <server>
    host 192.168.1.101
    port 24224
    standby
  </server>

  <server>
    host 192.168.1.102
    port 24224
  </server>

  buffer_type file
  buffer_path /var/log/td-agent/buffer/forward

  flush_interval 60s
</match>

# Fluentdのログ
<match fluent.**>
  type file
  path /var/log/td-agent/fluent.log
</match>

# マッチしなかったログ
<match **>
  type file
  path /var/log/td-agent/no_match.log
</match>
  • /etc/td-agent/conf.d/site1.conf

サイトごとの設定です

<source>
  type tail
  format /^(?<host>[^ ]*) [^ ]* [^ ]* \[(?<time>[^\]]*)\] "(?<method>\S+) +(?<path>[^ ]+) +\S*" (?<code>[^ ]*) (?<size>[^ ]*) "(?<referer>[^\"]*)" "(?<agent>[^\"]*)" (?<restime>[^ ]*)/
  time_format %d/%b/%Y:%H:%M:%S %z
  path /var/log/httpd/site1-access_log.link
  tag forward.apache.access.site1
  pos_file /var/log/td-agent/site1-access_log.pos
</source>
集約サーバ
  • ファイル構成

こちらもinclude用のディレクトリににサイトごとの設定をいれてます。

/etc/td-agent
|-- conf.d
|   |-- site01.conf
|   `-- site02.conf
|-- filter
|   `-- access_log_filter.rb
|-- plugin
|-- prelink.conf.d
|   `-- td-agent.conf
`-- td-agent.conf
  • /etc/td-agent/td-agent.conf

ベース設定です

# WEBサーバからの転送を受け付ける
<source>
  type forward
  port 24224
  bind 0.0.0.0
</source>

# 各プロジェクト設定をinclude
include conf.d/*.conf

# Fluentdのログデータ
<match fluent.**>
  type file
  path /var/log/td-agent/fluent.log
</match>

# マッチしなかったデータ
<match **>
  type file
  path /var/log/td-agent/no_match.log
</match>
  • /etc/td-agent/conf.d/site1.conf

サイトごとの設定です。
とりあえずベタ書きですがサイトごとにほぼコピペなので、plugin-forestなどで集約するか、ChefやPuppetで管理するかしましょう。

# 1段目。フィルタリング & ファイル保存
<match forward.apache.access.site1>
  type copy

  # フィルタ処理。サイトによって違ったりする
  <store>
    type     exec_filter
    command  /etc/td-agent/filter/access_log_filter.rb
    in_keys  host,method,path,code,size,referer,agent,restime
    out_keys host,method,path,code,size,referer,agent,restime,uid
    tag      log.access.site1
  </store>

  # ファイル保存
  <store>
    type file_alternative
    path /var/log/fluent/access/site1/%Y/%m/access.site1.%Y%m%d%H
    flush_interval 180s
    compress gzip
    localtime
  </store>

</match>

# 2段目。TreasureDataに転送 & datacounterでカウント
<match log.access.site1>
  type copy

  # TreasureDataに転送
  <store>
    type        tdlog
    apikey      XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    buffer_type file
    buffer_path /var/log/td-agent/buffer/site1/td
    use_ssl     true
    auto_create_table
    flush_interval 300s
  </store>

  # レスポンスコードごとにカウント
  <store>
    type           datacounter
    count_key      code
    aggregate      all
    tag            count.access.site1.code
    count_interval 300

    pattern1 2xx ^2\d\d$
    pattern2 3xx ^3\d\d$
    pattern3 4xx ^4\d\d$
    pattern4 5xx ^5\d\d$
  </store>

  # レスポンスタイムごとにカウント
  <store>
    type           datacounter
    count_key      restime
    aggregate      all
    tag            count.access.site1.restime
    count_interval 300

    pattern1 0to100ms     ^\d{1,5}$
    pattern2 100to200ms   ^1\d{5}$
    pattern3 200to500ms   ^[2-4]\d{5}$
    pattern4 500to1000ms  ^[5-9]\d{5}$
    pattern5 1000to3000ms ^[0-2]\d{6}$
    pattern6 over3000ms   ^([3-9]\d{6}|\d{8,})$
  </store>

</match>

# 3段目。カウント結果をzabbixに転送
<match count.access.site1.*>
  type copy

 # 確認用にファイル保存
  <store>
    type file
    path /var/log/fluent/count.access.site1.log
  </store>

  # zabbixに転送
  # 2重登録になるのでスタンバイ側ではコメントアウト。対策検討
  <store>
    type             zabbix
    zabbix_server    192.168.1.100
    port             10051
    host             webservice_site1
    name_key_pattern (xx_count|ms_percentage|3000ms_count)$
  </store>
</match>
/etc/td-agent/filter/access_log_filter.rb

exec_filterで指定されているフィルタープログラムです。
フォワードされてきたログはまず、サービスごとに適切なフィルターを通します。
例えば○バゲーのアクセスログであればこういうやつになります。

  • 不要なログをフィルタする(この例だと静的ファイルを除外)
  • クエリストリングからユーザIDを取り出してuidカラムを作る
#!/usr/lib64/fluent/ruby/bin/ruby
path_filter = Regexp.new "^/(static|healthcheck)/"

while line = STDIN.gets
  line.chomp!
  host,method,path,code,size,referer,agent,restime = line.split("\t")

  next if path_filter =~ path

  uid = ""
  if /XXXXXXXXXXXX_id=(\d+)/ =~ path
    uid = $1
  end

  output = [host,method,path,code,size,referer,agent,restime,uid].join("\t")
  puts output
end

まとめ

というわけで検証中です。こうしたほうがいいよ意見募集中!


タグの設計が簡単そうに見えて意外と難しいです。使ってみていろいろ考えるしかないです。
負荷的には、中規模までのサービスであれば気にするほどのものじゃないと思います。
既存サービスへの影響は、td-agentはパッケージの依存関係がほぼないし、WEBサーバ側の負荷もたいしたことないし、で特にありません。
というわけで使うところがありそうなら気軽に導入してみましょう。