EC2のログをfluentdを使ってタグ情報を利用したフォルダ構成でS3にアップロードする

オートスケール環境ではEC2の起動、停止が動的に行われるのでログ(syslogやアプリ、ミドルウェア)を外部に出す必要があります。

やり方として

+ S3にアップロード
+ CloudWatchLogsを使う
+ ログサーバーに転送

などが一般的だと思います。

S3にアップロードする方法を考えた場合、どのようなフォルダ構成でアップロードするかは結構大切なことだと思います。(探しずらいとあとで大変)

また、本番環境、検証環境などがある場合、どこにどうおくかをEC2のタグの情報を利用して考えたい場合もあると思います。

そこで今回はEC2のログをfluentdを使ってmetadataやタグ情報を利用したフォルダ構成でS3にアップロードすることをやってみました。

今回の例では/var/log/messagesをS3に以下の構成でアップロードするようにしてみました。

```
Bucket
│
├-Env(production,staging,development)
│  └─HostName
|        └─Year
|            └─Month
|                └─Day
│                   │
│                   └─syslog.messages-2015-08-15-09_0.gz
```

環境、ホスト名、年、月、日でディレクトリが切られ、その下に時間ごとのログが圧縮されたものが配置されます(時間のあとの数字はシーケンシャル番号)

fluentdの設定を増やせばそれ以外のログのアップロードも当然可能です。

# 環境

+ Amazon Linux AMI 2015.03 (HVM)
+ td-agent 0.12.12

# 参考

