LoginSignup
0
0

More than 5 years have passed since last update.

nginxのアクセスログをfluentdでS3に送ってRedshiftにコピーする 2016年版

Last updated at Posted at 2016-10-12

新旧色々な情報が入り乱れていて、なかなかうまく動かなかったので、動いたものをメモっておきます。ちなみに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への読み込みはクエリで手動でやっています。

td-agent.conf
<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;
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0