アクセス元の地域をKibanaで可視化する

ちょっとした内容をアクセスログから見たいなと思った時に
試した事があったので、それについて書こうと思います

# 検証したきっかけ
リリース直後や機能のアップデート後、はたまたアクセスが急増した時などに
アクセスログを見ることがよくあると思います。
ただ、上記要件以外でもカジュアルに
あれ、この地域からのアクセスが意外に多い等、
コーヒー飲みながら、サービス展開に思いを馳せたりしたいな、と思ったのがきっかけです。

今回はKibanaのvisualizeを使ってログを見ていきたいと思います。

# やる事
- Kibanaでどのあたりの地域からアクセスがあるのかを見たい
- Elasticsearchを使う
- ログファイルだけあれば解析できるように、embulkでElasticsearchで投入する
- どこでも気軽に試せるようにdockerコンテナで処理する
- 今回のエントリー用にログデータはダミーデータを利用する
- Elasticsearchに入れるデータはIPと位置情報のみ

# 今回の構成

```
- access_kibana
  - docker
    - elasticsearch
      - config
        schema.json
      Dockerfile 
    - embulk
      - config
        embulk_config.yml 
      Dockerfile 
    - kibana
      Dockerfile 
    docker-compose.yml
  - log
  Gemfile
  sample_log.rb     
 ```

# dockerコンテナで環境を用意する
#### Dockerfileの準備
- elasticsearch

```
FROM elasticsearch:5.6.5

# x-pack
RUN elasticsearch-plugin  install --batch x-pack

# kuromoji
RUN elasticsearch-plugin  install analysis-kuromoji
```
- kibana

```
FROM kibana:5.6.5

# x-pack
RUN kibana-plugin install x-pack
```

- embulk　

こちらはDockerhubに上がっているものを利用します。
[embulk Dockerfile](https://hub.docker.com/r/reflet/docker-embulk/~/dockerfile/)

#### docker-compose
docker-composeを準備してまとめて管理します

```
version: '2'
services:
  elasticsearch:
    build: elasticsearch
    volumes:
        - es-data:/usr/share/elasticsearch/data
        - ./elasticsearch/config:/usr/share/elasticsearch/config
    ports:
        - 9200:9200
    expose:
        - 9300
    ulimits:
        nofile:
            soft: 65536
            hard: 65536
    network_mode: "bridge"
  kibana:
    build: kibana
    links:
        - elasticsearch:elasticsearch
    ports:
        - 5601:5601
    network_mode: "bridge"
  embulk:
    build: embulk
    links:
        - elasticsearch:elasticsearch
    volumes:
        - ./embulk/config:/opt/config
        - ../log:/opt/log
    network_mode: "bridge"
volumes:
    es-data:
        driver: local
networks:
  default:
    external:
      name: bridge

```

dockerコンテナを起動

```
cd docker
docker-compose up -d
```

# アクセスログを作る apache-loggen
今回は apache-loggen を使ってダミーのアクセスログを作ります

```
gem install apache-loggen --no-ri --no-rdoc -V
apache-loggen --rate=10 --limit=10 > opt/log
```
# ログに位置情報を追加する
IPから位置情報を推測するようにします

- gem

```
# Gemfileに追加
gem 'maxminddb'
```
- gem install

```
bundle install --path vdendor/bundle
```
- 位置情報の取得
位置情報には[MaxMind](http://dev.maxmind.com/)のオープンソースデータを利用します
[GeoLite2-City](http://dev.maxmind.com/geoip/geoip2/geolite2/) をダウンロードする

- ruby
IPと位置情報を紐付けデータを再出力します

```
require 'MaxMindDB'

db = MaxMindDB.new('GeoLite2-City.mmdb')
readFile = "log/orig.log"
outFile  = "log/geoip.log"
outIp = []
begin
  File.open(readFile){|file|
    file.each_line{|access|
      access.match(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/){|md|
        ip  = md[0]
        ret = db.lookup(ip)
        str = "#{ip},¥"#{ret.location.latitude},#{ret.location.longitude}¥""
        outIp << str
      }
    }
  }
# 例外は小さい単位で捕捉する
rescue SystemCallError => e
  puts %Q(class=[#{e.class}] message=[#{e.message}])
rescue IOError => e
  puts %Q(class=[#{e.class}] message=[#{e.message}])
end

f = File.open(outFile, "w")
outIp.each { |s| f.puts(s) }

```


# Elasticsearchの初期化
何もtemplateを指定しないと、geo_pointを認識してくれないので
明示的に型を定義します。
今回は、ipと位置情報のみを登録するようにします。

docker-composeのvolumeで指定した [./elasticsearch/config]配下に
設定ファイルを準備する

```
/* schema.json */
{
  "mappings": {
      "geo_ips": {
        "properties": {
          "ip": {
            "type": "string",
            "index" : "not_analyzed"
          },
          "location": {
            "type": "geo_point"
          }
        }
      }
    }
  }
}
```

- 作ったtemplateを元に indexの作成
 
```
curl -XPUT localhost:9200/geo_ips --data-binary @schema.json
```

# embulk_configの作成
CSV情報をElasticsearchに取り込むように設定ファイルを作成します。

- embulk_config.yml

```
in:
  type: file
  path_prefix: /opt/log/geoip
  parser:
    charset: UTF-8
    newline: CRLF
    type: csv
    delimiter: ','
    quote: '"'
    escape: ''
    header_line: true
    columns:
    - {name: "ip", type: string}
    - {name: "location", type: string}
out:
  type: elasticsearch
  mode: insert
  nodes:
    - {host: elasticsearch, port: 9200}
  index: geo_ips
  index_type: geo_ips
```

# Elasticsearch実行 
dockerコンテナ上で実行します

```
embulk run embulk_config.yml 
```


# Kibanaで確認

```
localhost:5601/
```
![スクリーンショット 2017-12-14 2.45.25.png](https://qiita-image-store.s3.amazonaws.com/0/103324/2a6e02de-6a57-9f3a-5aa2-5aca2a662619.png)
登録したデータが地図にプロットされました。


# まとめ
fuluentdやLogstashを導入する場合はindexのtemplateだけしっかりしておけば、
そのまま表示できると思います。
ただサーバーになかなか入れらなかったり、アクセスログだけからの調査や確認をする場合は
割と便利だなと思いました。
もう少しtemplateを実用化してレガシーシステムのログも集約していければと思います。