EBに置いたrailsからfluentdでlogをamazonESに投げたメモ

AWS Elastic Beanstalkに設置したrailsから、Amazon Elasticsearch Serviceにfluentdでログを投げつけたときのもろもろをメモ。

## railsをEBに設置

略。

[公式のドキュメント](http://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/create_deploy_Ruby_rails.html)なんかを読みつつ。

環境変数に`SECRET_KEY_BASE`やら`AWS_ACCESS_KEY_ID`や`AWS_SECRET_ACCESS_KEY`を設定するのを忘れずに。

## logrageでjsonのlog吐き出す

https://github.com/roidrage/lograge

http://qiita.com/tos-miyake/items/cafa31049e543dba3049 を参考にしつつ必要な情報をjsonに突っ込むよう設定。

```ruby:Gemfile
gem 'lograge'
gem 'logstash-event'
gem "rack-user_agent"
```

```ruby:app/controllers/application_controller.rb

  def append_info_to_payload(payload)
    super

    payload[:uuid]                = request.uuid
    payload[:host]                = request.host
    payload[:remote_ip]           = request.remote_ip
    payload[:user_agent]          = request.user_agent
    payload[:os]                  = request.os
    payload[:os_version]          = request.os_version
    payload[:browser]             = request.browser
    payload[:browser_version]     = request.browser_version

    payload[:user_id]             = current_user.id.to_s if user_signed_in?
    payload[:administrator_id]    = current_administrator.id.to_s if administrator_signed_in?
  end

```


```ruby:config/initializers/lograge.rb
Rails.application.configure do
  config.lograge.enabled = true
  config.lograge.formatter = Lograge::Formatters::Logstash.new
  config.lograge.logger = ActiveSupport::Logger.new(Rails.root.join("log/lograge_#{Rails.env}.log"), 1, 100.megabytes)
  config.lograge.keep_original_rails_log = true

  config.lograge.custom_options = lambda do |event|
    payload = event.payload

    data = payload.slice(*%i(
      uuid host remote_ip user_agent os os_version browser browser_version
      user_id administrator_id
    ))

    if payload[:exception]
      e = payload[:exception_object]
      data[:exception_class] = e.class.to_s
      data[:exception_message] = e.message
      data[:stacktrace] = "#{e.class}: #{e.message}\n" + (e.backtrace || []).join("\n")
    end

    data
  end
end

```

## Amazon Elesticesearch Serviceを立ち上げる

画面の指示に従ってポチポチ設定。
アクセスポリシーは設定画面で出てくるテンプレートを参考にしつつEBで使用しているIAMユーザーとIP制限とを設定。

参考: http://dev.classmethod.jp/cloud/aws/cm-advent-calendar-2015-getting-started-again-amazon-es/


```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::IAMユーザーのarn"
        ]
      },
      "Action": [
        "es:*"
      ],
      "Resource": "arn:aws:es:ESのarn/*"
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": "es:*",
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": [
            "IPその1",
            "IPその2"
          ]
        }
      },
      "Resource": "arn:aws:es:ESのarn/*"
    }
  ]
}

```


### template設定

ドメインの用意ができたら、elasticsearchデフォのままだとstringが勝手にトークナイズされてしまうので、[Dynamic templates](https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-templates.html)という機能を使うことでごっそりstringは全部解析するのとしないのを用意するようにした。

参考: http://qiita.com/harukasan/items/4ec517d8d96f557367e1

```bash
$ curl -XPUT https://ESのどめいん/_template/template_all -d "`cat template_all.json`"
```

```json:template_all.json
{
    "template": "*",
    "mappings": {
        "_default_": {
            "dynamic_templates": [
                {
                    "strings": {
                        "match_mapping_type": "string",
                        "match": "*",
                        "mapping": {
                            "type": "multi_field",
                            "fields": {
                                "{name}": {
                                    "type": "string",
                                    "index": "analyzed"
                                },
                                "full": {
                                    "type": "string",
                                    "index": "not_analyzed"
                                }
                            }
                        }
                    }
                }
            ]
        }
    }
}
```



## EBにfluentd入れたり設定したり

`.ebextensions`で設定。

[fluentd公式が用意してくれてる設定](https://github.com/treasure-data/elastic-beanstalk-td-agent)をベースにいじる。

アウトプットのpluginはhttps://github.com/uken/fluent-plugin-elasticsearch によると

> Note: For Amazon Elasticsearch Service please consider using [fluent-plugin-aws-elasticsearch-service](https://github.com/atomita/fluent-plugin-aws-elasticsearch-service)

とのことなので、今回は[fluent-plugin-aws-elasticsearch-service](https://github.com/atomita/fluent-plugin-aws-elasticsearch-service)を使用。

参考: http://qiita.com/k-nishigaki/items/d6b4d39391914ca4a278

```yaml:.ebextensions/40_td-agent-gen-config.config
files:
  "/etc/td-agent/td-agent.conf":
    owner: root
    group: root
    content: |
      <source>
        @type     tail
        format    json
        tag       rails.lograge
        path      "/var/app/current/log/lograge_#{ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'unknown'}.log"
        pos_file  "/var/log/td-agent/lograge_#{ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'unknown'}.log.pos"
        time_key  time
      </source>

      <filter **>
        @type stdout
      </filter>

      <match rails.lograge>
        @type aws-elasticsearch-service
        logstash_format true
        logstash_prefix "lograge-#{ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'unknown'}"
        id_key uuid
        include_tag_key true
        flush_interval  1s
        reload_connections false
        reload_on_failure false

        <endpoint>
          url "#{ENV['AWS_ES_URL']}"
          region ap-northeast-1
          access_key_id "#{ENV['AWS_ACCESS_KEY_ID']}"
          secret_access_key "#{ENV['AWS_SECRET_KEY']}"
        </endpoint>
      </match>
      EOS

```


```yaml:.ebextensions/41_td-agent-install.config
commands:
  01-command:
    command: grep '^Defaults:root    !requiretty' /etc/sudoers || 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: /usr/sbin/td-agent-gem install fluent-plugin-aws-elasticsearch-service

container_commands: # confにENVが必要なので
  01-command:
    command: /etc/init.d/td-agent restart

```

`td-agent.conf`で環境変数使ってるので、`commands`ではなく`container_commands`でrestartさせてる。
あと、EBにてamazonESのエンドポイントを`AWS_ES_URL`環境変数に設定してる。



## kibanaで見たり

amazonESでドメイン用意した時点で一緒に用意されているkibanaで、indexパターンを`td-agent.conf`で設定したもの(`lograge-production-*`的な)で設定して色々さわったり見たり。