開発メモ#6 : ログの取り扱い : GrowthForecast, Amazon S3, Treasure Data で心労ゼロ

開発メモ#6 です。前回から少し間があいてしまいました。

開発メモ#2 : AWS でのホスト / クラウドネイティブなデプロイ - naoyaのはてなダイアリー で書いたように、EC2 へのアプリケーションのデプロイにあたっては Elastic IP の利点を活かしてカジュアルにホストを入れ替えまくっています。ちょっとこのデプロイは慎重になりたいな、と思ったらスナップショットからインスタンスを立ち上げては切り替える、の繰り返し。

この運用をしていると、スナップショットとの差分ができやすいのは chef-solo で吸収するというのが前回、前々回のはなし。

もう一点問題があります。アクセスログやアプリケーションのログです。フロントエンドのサーバをあっちこっち切り替えているうちに、そのままではログが分断されてしまう。ホストを Terminate しようものならログは消失してしまいます。

この手のケース、ログサーバーなんかを立てて古くは syslog agent、最近は fluentd に飛ばして別途集約しておくというのが定石かと思います。

しかしまあ、個人でやってるアプリケーションだったりするとわざわざログサーバー立てるまでもないよね。なんか良い方法ないかな、ということもあるはず。そうでなくても、少ない人手でログサーバーを運用するのはちょっと面倒という話も。

ログのユースケース

そこで、そもそもログファイルの主なユースケースを考えます

  • 障害発生時などの調査用
  • アクセス数その他の統計データ解析用
  • ログの提出を求められるようなイレギュラーなイベントへの対応用

あたりでしょうか。これに対し

という方針を採ることで、自分はログサーバなしで要件に対応するようにしました。

Treasure Data については後日もうすこし詳細に書いてみようかなと思ってますが、簡単にいうとログデータを継続的に送り続けておいて、解析したいと思ったら SQL のような言語 (HiveQL) で(大規模並列に)解析がかけられる、というサービス。

Amazon Glacier は、AWSアーカイブサービスで、普段は参照しないけど必要なときには時間がかかってもいいから取り出したい、というデータを安価に保存することができるサービスです。

ログは fluentd で飛ばす

例によってログは fluentd で飛ばします。基本ログファイルは LTSV で吐いておいて、それを fluentd でつかんで転送。

入力は

<source>
  type tail_labeled_tsv
  path /var/log/nginx/access.log
  tag nginx.access
  pos_file /var/log/td-agent/nginx.access.log.pos
</source>

としている。LTSV 解析の tail_labeled_tsv 相当の機能は既に本体に取り込まれてるので今ならプラグインを使う必要はない。

出力は以下のように GF、S3、TD へそれぞれ転送。途中、extract_query_params プラグインを使っています。これはアクセスログ内の "/entry?foo=bar&bar=baz" みたいなフィールドを { "foo" : "bar", "bar" : "baz" } みたいなデータ構造に変換してくれるもの。TD に送るにあたっては、クエリパラメータの値を軸にした集計なんてのもよくやるのでそれらに分解して送信しておくと良い。

なお、以下の例は設定の一部です。

<match nginx.access>
  type copy
  
  ## GrowthForecast へレスポンスタイムを送る
  <store>
    type growthforecast
    gfapi_url http://localhost:5125/api/
    service   nginx
    section   response_time
    name_keys response_time,upstream_response_time
  </store>

  ##  Amazon S3 へ保存
  <store>
    type config_pit
    <pit aws>
      type s3
      s3_bucket amazlet-ec2-log
      s3_endpoint s3-ap-northeast-1.amazonaws.com

      aws_key_id  $pit[aws_access_key]
      aws_sec_key  $pit[aws_secret_access_key]

      path nginx/
      buffer_path /var/log/td-agent/buffer/s3
      time_slice_format %Y/%m/%d/access_log-%Y%m%d-%H
    </pit>
  </store>
  
  ## extract_query_params クエリパラメータを分解・展開しておく
  <store>
    type extract_query_params
    key path
    add_tag_prefix extracted.
    only locale, __mode, affiliate_id, keyword, asin
  </store>
