AWS : Fluentdを使ってEC2からApacheのログをS3へ

月花です。
久しぶりの技術記事です。

最近AWSを触っていて、わかりにくいなあと思ったS3との連携について。

AWSでは、障害が起こったりAutoScalingしてたりすると、EC2インスタンスが勝手にシャットダウンされてしまう。
このとき、ログが吹っ飛ぶのでS3へ逃しておく必要がある。

前提

  • WebサーバとしてのEC2が複数
  • OSはAmazon Linux
  • Apache
  • S3バケットはサーバ分作る

やりたいこと

  • Apacheの アクセス/エラー ログを両方S3へ逃す
  • 設定は軽いほうがいい
  • 自動でやってほしい
  • {server_name}/access_log/{YYYY}/{MM}/{DD}/access_log_{YYYY}-{MM}-{DD}_{HH}.log みたいにしてほしい

アプローチ

まずはどのみちS3へ転送が必要なため、EC2インスタンスそれぞれにS3フルアクセス権限を付与する。

これで、S3への操作権限を得た。このままcpコマンドでコピーすることもできるが、勝手にシャットダウンされて吹っ飛ぶので極めて短い間隔のcronかなにかでcpすることになる。
しかも、都度時間を見て、ディレクトリなければ作るなんてコード書くのめんどくさいし非効率。

そこで Fluentd を用いて、自動化してもらう。
こういうのを、Web Storage Archiveパターン と呼ぶらしい。
CDP:Web Storage Archiveパターン - AWS-CloudDesignPattern

S3へのアクセス権限を与える

まずはIAMロール・・・ではなく、既にEC2インスタンスは動いてしまっているので、IAMユーザを作成する。
サーバ分作ったら(1つでもいいのかしら)、アクセスキーとかをメモしておく。

下記の「AWS CLI を使用してロールを切り替えるには」を参考に、起動中EC2インスタンスに接続し、ユーザを割り当てる。
docs.aws.amazon.com

$ aws configure
AWS Access Key ID [None]: your_key_id
AWS Secret Access Key [None]: your_sec_key
Default region name [None]: ap-northeast-1
Default output format [None]: json

これでアクセス権限を付与できた。
Amazon LinuxにはデフォルトでAWS CLIという、AWSサービスをいじるためのクライアントが搭載されており、S3関連のコマンドは以下の記事に詳しい。
www.task-notes.com

なお、再起動は必要ないので、そのまま叩いてみて、成功なら成功である。失敗したら頑張れ。

NTPを設定する

Amazon LinuxのデフォルトはUTCなので、設定してないと、Fluentdくんが作るファイルを時刻を使って階層化するとき、めちゃくちゃになる。
docs.aws.amazon.com

インスタンスのセキュリティグループのルールでは、ポート 123 (NTP) でアウトバウンド UDP トラフィックを許可する必要があります。ネットワーク ACL のルールでは、ポート 123 でインバウンドとアウトバウンドの UDP トラフィックを両方許可する必要があります。

筆者はこれを忘れてハマった。

Fluentd のインストール

公式ページ
docs.fluentd.org
を参考に、とりあえず入れるだけ入れる。

$ curl -L https://toolbelt.treasuredata.com/sh/install-redhat-td-agent2.sh | sh

こういうインストール方法、めちゃめちゃ助かる。

素のままでも使いやすいっぽいのだが、やりたいことがあるので、Fluentdのプラグインをいれる。
github.com

$ /usr/sbin/td-agent-gem install fluent-plugin-forest

Fluentd の設定

どのファイルをどれくらいの間隔で、どんな名前でどこに転送するのかを定義する。
その定義ファイルは、以下にある。

/etc/td-agent/td-agent.conf

まずは、source節を編集して、どのファイルを対象とするかを定義する。
なお、基本的に末尾に追記で問題ない。

<source>
  @type tail
  format apache
  path /var/log/httpd/access_log
  pos_file /var/log/td-agent/httpd.access.log.pos
  tag s3.httpd.access
</source>

