Elastic Beanstalk上のログをBigQueryに保存して分析基盤を構築する


# はじめに

BigQueryとかFluentdとか知識だけはあったのに触ったことが無かったので、勉強も兼ねて個人的に構築中のシステムにBigQueryを利用した分析基盤を構築しました。

調べるとBigQuery周りの情報はあったのですが、Elastic BeanstalkからBigQueryを利用する記事がなかったのでまとめました。

Rails on AWS Elastic Beanstalk の環境に td-agent をインストールし、BigQueryにログを送信する環境を構築しています。

# なぜBigQueryか

どんなサービスでも初期時点でKPIを追えるようにしておくことは必須だと思っています。

初期の指標はDaily Active Userや継続率などになると思いますが、ぶっちゃけGoogle Analyticsとかだけで十分ですよね。

今回はAPIのみが利用される感じなのでフロント側での測定ができず、何かしらサーバサイドでの分析環境を構築する必要がありました。

一番簡単なのは利用しているMySQLにログをぶち込んでいく形ですが、もし大規模になった時にコスト面や性能面で問題が出てきそうなので、どうせならはじめからBigQueryを使ってみることにしました。（正直使ってみたかっただけという話もありますが）

AWSだと列指向のDBで高速な分析環境としてはRedShiftがありますが、いかんせん最低金額が高いので今回は見送りました。

# セットアップ手順

## 1. BigQueryのセットアップ

GCPアカウントを作り、今回のアプリケーション用のProjectを作成します。

その後BigQueryにアクセスし、Datasetを作成します。DatasetはMySQLでのデータベースにあたるみたいです。

<img width="623" alt="スクリーンショット 2016-08-16 17.26.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/82022/e3367827-b391-b524-bedf-1c845f462def.png">

<img width="672" alt="スクリーンショット 2016-08-16 17.16.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/82022/d272f4a8-bf5b-b169-4fb4-5b8ea76f7a55.png">


今回テーブル定義はfluentdの方で自動的に作る設定にするので設定はこれで完了です。

最近使えるようになったDate-Partitioned Tables を利用する場合はあらかじめ `bq` コマンドで作成しておく必要があるのでその場合はここで作成しておきます。

## 2. 認証情報の取得、配置

AWSからアクセスするにはAPIのアクセスキーが必要となります。

https://console.cloud.google.com/apis/credentials

にアクセスし、サービスアカウントキーを作成します。
サービスアカウントとしてここではCompute Engine default service accountを選択します。
（きちんとやる場合は専用のサービスアカウントを作成し、限られたパーミッションを与えることになると思います。）

<img width="484" alt="スクリーンショット 2016-08-16 18.21.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/82022/516428f8-9f2d-e370-bf08-b1ebc4aa475c.png">


jsonファイルがダウンロードできます。
これをElastic Beanstalkで使いたいのですが、リポジトリにコミットするわけにもいかないので、S3に配置し、動的に取得するようにします。

S3に適当なプライベートバケットを作成し、そこにJSONを配置しておきます。（Elastic Beanstalkのアプリケーションが保存されるバケットでもOKです。）

## 3. Railsでログを出力する

今回はRails環境でつくったので、せっかくなのでそこについても触れておきます。

ログの形式は色々考えられますが、最近だとLTSVかJSONかという感じでしょうか。

JSONで問題ある場合が無い気がするのでJSONで出力するカスタムログを作ります。

まずFormatterを作成します。

```ruby:app/model/analysis/formatter.rb
module Analysis
  class Formatter < ActiveSupport::Logger::SimpleFormatter
    def call(_severity, _timestamp, _progname, message)
      "#{message.to_json}\n"
    end
  end
end
```

RailsのLoggerとして使えるようにenvironmentsのファイルに適当に設定します。

```ruby:config/environments/*.rb
Rails.application.configure do
  ...
  config.custom_logger = Logger.new('log/custom.log', 'daily')
  config.custom_logger.formatter = Analysis::Formatter.new
  ...
end
```

（これは必須ではないですが）ログを出力をラップするLoggerクラスを作成しました。

```ruby:app/model/analysis/logger.rb
module Analysis
  class Logger
    def self.custom_log(user, path)
      Rails.application.config.custom_logger.info(
        timestamp: Time.zone.now.to_i,
        user_id: user&.id,
        path: path
      )
    rescue => e
      Rails.logger.error(e.message)
      Rails.logger.error(e.backtrace)
    end
  end
end
```

任意の場所でログを出力します。

```ruby:application_controller.rb
class ApplicationController < ActionController::Base
  after_action :custom_log
  
  def custom_log
    Analysis::Logger.custom_log(current_user, request.url)
  end
end
```

## 4. td-agentを設定する

Elastic Beanstalk はPaaSですので直接サーバをいじれません。
.ebextensions というディレクトリの中に設定を書いて実装することになります。

まずは2.の手順で行ったcredential情報の入ったjsonを持ってきます。
もしかしたらあらかじめroleにs3へのアクセス権を付与しておかないといけなかったかも。ちょっとここのRoleの記述は設定を色々変えながらやったので手順が漏れているかも。。

```yaml:.ebextensions/10-credential.config
Resources:
  AWSEBAutoScalingGroup:
    Metadata:
      AWS::CloudFormation::Authentication:
        S3Auth:
          type: "s3"
          buckets: ["backet-name"]
          roleName:
            "Fn::GetOptionSetting":
              Namespace: "aws:autoscaling:launchconfiguration"
              OptionName: "IamInstanceProfile"
              DefaultValue: "aws-elasticbeanstalk-ec2-role"
files:
  /etc/td-agent/big_query.json:
    mode: "000644"
    owner: root
    group: root
    authentication: "S3Auth"
    source: https://s3-ap-northeast-1.amazonaws.com/backet-name/credentials/big_query.json
```

次にtd-agentの設定ファイルを配置します。
json_keyを使う設定がポイントでしょうか。

```yaml:.ebextensions/11-td-agent-gen-config.config
files:
  "/etc/td-agent/td-agent.conf":
    owner: root
    group: root
    content: |
      <source>
        @type tail
        format json
        path /var/app/current/log/custom.log
        tag app.custom
        pos_file /var/log/td-agent/app.custom.pos
      </source>

      <match app.**>
        @type bigquery
        method insert
        auth_method json_key
        json_key /etc/td-agent/big_query.json

        project myservice-140110
        dataset base
        
        auto_create_table true
        table accesslog_%Y_%m

        field_integer   user_id
        field_string    path
        field_timestamp timestamp
      </match>
```

そしてtd-agentのインストール設定を書きます。

```yaml:.ebextensions/12-td-agent-install.config
# errors get logged to /var/log/cfn-init.log. See Also /var/log/eb-tools.log
commands:
    01-command:
        command: echo 'Defaults:root    !requiretty' >> /etc/sudoers

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

    03-command:
        command: td-agent-gem install fluent-plugin-bigquery

    04-command:
        command: /etc/init.d/td-agent restart
```

以上でeb deployを行えば、BigQueryにログが入るようになるはずです。


# おわりに

Elastic Beanstalk便利ですよね。

ただ、あくまでPaaSなので少し凝ったことをやろうとすると大変な部分はあるので、こういった情報を増やしていけたらと思っています。

この記事ではBigQueryにログを送信しただけですが、あとはRe:dashなどで可視化をすれば初期の分析環境としては十分だと思います。

ちなみに、BigQueryは基本かなり安いですが、使い方を間違えると150万円を溶かした顔をすることになるので気をつけていただければと思います。

http://qiita.com/itkr/items/745d54c781badc148bb9