</match>

<match extracted.nginx.access>
  type copy
  
  ## クエリパラメータも込みで、Treasure Data へ飛ばす
  <store>
    type config_pit
    <pit treasuredata>
      type tdlog
      apikey  $pit[td_apikey]
      auto_create_table
      buffer_type file
      buffer_path /var/log/td-agent/buffer/td
      use_ssl true
    </pit>
  </store>
  …
</match>

こうやって飛ばしておくとGF で以下のように風にグラフ化されて

http://cdn.bloghackers.net/images/20130219_171040.png

S3 にちゃんとログが小分けにされて保存されて

http://cdn.bloghackers.net/images/20130219_171226.png

Treasure Data で SQL みたいなもので簡単に解析ができる。

$ td query -w -d nginx "select v['asin'] as asin, count(1) as cnt from access group by v['asin'] order by cnt desc limit 100"
Job 1792464 is queued.
Use 'td job:show 1792464' to show the status.
queued...
  started at 2013-02-18T01:26:58Z
  Hive history file=/tmp/1624/hive_job_log__800096608.txt
  Total MapReduce jobs = 2
  Launching Job 1 out of 2
  Number of reduce tasks not specified. Defaulting to jobconf value of: 12
  In order to change the average load for a reducer (in bytes):
  …

これだけやってもログの管理はしなくていい。さっぱりしますね!

S3 ⇒ Glacier への変換は自動で

Amazon S3 へ保存したログは、ある程度の日数が経ってもういいかなと思った頃には Glacier でアーカイブ化してしまいたい。そうすることで、古いログは参照しづらくはなるけどその分ストレージ価格がずっと安価になる。Amazon S3 の 1/10 の価格で、1GB 1円 です。

「その変換のために定期的にバッチスクリプトとか回すの?」─ いいえ、S3 でチェックボックス一個です。

http://cdn.bloghackers.net/images/20130219_171939.png

という風にバケット全体に「30日経ったら Glacier に移す」という風に設定できる。簡単!

豆知識: Glacier には S3 を経由せずに保存することもできるけど、その場合は Glacier に保存されたアーカイブが元々どういうファイルだったかという紐づけは自分で管理しなければいけないらしい。S3 経由にしておくと、そもそも S3 のインタフェースで Glacier のファイルを引っ張り出せるので、その必要がない。ただし S3 経由で保存したものは S3 経由でしか取り出せなくなり、Glacier API 直接では取り出せない。その逆もまた然り・・・ということを昨日の #awspad で知った。

なお、Glacier オプションを有効にしておいたのにそれまで取り出し方を知らなかったのは秘密だ。

雑感

最終的にフロントエンド周りのサーバーが複数台構成になってくると、結局ログを GF なり TD なりに転送するまえにいったん集約する必要が出てきてログサーバーが必要になるだろうけど、それまではこのやり方で運用できるでしょう。複数台になった場合も、ログを保存するストレージ周りを気にしなくて良いという意味でも中長期的に負担の少ない運用だと思います。

仮にもうちょっと小規模な集計とか調査をばりばりやりたいというニーズが出てきたら、それ目的の mongodb なり何なりを用意して保存先を追加すれば OK。長期保存や大規模解析は S3 や TD に任せたままにしておけるのでその mongodb の役割も必要最小限にできるし気を病まなくて良い。

とにかくログ周りをどうするか、ストレージをどうするか、ということをもう悩まなくてもいいのはとても気がらくでいいですね。

fluentd をはじめ、こんな便利な各種ツールやサービスを開発・運営していただいている皆様には足を向けては寝られませんね。ただし、実際どの方角にいるか知らないから、案外毎晩足を向けてる可能性は否めません。