MongoDBからElasticSearchへデータを流し込んでKibanaで可視化 with docker compose



# やりたいこと
mongodbに随時アップデートされるデータを、リアルタイムでElasticSearchに流し込みつつ、Kibanaで可視化する。
これらのコンポーネントはコンテナとして稼働させる。
MongoDBは別の機会に作成したので、ここでは既存となる。(そのため少し古い)
また別の用途でも使っているので、ネットワーク的にまとめずに、今回の環境から既存ネットワークにも足をだす。
※docker上では基本的に異なるネットワーク間では通信できない。

# 前提条件
既にある環境として、
Docker Host: coreos
CoreOS: Container Linux by CoreOS 1911.5.0 (Rhyolite)
Docker: 18.06.1-ce, build e68fc7a
MongoDB: 3.6.1

新規に作成するコンポーネントは、
ElasticSearch: 6.5.4(OSS)
Kibana: 6.5.4(OSS)
CentOS: 7.6.1810 (Core)

CentOSはmongo-connectorを起動させておくために使用。

## MongoDBについて
mongo-connectorで差分データを流し込むため、クラスタ構成にしてReplicationさせている。
作成した際に使用した(と思われる)コマンドは以下。

```shell-session
$ docker network create my-mongo-cluster
$ docker run -p 30001:27017 --name mongo1 --net my-mongo-cluster -d -v /home/docker/mongodb-volume1:/data/db mongo:3.6.1-jessie mongod --replSet my-mongo-set
$ docker run -p 30002:27017 --name mongo2 --net my-mongo-cluster -d -v /home/docker/mongodb-volume2:/data/db mongo:3.6.1-jessie mongod --replSet my-mongo-set
$ docker run -p 30003:27017 --name mongo3 --net my-mongo-cluster -d -v /home/docker/mongodb-volume3:/data/db mongo:3.6.1-jessie mongod --replSet my-mongo-set

$ docker exec -it mongo1 mongo

> config = {
  	"_id" : "my-mongo-set",
  	"members" : [
  		{
  			"_id" : 0,
  			"host" : "mongo1:27017"
  		},
      {
  			"_id" : 1,
  			"host" : "mongo2:27017"
  		},
  		{
  			"_id" : 2,
  			"host" : "mongo3:27017"
  		}
  	]
  }

> rs.initiate(config)
```

