新旧色々な情報が入り乱れていて、なかなかうまく動かなかったので、動いたものをメモっておきます。ちなみにEC2のAmazon Linuxです。fluentdは0.12.26です。
fluentdのはなし
ログにホスト名を入れる方法
まず、ホスト名を入れる方法で悩みました。config expanderを使えとか、record transformerを使えとか、色々見つかったのですが、ことごとく動きませんでした。正確には動かせませんでした・・・そもそも{}で変数囲っても置換されません・・・
で、結局どうしたかっていうと、環境変数を読み込みました。
time_slice_format "#{ENV['HOSTNAME']}/%Y/%Y%m/%Y%m%d/%H"
これでS3に上がるときにホスト名が入ります。EC2のインスタンスはELBにぶら下がっているので、ホスト名いれとかないとめんどくさいことになります。
追記
とやっていたのですが、これ、OSを再起動するとだめなんですよね。なのでインスタンス増やしたときやAuto Scalingでもダメです。起動時のserviceコマンドは環境変数をリセットするようになっているので、HOSTNAMEは引き継がれません。なので、こう変えて動くようになりました。
time_slice_format "#{`hostname`.chop}/%Y/%Y%m/%Y%m%d/%H"
時間を消さない方法
nginxでLTSVを選んで、fluentdでJSONに変換しているのですが、これお馴染みの問題らしいのですが、JSONになったときにタイムスタンプが消されます。消されるというか、外に出されます。最終的にRedshiftに持っていく予定でしたので、JSONのデータの中にタイムスタンプを保持する方法を探しました。これはGoogle先生に聞けばすぐ見つかりました。これです。
format_json true
include_time_key true
きれいなJSONにする方法
デフォルトの設定だと、S3に上げることはできても、そのままRedshiftで読み込みできません。なので、JSONをきれいにしておきます。それがこれ。
output_tag false
output_time false
これで先頭にある余計なタイムスタンプとタグが消えてくれます。
タイムスタンプはJSTに
ネットでサンプル拾ってくると、だいたいUTCになっています。解析の時に面倒なので、JSTに統一する場合は、コンフィグファイルのutcと書いてある行を削除しましょう。
S3に上げる方法
去年くらいに仕様変わりました。
s3_endpoint⇒s3_regionになっています。
ログの分割ルール
time_slice_format、time_slice_wait、flush_intervalと良く分からなかったんですけど、試行錯誤でなんとなく分かってきました。
time_slice_format "#{ENV['HOSTNAME']}/%Y/%Y%m/%Y%m%d/%H"
time_slice_wait 10m
この場合、S3には1時間に1回ファイルがアップされます。アップのタイミングはその時間の末に行われるので、最悪59分59秒待たないとS3に上がらないです。time_slice_waitの10分ってのは、10分間はログの到着を待つってことらしいです。例えば、nginxがめっちゃ重くて、12時59分59秒99のログの到着が30秒遅れた場合、これを指定しておかないと12時台のログファイルはCLOSEされてしまうので、ログが抜けてしまう、って感じ?? 試すの面倒なので正直良く分かりません・・・
time_slice_format "#{ENV['HOSTNAME']}/%Y/%Y%m/%Y%m%d/%H"
time_slice_wait 10m
flush_interval 1m
こうするとS3へのアップは1分毎になり、1時間で最大60ファイルがアップされます。ファイル名の末尾に_1から_60まで通し番号が振られます。.gzの手前ですね。こうすることで、仮にインスタンスが落ちても、消失するログは1分だけで済みます。
まとめ
最終的なコンフィグがこちら。
これでnginx⇒fluentd⇒S3⇒Redshiftまでテスト済みです。
いまのところ、Redshiftへの読み込みはクエリで手動でやっています。
<source>
type tail
path /var/log/nginx/access.log
pos_file /var/log/td-agent/buffer/api.access.log.pos
format ltsv
time_format %d/%b/%Y:%H:%M:%S %z
tag nginx.access
</source>
<match nginx.access>
type s3
format_json true
output_tag false
output_time false
include_time_key true
include_tag_key true
time_format %Y-%m-%d %H:%M:%S
aws_key_id アクセスキー
aws_sec_key シークレットアクセスキー
s3_bucket バケット名
s3_region ap-northeast-1
path logs/
buffer_path /var/log/td-agent/s3
time_slice_format "#{ENV['HOSTNAME']}/%Y/%Y%m/%Y%m%d/%H"
time_slice_wait 10m
flush_interval 1m
# utc
</match>
Redshiftのはなし
Redshiftへのコピー
Redshiftへの読み込みについて、まずS3は上記設定ファイルにしたがって、
バケット名/logs/ホスト名/YYYY/YYYYMM/YYYYMMDD/
の中に、
HH_連番.gz
というファイルが大量に生成されます。
連番とgzipについてはRedshiftのCOPYコマンドが自動で処理してくれますので、こんな感じで読み込めます。
COPY テーブル名 FROM 's3://バケット名/logs/ホスト名/YYYY/YYYYMM/YYYYMMDD/HH'
CREDENTIALS 'aws_access_key_id=アクセスキー;aws_secret_access_key=シークレットアクセスキー'
JSON 'auto' GZIP;
このように、連番や拡張子は入力しなくても、Redshiftが勝手にやってくれます。
ちなみに、JSONPATHファイルは不要です。ログファイルのJSONは構造フラットですし、CREATE TABLEをちゃんとやっておけばautoでCOPYできます。一旦CHARとVARCHARで取り込んで、あとからintとかに変換しています。巨大になったらまずいかなあって思いつつです。
馬鹿みたいに冗長化してあるので、日単位や月単位の読み込みも簡単です。
COPY テーブル名 FROM 's3://バケット名/logs/ホスト名/YYYY/YYYYMM/YYYYMMDD/'
CREDENTIALS 'aws_access_key_id=アクセスキー;aws_secret_access_key=シークレットアクセスキー'
JSON 'auto' GZIP;
COPY テーブル名 FROM 's3://バケット名/logs/ホスト名/YYYY/YYYYMM/'
CREDENTIALS 'aws_access_key_id=アクセスキー;aws_secret_access_key=シークレットアクセスキー'
JSON 'auto' GZIP;