IPアドレスをKibana+Elasticsearchでプロットするメモ

#目的
いろんなサーバを運用しているのですが、どこの国からアクセスされてるかをプロットしたい！
(Elasticsearch + Kibana + Python3で)
でもLogstashは使わない（事情があって使えない）・・・という時のメモ（普通ない）

#環境
ubuntu 18.04
Elasticsearch 7.4
Kibana 7.4
※Elasticsearchについてはいろいろ情報があるので、9200ポートで動いてるのが前提で書いてます。
　同じくKibanaは5601で動作中

#使用するモジュールのインストール

```
sudo pip3 install python-geohash
sudo pip3 install geoip2
sudo pip3 install elasticsearch
sudo pip3 install pytz
```

#geoip2用のマップをダウンロード
MAXMINDのページから「GeoLite2 City (MAXMIND DB版)」をダウンロード
[MAXMIND](https://dev.maxmind.com/geoip/geoip2/geolite2/)
※Countryバージョンだと緯度経度が取得できないためCityバージョンを

#Elasticsearchへの投げ込み
ログファイルの例

```
DATE,IP,SRC_PORT,DST_PORT,SIZE,DATA
2019/11/05 19:00:00,1.2.3.4,44455,80,180,474554202F20485454502F312E31・・・
2019/11/05 19:00:00,2.3.4.5,44456,80,180,474554202F20485454502F312E31・・・
・・・
```

このIPを位置情報としてKibanaにプロットしたい・・・

###Indexの作成
Kibana上で位置情報と認識させるには、geohashはタイプを「"type": "geo_point"」として指定する必要があります。
KibanaへアクセスしてDevTool画面で次のクエリを実行します。

```json

PUT geoip_map
{
  "mappings": {
    "properties": {
      "add_time": {
        "type": "date",
        "format": "yyyy/MM/dd HH:mm:ss||yyyy/MM/dd||epoch_millis"
      },
      "data": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "geohash": {
        "type": "geo_point"
      },
      "ip": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "latitude": {
        "type": "float"
      },
      "longitude": {
        "type": "float"
      },
      "src_port": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "dst_port": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "size": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      }
    }
  }
}

```

以上で入れ物は出来上がりました。

###データの投入

```python

import pytz
import geoip2.database
import geohash
from datetime import *
from elasticsearch import Elasticsearch

ELASTIC_HOST = "127.0.0.1"

elasticobj = Elasticsearch(ELASTIC_HOST + ":9200")

with open("logfile.txt","r") as f:
    logdata = f.read()
    loglst = logdata.replace('\r','').split('\n')

reader = geoip2.database.Reader('GeoLite2-City.mmdb')

for line in loglst:
    cols = line.split(',')

    #geoip2を使用して緯度経度等の情報をセット
    resp = reader.city(cols[1])

    #geohashを計算 精度はデフォルトで
    str_geohash = geohash.encode(resp.location.latitude,resp.location.longitude)

    #Elasticsearchへ登録 不要ですがこのコードでは緯度経度も生データを保存してます
    #実際には実際のログデータに合わせてこれらの値は変更して下さい
    elasticobj.index(index="geoip_map",doc_type="_doc",body={"geohash":str_geohash,"latitude":resp.location.latitude,"longitude":resp.location.longitude,"add_time":cols [0].replace("-"," "),"ip":cols [1],"src_port":cols [2],"dst_port":cols [3],"size":cols [4],"data":cols [5]}) 
```
#Kibanaでの描画
Kibanaでgeoip_mapを見れるようにする手順等はさらっと・・・

##indexパターンの作成
Management⇒index patternなどと書かれているところに「geoip*」などと書いてCreateボタンをクリック

日時情報のフィールドが自動的に検出されていると思いますので、add_timeを選択しCreateボタンをクリック

以上で、Discover画面でデータが見れる状態になっています。

##Visualizationの作成
次の画面でMapsをクリック（RegionMapやCoodinateMapでも同じようなことができます）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251555/2cbe1b23-5e40-89d1-3040-0b7bad9664e8.png" width=60%>

Add layerを押して、既にKibana上にあるIndexを使用することが書かれている項目を選択し、
Select Index Patternと書かれているところで、「geoip*」などで作成したパターンを選択。
（位置情報のフィールドを指定する必要がある場合は、geohashフィールドを選択。）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251555/4c8be49f-fe06-4487-4e24-af004266727a.png" width=60%>

こんな感じでプロットができる。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251555/933a1a2c-c982-a9ce-0733-0519a0530810.png" width=60%>

とりあえずは完成！
もっとカッコイイ地図を使ったりもできるらしいので後でやってみよう。