【EC2】Fluentd で Auto Scaling グループ配下にあるインスタンス内のログをS3へ転送 (前編)

そもそもなぜログを転送・集約するのか

冗長構成の Web サーバーにおいて、アクセスログやエラーログといった各種ログファイルが複数サーバーに分散してしまうことへの対策。
また、オートスケーリング環境だとスケールイン時にサーバーそのものが消えてしまうといった事情より。

ログ集約先

用途によって様々。
監査目的で単純に一定期間ファイル保管するだけだったり、分析するために DB へ投入したり。

ログ収集ツール (エージェント)

ログ集約のために、主に下記のようなツールが存在する。
大体、サーバー内でデーモンとして動くタイプかと。

やってみた

Fluentd を使ってログファイルを、とりあえず Amazon S3 へ転送してみた。
ちなみに、設定を変えれば、他のログファイルも転送できるし、転送先も自由自在。

Fluentd のインストール

上記手順にも記載されているが、RPM インストール前にNTP デーモンの確認やファイルディスクリプタ数の増加等の事前準備 を行うべし。

また、Apache のログファイルを読み取る関係上 Fluentd を root 権限で起動する必要があるので、systemd のドロップインを作成して起動設定を変更。

# mkdir /etc/systemd/system/td-agent.service.d
# vim /etc/systemd/system/td-agent.service.d/10-td-agent.service.conf

[Service]
User=root
Group=root
Environment="TD_AGENT_OPTIONS=--user root --group root --config /etc/td-agent/td-agent.conf"
# systemctl daemon-reload
# systemctl enable td-agent
# systemctl restart td-agent

Fluentd の設定

とりあえず、作成した設定。

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

字面から大体読み取れるが、Input の設定 (<source>) と Output の設定 (<match>) をどう繋ぐ (ルーティングする) かを設定しているだけ。

syslog 読み取り設定

<source>
  @type syslog
  @label @META
  tag system.local
  port 5140
  bind 127.0.0.1
  priority_key priority
  facility_key facility
</source>

/var/log/messages のファイルなどを tail するわけではなく、syslog デーモンへ UDP 接続しに行くので、syslog デーモン側でも UDP ソケットの待受設定を行っておく必要がある。

Amazon Linux 2 は rsyslog なので、/etc/rsyslog.conf 末尾に下記を追記して systemctl restart rsyslog すれば良い。

/etc/rsyslog.conf

.
.
.
# Send log messages to Fluentd
*.* @127.0.0.1:5140

Apache ログ読み取り設定

<source>
  @type tail
  @label @META
  tag httpd.access
  path /var/log/httpd/access_log
  pos_file /var/log/td-agent/httpd-access.log.pos
  <parse>
    @type ltsv
    time_key time
    time_format %d/%b/%Y:%H:%M:%S %z
  </parse>
</source>

<source>
  @type tail
  @label @META
  tag httpd.error
  path /var/log/httpd/error_log
  pos_file /var/log/td-agent/httpd-error.log.pos
  <parse>
    @type apache_error
  </parse>
</source>

こっちは普通にログファイルを tail する設定。

アクセスログに関しては、標準では conbined 形式のログをパースできる。
しかし、ログ形式をカスタマイズした場合に対応が難しいので、こちらの記事を参考に LTSV (Labeled Tab-separated Values) 形式でアクセスログを出力するように Apache の設定を変更した。

/etc/httpd/conf/httpd.conf

    LogFormat "domain:%V\thost:%h\tserver:%A\tident:%l\tuser:%u\ttime:%{%d/%b/%Y:%H:%M:%S %z}t\tmethod:%m\tpath:%U%q\tprotocol:%H\tstatus:%>s\tsize:%b\treferer:%{Referer}i\tagent:%{User-Agent}i\tresponse_time:%D\tcookie:%{cookie}i\tset_cookie:%{Set-Cookie}o" ltsv

    CustomLog "logs/access_log" ltsv

ログ行に EC2 メタデータを付与

<label @META>
  <match **>
    @type ec2_metadata
    @label @S3
    output_tag ${tag_parts[0]}.${tag_parts[1]}
    <record>
      instance_id   ${instance_id}
      private_ip    ${private_ip}
    </record>
  </match>
</label>

ドキュメントから読み取れる通り、Fluentd はプラグインアーキテクチャを採用しており、入出力やログの加工といった様々な機能がプラグインとして実装されていて、サードパーティ製のプラグインも多数公開されている。

今回は、オートスケーリンググループ配下の EC2 インスタンスに保存されているログを集約するので、fluent-plugin-ec2-metadata というプラグインを使用して、どのインスタンスのログか識別できるように EC2 のメタデータ (インスタンス ID、プライベート IP) をログに含めてみた。

なお、サードパーティ製のプラグインを使用する場合は、プラグインのインストールが必要。

# /opt/td-agent/embedded/bin/fluent-gem install fluent-plugin-ec2-metadata

S3 へ転送

<label @S3>
  <match **>
    @type s3
    s3_bucket logs.example.com
    s3_region ap-northeast-1

    <shared_credentials>
      path         /root/.aws/credentials
      profile_name logs
    </shared_credentials>

    store_as json # gzip, json, txt, lzo

    <format>
      @type json # json, csv, msgpack...
    </format>

    path ${tag[0]}-${tag[1]}/${instance_id}_
    time_slice_format %Y%m%d%H

    <buffer tag,time,instance_id>
      @type file
      path /var/log/td-agent/buffer/${tag[0]}
      timekey 3600
      timekey_wait 10m
      chunk_limit_size 256m
    </buffer>
  </match>
</label>

これも見りゃ大体わかりますが…… S3 へ転送する際の諸々の設定。

IAM の認証情報は <shared_credentials>~/.aws/credentials に記載されたプロファイルを使えたり、<instance_profile_credentials> でインスタンスに割り当てられた IAM ロールを使えるらしい (参考)。
権限的には当然ながら、対象S3バケットへの読み書き権限が必要になる。

また、S3 なので当然ファイル単位での転送となる。よって、バッファを設けてファイルに書き込むべきログを一定期間ためておく仕組みが必要になる。
今回は 1 時間ごとにファイルを生成して S3 へ転送するようにした。

後編へ続く……

(余談)

新しいバージョンの Fluentd では、設定ファイル内に path ${tag[0]}-${tag[1]}/${instance_id}_ のようなプレースホルダが用いてログの内容を設定値として使えるようになった。

しかし、これを使うためにはプレースホルダとして使うキーを <buffer tag,time,instance_id> のように <buffer> のところに列挙する必要がある (数時間ハマった)。

The important point is if you want to refer time, tag or record keys on path, you need to list keys in (See also v0.14 Plugin API slide).
Fluentd v0.14.9 has been released | Fluentd