[参考](https://www.sohamkamani.com/blog/2016/06/30/docker-mongo-replica-set/)

# 環境作成
前回はバラバラに作成したため、今回はdocker-composeを使用する。

## coreosへのdocker-composeインストール

[こちら](https://gist.github.com/sourcec0de/5cf7d36a5f696c2ffc68)を参考にしました。

```shell-session
sudo su -
mkdir -p /opt/bin
curl -L "https://github.com/docker/compose/releases/download/1.19.0/docker-compose-$(uname -s)-$(uname -m)" -o /opt/bin/docker-compose
chmod +x /opt/bin/docker-compose
exit
```

## コンポーネント作成
以下のようなディレクトリ構成にする。
timerecord/
    - docker-compose.yml
    - elasticsearch-kuromoji/
        - Dockerfile
    - mongo-connector/
        - Dockerfile

### mongo-connector
まずmongo-connector用のDockerfileは以下の通り。

```txt:Dockerfile
FROM centos:7.6.1810
LABEL maintainer="ikedak2"

RUN yum update -y && \
    yum clean all

RUN yum install -y epel-release && \
    yum clean all

RUN yum install -y https://centos7.iuscommunity.org/ius-release.rpm && \
    yum clean all

RUN yum install -y python36u python36u-devel python36u-pip \
  && yum clean all \
  && ln -sf /usr/bin/python3.6 /usr/bin/python3 \
  && ln -sf /usr/bin/pip3.6 /usr/bin/pip3

RUN pip3 install --upgrade pip

RUN pip3 install mongo-connector[elastic5]
```

#### Tip1
mongo-connectorを作成する際に、最初下記のDockerfileで作成すると、MongoDB内の日本語が読み取れずエラーになった。pythonが2.7系だったのがまずかったのか、前項の通りpython3を入れて、3のpipからインストールすると問題なくなった。

```txt:NGだったDockerfile
FROM centos:7
LABEL maintainer="ikedak2"

RUN yum update -y && \
    yum clean all

RUN yum install -y epel-release && \
    yum clean all

RUN yum install -y python-pip && \
    yum clean all

RUN pip install mongo-connector[elastic5]
```

エラーは以下。

```shell-session
core@localhost ~/timerecord $ docker exec -it timerecord_mongo-connector_1 bash
[root@6c516bbbbcae /]#
[root@6c516bbbbcae /]# tail -f mongo-connector.log
2019-01-10 07:07:39,218 [WARNING] mongo_connector.doc_managers.formatters:106 - Invalid value for key: projectname as 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)
2019-01-10 07:07:39,218 [WARNING] mongo_connector.doc_managers.formatters:106 - Invalid value for key: comment as 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
```

#### Tip2
CentOS7のイメージにpython3を入れる、で検索するとpython36uを入れるとなっているケースが多いが、u付きはEPELから提供ではなかったので、python36(uなし)を最初入れたが、そうするとbuild時には失敗しないが、pip3が以下のエラーで失敗する。

```shell-session
/bin/sh: pip3: No such file or directory
```

確認すると、/usr/bin/python3.6や、/usr/lib/python3.6/のようなフォルダが作成されておらず、インストールされていないように見えたので、python36uに変更したところ、問題なく実行できた。


### elasticsearch
基本的にはオフィシャルのイメージをそのまま使用するが、それだと日本語に対応できないらしいので、pluginを1つだけ入れる。

```Dockerfile
FROM docker.elastic.co/elasticsearch/elasticsearch-oss:6.5.4
RUN elasticsearch-plugin install analysis-kuromoji
```

### docker-compose
次に、Kibana,Elasticsearch,mongo-connectorを含むdocker-composeを作成する。
ポイントは、mongo-connectorは既存のネットワーク(my-mongo-cluster)と、新規のネットワーク(timerecord_main)の双方にアクセスできる必要がある点。ここに関して、ymlの記載は色々迷ったが最終的には下記になった。

- 既存のネットワークはexternal: trueをつけたうえで、トップレベルに記載。
- mongo-connectorはnetworksに2つ指定

```yml:docker-compose.yml
version: '3'
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.5.4
    container_name: elasticsearch654
    ports:
      - "9200:9200"
      - "9300:9300"
    networks:
      - main
    environment:
      discovery.type: single-node
      ES_JAVA_OPTS: "-Xms512m -Xmx512m"
    volumes:
      - esdata1:/usr/share/elasticsearch/data

  kibana:
    image: docker.elastic.co/kibana/kibana-oss:6.5.4
    container_name: kibana654
    ports:
      - "5601:5601"
    depends_on:
      - elasticsearch
    networks:
      - main
    environment:
      ELASTICSEARCH_URL: "http://elasticsearch:9200"

  mongo-connector:
    build: mongo-connector
    depends_on:
      - elasticsearch
    command: mongo-connector -m mongo1 -t elasticsearch:9200 -d elastic2_doc_manager --continue-on-error --auto-commit-interval=0
    networks:
      - my-mongo-cluster
      - main

volumes:
  esdata1:
    driver: local

networks:
  main:
  my-mongo-cluster:
    external: true
```

イメージのbuild後、コンテナを起動。

```shell-session
core@localhost ~/timerecord $ docker-compose build
core@localhost ~/timerecord $ docker-compose up -d
```

念のため、コンテナに入って確認すると、mongo-connectorはmongodbとelastichsearchの両方に通信できている。

```shell-session
core@localhost ~/timerecord $ docker exec -it timerecord_mongo-connector_1 bash
[root@fb02d417ee52 /]#
[root@fb02d417ee52 /]# ping mongo1
PING mongo1 (172.18.0.2) 56(84) bytes of data.
64 bytes from mongo1.my-mongo-cluster (172.18.0.2): icmp_seq=1 ttl=64 time=0.129 ms
64 bytes from mongo1.my-mongo-cluster (172.18.0.2): icmp_seq=2 ttl=64 time=0.041 ms
^C
[root@fb02d417ee52 /]# ping elasticsearch
PING elasticsearch (172.21.0.2) 56(84) bytes of data.
64 bytes from elasticsearch654.timerecord_main (172.21.0.2): icmp_seq=1 ttl=64 time=0.043 ms
64 bytes from elasticsearch654.timerecord_main (172.21.0.2): icmp_seq=2 ttl=64 time=0.039 ms
^C
```

# Kibanaでの可視化(さわりだけ)
まずはIndex Patternを作成して、データを取り込む。
![image](https://qiita-image-store.s3.amazonaws.com/0/159980/d6997b00-56d4-2db2-a84c-19f86cce67cd.png)
mongodb内のテーブルの名前で検索して取り込む。
![image](https://qiita-image-store.s3.amazonaws.com/0/159980/1dce1558-9315-baed-01e7-71d19a9793b9.png)
時系列のカラムがあれば、timeとして取り込む。
![image](https://qiita-image-store.s3.amazonaws.com/0/159980/d591a5a2-0965-7bcf-06fc-8c4207d94cfa.png)
これで取り込みは完了。
![image](https://qiita-image-store.s3.amazonaws.com/0/159980/cbd99c68-fb5b-17aa-a65e-9f31dc78ee36.png)

基本的にはここから先は入っているデータに依存するので、割愛。とりあえずデータは可視化できていました。
![image](https://qiita-image-store.s3.amazonaws.com/0/159980/080e50e9-8545-027d-ffd2-02300915af38.png)