せっかくなのでアクセスログ関連のところだけ抜き出してみます。
構成
概要
Fluentdを使ってWEBサーバ(APPサーバ)のアクセスログを集約サーバに集約、いくつかの処理をやってます。
要するにこの3つです。
- まとめてファイルに保存する(とりあえずやってみてるだけ)
- TreasureDataプラットフォームにデータを送信して集計可能にする
- Zabbixでサービスの稼働状況を可視化する
TreasureDataプラットフォームに関しては前の記事で書いたように、簡単な管理画面を作って集計テストをしています。自社フレームワーク用のライブラリも作成するつもりです。
Zabbixを使った可視化はこんな感じです。

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

4コアなのでusrが25%いったら限界、白いところはIO Waitです。
メモリは500Mくらい(OSこみで800M)しか食ってません。
サーバは4年くらい前ので、スペックしょぼめです。
最近のサーバであれば、ローエンドマシンでも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サーバ側の負荷もたいしたことないし、で特にありません。
というわけで使うところがありそうなら気軽に導入してみましょう。