ここでは例として、Apacheアクセスログを対象とする。

type tailにするとtailコマンドで末尾をフォローしてくれる。このタイプによってオプションが変わる。ここから先は全部tailが対象のオプション
format 対象とするログのフォーマットを選択する*1
path 対象のログへのフルパス
pos_file 前回どこまで読んだかが書いてある栞ファイル。Fluentdが勝手に生成するので、わかりやすい名前にしておく
tag ソースごとにタグ付けが行える

このあと、エラーログを見るsourceも書いておいた。
タグは s3.httpd.error とした。


次に、さきほどのソースを、どれくらいの間隔で、どんな名前で、どこに転送するのかを定義するために、match節を記述する

<match s3.*.*>
  type forest
  subtype s3
  <template>
    aws_key_id your_key_id
    aws_sec_key your_sec_key
    s3_bucket bucket_name
    s3_region ap-northeast-1
    path ${tag_parts[1]}/${tag_parts[2]}_log/
    buffer_path /var/log/td-agent/s3/${tag_parts[2]}_log
    time_slice_format %Y/%m/%d/${tag_parts[2]}_log_%Y-%m-%d_%H
    time_slice_wait 10m
    #flush_interval 3s
    buffer_chunk_limit 256m
  </template>
</match>
match タグに対応している。このmatch節はどのソースに使うのか、ここで定義する
type さきほどインストールしたプラグインを使うのでforestとする
subtype S3に投げるため。この辺は用途によって決まってくる
aws_key_id IAMユーザのキーID
aws_sec_key IAMユーザのシークレットキー
s3_bucket S3バケット名。なければ作ってくれる
s3_region S3のあるリージョン。例では東京
path バケット直下からの共通のパス。forestによって、${tag_parts}が有効になっている。matchのところで指定した、*の部分が配列になって1から順に収まっている。このおかげで、複数のソースに対して1つのmatchで済むようになる
buffer_path S3へ転送する前のバッファファイル。わかりやすい名前にしておく
time_slice_format 生成したいファイル名。年月日とかはforestがなくても取れるから階層化しておこう
time_slice_wait S3へ転送する間隔
flush_interval こいつがあると、上記の間隔より優先される。例ではコメントアウトしているが、3秒なのでテスト向け
buffer_chunk_limit 指定したサイズを超えたバッファファイルはエンキューされて、次のバッファファイルが作られる

Fluentd を起動してみる

いつものやつで起動して、ログを見てみる。

$ chkconfig td-agent on
$ service td-agent start
$ td-agent td-agent:                                         [  OK  ]
$ cat /var/log/td-agent/td-agent.log

起動はしたが、エラーがでていた。

2016-11-08 17:13:58 +0900 [error]: Permission denied @ rb_file_s_stat - /var/log/httpd/access.log
2016-11-08 17:13:58 +0900 [error]: suppressed same stacktrace

出たー、Permissionだ!!!!
ありがちなハマり。

多少強引だが、他の設定をいじらなくてよいので、Fluentd の実行ユーザをrootにしてしまおう。
kenzo0107.hatenablog.com

TD_AGENT_USER=td-agent 
TD_AGENT_GROUP=td-agent 

↓ここをこう↓

TD_AGENT_USER=root
TD_AGENT_GROUP=root

restartしてログを見てみると、

2016-11-08 18:51:54 +0900 [info]: listening fluent socket on 0.0.0.0:24224
2016-11-08 18:51:54 +0900 [info]: listening dRuby uri="druby://127.0.0.1:24230" object="Engine"
2016-11-08 18:51:54 +0900 [info]: following tail of /var/log/httpd/access_log
2016-11-08 18:51:54 +0900 [info]: following tail of /var/log/httpd/error_log

ということで、無事にログファイルがフォローされた。

S3を確認

AWSのサイトでバケットを見に行こう。

今回の例では

すべてのバケット /bucket_name/httpd/access_log/2016/11/08/access_log_2016-11-08_17_2.gz

という具合になって、終わり。

*1:一覧 : docs.fluentd.org