+ [fluentdを使ってS3へログを送る](http://blog.serverworks.co.jp/tech/2013/06/27/fluentdtos3/)
+ [FluentdとAWSを使ったログの運用](http://www.slideshare.net/kski0530/fluentdaws)

# 利用するfluentdのプラグイン

今回は以下3つのfluentdのプラグインを利用しました。

EC2のmetadataを利用できるプラグイン

[takus/fluent-plugin-ec2-metadata](https://github.com/takus/fluent-plugin-ec2-metadata)


ログをS3へアーカイブして送るプラグイン

[fluent/fluent-plugin-s3](https://github.com/fluent/fluent-plugin-s3)


hostnameコマンドの結果の展開やタグの分解と展開ができるプラグイン

[tagomoris/fluent-plugin-forest](https://github.com/tagomoris/fluent-plugin-forest#forestoutput)

# EC2へ設定するIAMロール

fluentdからS3への書き込み及びEC2のタグ情報を取得するため、以下の権限を持ったIAMロールを事前に作成しておきます。

+ S3への書き込み権限
+ EC2のdescribeInstancesAPIの利用(EC2のタグはmetadataでは取れず、describeInstancesで取得する必要があるので)

# 起動するEC2の設定

先ほど作成したIAMロールを付与します。

また、今回の例ではタグを以下のように設定。

+ key=Name, Value=fluentd-test
+ Key=Env, Value=production

Envではサーバーの環境を示してproduction,staging,developmentなどを指定するイメージ。

# EC2の時刻設定

[【Linux】タイムゾーン(Timezone)の変更](http://qiita.com/azusanakano/items/b39bd22504313884a7c3)

AmazonLinuxではデフォルトの時間がUTCとなっているのでJSTに変更します。

```
$sudo cp /etc/localtime /etc/localtime.old
$sudo cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
```

```lang:/etc/sysconfig/clock
ZONE="Asia/Tokyo"
```

今回は/var/log/messagesの変更をしたいのでsyslogを再起動します(再起動しないとタイムゾーンが変更前のままでロギングされる）

```
$sudo service syslog restart
```
# flunetdのインストール

```
$curl -L http://toolbelt.treasuredata.com/sh/install-redhat-td-agent2.sh | sudo sh
$sudo chkconfig --add td-agent
```

# プラグインの追加

```
$sudo /opt/td-agent/embedded/bin/fluent-gem install fluent-plugin-ec2-metadata
$sudo /opt/td-agent/embedded/bin/fluent-gem install fluent-plugin-s3
$sudo /opt/td-agent/embedded/bin/fluent-gem install fluent-plugin-forest
```

# ec2-metadataプラグインの確認

Github記載の通りの形で試してみる

```lang:/etc/td-agent/td-agent.conf
<source>
  type forward
</source>

<match foo.**>
  type ec2_metadata

  output_tag ${instance_id}.${tag}
  <record>
    hostname      ${tagset_name}
    instance_id   ${instance_id}
    instance_type ${instance_type}
    az            ${availability_zone}
    vpc_id        ${vpc_id}
  </record>
</match>

<match **>
  type stdout
</match>
```

td-agentをトレースモードで起動

```
$td-agent -vv &
```

fluent-catを使って試してみる

```
$echo '{"json":"message hello ec2"}' | /opt/td-agent/embedded/bin/fluent-cat foo.bar
2015-08-13 08:25:50 +0000 [trace]: plugin/in_forward.rb:194:initialize: accepted fluent socket from '127.0.0.1:44238': object_id=26543580
2015-08-13 08:25:50 +0000 i-3d9d08cf.foo.bar: {"json":"message hello ec2","hostname":"fluentd-test","instance_id":"i-3d9d08cf","instance_type":"t2.micro","az":"ap-northeast-1a","vpc_id":"vpc-c406dea1"}
2015-08-13 08:25:50 +0000 [trace]: plugin/in_forward.rb:262:on_close: closed socket
[ec2-user@ip-172-31-22-152 td-agent]$ 2015-08-13 08:25:50 +0000 fluent.trace: {"message":"accepted fluent socket from '127.0.0.1:44238': object_id=26543580"}
2015-08-13 08:25:50 +0000 fluent.trace: {"message":"closed socket"}

```

無事タグ情報が取得できています!

fluentdの停止

```
$ps aux|grep ruby|awk '{print $2;}'|sudo xargs kill -KILL
```

# /var/log/messageの送信をやってみる

以下になるように設定します。

```
Bucket
│
├-Env(production,staging,development)
│  └─HostName
|        └─Year
|            └─Month
|                └─Day
│                   │
│                   └─syslog.messages-2015-08-15-09_0.gz
```

`{Bucket}/production/ip-172-31-20-54/2015/08/14/syslog.messages-2015-08-14-11_0.gz`という感じ。


対象ログをtd-agentで読み取れるようにします。

```
$sudo chgrp td-agent /var/log/messages
$sudo chmod g+rx /var/log/messages
```


また、以下のように設定します。

```lang:/etc/td-agent/td-agent.conf
<source>
  type tail
  format syslog
  pos_file /tmp/syslog.pos
  path /var/log/messages
  tag syslog.messages
</source>

<match syslog.**>
  type ec2_metadata

  output_tag ${tagset_env}.${tag}
  <record>
    env      ${tagset_env}
  </record>
</match>

<match {production,staging,development}.**>
  type forest
  subtype s3
   <template>
     s3_bucket toshihirock-fluentd-test
     s3_region ap-northeast-1
     buffer_path /var/log/td-agent/buffer/${tag}
     time_slice_format ${tag_parts[0]}/${hostname}/%Y/%m/%d/${tag_parts[1..-1]}-%Y-%m-%d-%H
      flush_at_shutdown true
   </template>
</match>
```

+ sourceで/var/log/messagesを読み込み
+ 一つ目のmatchでEC2のタグ情報(Envで指定した内容)を取得し、fluentdのタグに追加
+ 2つ目のmatchでS3へのアップロードを実施。time_slice_formatでfluentdのタグの情報やhostnameの値を取得してディレクトリ構成に利用

ec2_metadataプラグインの指定部分のrecordの記述はいらない気がしますが、現在はtagset_xxxというものをoutput_tagで使う場合、recordにも入れないとダメなようなので入れています。（対応するプルリク送ってみました)

これでサービス起動し、適当に待てばいい感じにS3にアップロードされています。

```
$sudo service td-agent start
```

![Screen Shot 2015-08-15 at 10.45.50 AM.png](https://qiita-image-store.s3.amazonaws.com/0/20193/d27bc45a-4561-e663-1af2-57df7ea2172f.png)

# まとめ

EC2のログアップロードはAWS運用で避けられない作業な気がしますが、適当にするとあとあと大変なので予めやり方を確立しておくと良いと思います。

今回紹介したfluentdのやり方であれ導入障壁も少なく、ディレクトリ構成もある程度好きな感じにできるので実用的だと思います。

# 20150818追記

上記プルリクエストががマージされました！ありがとうございます！