kibana4によるRailsアプリの可視化 EC2 Amazon Linux

Railsとkibanaで新しい情報がまとまってなかったので、まとめました。

## 出来ること
- Rails logの可視化
  - アプリの速度の分析、高速化などに使える
     - デプロイ時に、各種数値変化をリアルタイムでモニタリングできる（切り戻しの判断）
         - ステータスコードの割合
         - 処理時間の分布
  - クローラーや、PC、スマフォなどのユーザーの動向を分析
  - 特定のユーザーの行動を追うことが出来る（運用保守）

![image](https://qiita-image-store.s3.amazonaws.com/0/31015/d152ea77-8071-3a73-62f5-6b4301deb826.png)

![image](https://qiita-image-store.s3.amazonaws.com/0/31015/665912ee-0300-51bf-f76c-301c832d5a58.png)


## お試しなのでいろいろ妥協している
`kibana 4.4.1`
`elasticsearch 2.2`
ネットワーク、セキュリティーグループの設定は省略。
前提条件として、kibanaとrailsのサーバーは同じネットワークにいて、
private ipでやり取りしていることにする。

##Rails 下準備

```ruby:Gemfile
gem "lograge"        #elastic_search用 log整形
gem "logstash-event" #elastic_serach用ログをカスタマイズ
gem "rack-user_agent" #requestから、os, browserのバージョンが分かる（ログに情報をだしたいために利用）
```

fluentdエージェントにログを直接送ればよかったけど、わからなかったのでログを出力して
fluentdにtailさせることにした。
別途通常のログとは分けて別のログに出力した。

```ruby:config/application.rb
unless Rails.env.test? #テストの時以外を想定
  #fluentdがtailする用のログを設定
  path                                   = "#{Rails.root}/log/lograge_#{Rails.env}.log"
  #100Mごとに捨てる（ローテート設定）
  config.lograge.logger                  = ActiveSupport::Logger.new(path, 1, 100.megabytes)
  config.lograge.enabled                 = true
  config.lograge.keep_original_rails_log = true
  config.lograge.formatter               = Lograge::Formatters::Logstash.new

  config.lograge.custom_options = lambda do |event|
    opts = { host:            event.payload[:host],
             user_id:         event.payload[:user_id],
             remote_ip:       event.payload[:remote_ip],
             user_agent:      event.payload[:user_agent],
             os:              event.payload[:os],
             os_version:      event.payload[:os_version],
             browser:         event.payload[:browser],
             browser_version: event.payload[:browser_version],
             es:              event.payload[:elasticsearch_runtime],
             category:        event.payload[:category_name], #アプリの出したい情報など
             time:            event.time }

    #error時のバックトレースなんかも見れたら保守が便利なので出す
    if stacktrace = event.payload[:stacktrace] 
      opts[:stacktrace]    = stacktrace
      opts[:error]         = event.payload[:error]
      opts[:error_message] = event.payload[:error_message]
    end
    opts
  end
end

```

```ruby:app/controller/application_controller.rb
protect_from_forgery #←この記述はもともとあるはず、アプリの要件に合わせてオプションを記述、この記述がないとログが出力されない（csrf関連のrailsの設定）

def append_info_to_payload(payload)
  super
  payload[:host]                = request.host
  payload[:user_id]             = current_user.try(:id).to_s
  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[:category_name]       = params.try(:[], :category_name) #アプリの出したい情報
end

```



stacktraceを出力するためにモンキーパッチ
rails 4.2.5の時のパッチなので、別のバージョンの時は要確認
`actionpack-4.2.5/lib/action_controller/metal/instrumentation.rb`の
response.statusを代入している所の例外処理の所を修正して、情報を追加している。

また、devlpment環境で、Railsのバージョンがあがったら、コケるようにしておく。
(バージョンアップ時に、ここだけ古い状態が続くと、アプリの動作がおかしくなるため)

```ruby:config/initializer/kibana_log_stacktrace_patch.rb
module ActionController
  module Instrumentation
    def process_action(*args)
      if Rails.env.development? && Rails.version != "4.2.5"
        raise "#２０年後の僕へ、4.2.5の時のgemの内容をコピーして持ってきています.バージョンがあがったら確認して, 変更があればコピーして下さい。actionpack-4.2.5/lib/action_controller/metal/instrumentation.rb"
      end
      raw_payload = {
        controller: self.class.name,
        action: self.action_name,
        params: request.filtered_parameters,
        format: request.format.try(:ref),
        method: request.request_method,
        path: (request.fullpath rescue "unknown")
      }

      ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup)

      ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
        begin
          result = super
          payload[:status] = response.status
          result
        rescue => e
          #kibanaでstack_traceを見るために拡張している、他はそのまま
          payload[:error]         = e.class.name
          payload[:error_message] = e.message
          payload[:stacktrace]    = e.backtrace.join("\n\t")
          raise
        ensure
          append_info_to_payload(payload)
        end
      end
    end
  end
end

```

rails アプリを動かせばログが出力されるはず。
jqを使ってtailして確認

```bash:console
tail -f log/lograge_development.log | jq "."
```

##AWS 下準備(elasticsearch kibanaのEC2)
インスタンス作成 amazone linux
[仮想化されたストレージ（AmazonのElastic Block Storageなど）に注意：仮想化されたストレージはElasticsearchで十分に動作します。また、十分早く簡単に用意できることから魅力的です。しかし、残念なことに、ローカルストレージと比較すると本質的に遅いです。](http://blog.johtani.info/blog/2014/09/09/performance-considerations-for-elasticsearch-indexing/)

よって、rootのストレージを拡張する。
とりあえず適当にrootのストレージを20GBにしてすすめた。

```
$ sudo yum update -y
```

日本時間に設定

```
$ sudo vim /etc/sysconfig/clock
```

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

```
$ sudo ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
$ sudo reboot
```

elasticsearch2.2 インストール

```
sudo yum -y install java-1.8.0-openjdk-devel.x86_64
sudo rpm --import https://packages.elastic.co/GPG-KEY-elasticsearch
sudo vim /etc/yum.repos.d/elasticsearch.repo
```

```config:/etc/yum.repos.d/elasticsearch.repo
[elasticsearch-2.x]
name=Elasticsearch repository for 2.x packages
baseurl=http://packages.elastic.co/elasticsearch/2.x/centos
gpgcheck=1
gpgkey=http://packages.elastic.co/GPG-KEY-elasticsearch
enabled=1
```

```
sudo yum install elasticsearch -y
sudo vim /etc/elasticsearch/elasticsearch.yml
```

この設定しないと外のネットワークからの接続ができない

```config:elasticsearch.yml
cluster.name: kibana_cluster #適当に名前つけとく
network.host: xxxx.xxxx.xxx.xxx #elasticsearchサーバーのip
http.port: 9200
```


起動

```
sudo /etc/init.d/elasticsearch start
```

確認

```
curl localhost:9200
```

pluginのinstall(好み)

```
sudo /usr/share/elasticsearch/bin/plugin install analysis-kuromoji
sudo /usr/share/elasticsearch/bin/plugin install mobz/elasticsearch-head
sudo /usr/share/elasticsearch/bin/plugin install royrusso/elasticsearch-HQ
sudo /etc/init.d/elasticsearch restart
```

kibana4.4.1 install

```
curl -L -O https://download.elastic.co/kibana/kibana/kibana-4.4.1-linux-x64.tar.gz
tar xzvf kibana-4.4.1-linux-x64.tar.gz
vim kibana-4.4.1-linux-x64/config/kibana.yml
```

![image](https://qiita-image-store.s3.amazonaws.com/0/31015/33ddd598-650c-744d-0d38-c090101c6413.png)


動作確認

```
./kibana-4.4.1-linux-x64/bin/kibana
```

![image](https://qiita-image-store.s3.amazonaws.com/0/31015/a0ad14eb-acaf-46a5-b584-e303acc54b5a.png)


バックグラウンド起動（Nginx噛ませてやるのがめんどくさかったのでとりあえず妥協）

```
sudo ./kibana-4.4.1-linux-x64/bin/kibana  > ./kibana.log &
```
kibanaの動作確認
http://xxxx.xxxx.xxxx.xxxx:5601/


## Railsが動いているEC2のfluentdの準備

fluentd install

```
sudo vi /etc/yum.repos.d/treasuredata.repo
```

```config:/etc/yum.repos.d/tresuredata.repo
[treasuredata]
name=TreasureData
baseurl=http://packages.treasure-data.com/redhat/$basearch
gpgcheck=0
```

```
sudo yum install td-agent -y
sudo chkconfig td-agent on
```

```
sudo /usr/lib64/fluent/ruby/bin/fluent-gem install --no-ri --no-rdoc fluent-plugin-elasticsearch
```

```
sudo vim /etc/td-agent/td-agent.conf
```

```xml:td-agent.conf
<source>
  type     tail
  format   json
  tag      rails
  path     /path/to/log/lograge_production.log
  pos_file /path/to/log/lograge_production.log.pos
  time_key time
</source>

<match rails>
  type elasticsearch
  retry_limit 5
  host xxxx.xxxx.xxxx.xxxx #elasticsearch ip
  port 9200
  logstash_format true
  logstash_prefix lograge
  flush_interval  1s
</match>
```

権限周りで起動しても落ちるため、fluentdをroot権限で実行するよう変更

```
sudo vim /etc/init.d/td-agent
```

```diff:/etc/init.d/td-agent
- DAEMON_ARGS=${DAEMON_ARGS---user td-agent}
+ DAEMON_ARGS=${DAEMON_ARGS---user root}
TD_AGENT_ARGS="${TD_AGENT_ARGS-/usr/sbin/td-agent --user root --group td-agent --log /var/log/td-agent/td-agent.log}"
```

起動

```
sudo /etc/init.d/td-agent start
# 必ずstatusで起動状態を確認する
sudo /etc/init.d/td-agent status
```

status確認でコケたらログをみてみる

```
less /var/log/td-agent/td-agent.log
```


ここまでできたら、Railsが動けばelasticsearchにログが貯まり、
kibanaで見れるようになっているはず。

![image](https://qiita-image-store.s3.amazonaws.com/0/31015/1840873e-019b-fd65-1d35-68e36eb6a951.png)


## 参考URL・メモ
[logstash-logger使えばtailしなくても行けそうかもしれない](https://github.com/dwbutler/logstash-logger)
[stacktraceの部分の参考Gist](https://gist.github.com/bf4/5911868)
[Qiita:Logging of Rails in Lograge](http://qiita.com/ogomr/items/fd47dbafc8d9d3dfba00)