Dockerで、DatabaseからElasticsearchにLogstashを使ってデータを流してみた

こんにちは。
Web・iOSエンジニアの三浦です。

今回は、DatabaseからElasticsearchへ、Logstashを使ってデータを流す処理をDocker上で実現してみました。

ちなみに今回説明するコードは、以下のgithubで公開しています。
https://github.com/takayuki-miura0203/docker-elastic-sample

# 準備
## 環境
Dockerが実行できる環境であれば大丈夫です。

# 実装してみる
## Elasticsearch
Databaseからデータを移す先です。

### Dockerfile
Dockerfileでは、公式のイメージを入れるだけです。

```dockerfile

FROM docker.elastic.co/elasticsearch/elasticsearch:7.3.0
```

### docker-compose.yml
テストのため、シングルノードで実行します。

```yml
elasticsearch:
  build: ./elasticsearch
  container_name: elasticsearch
  environment:
    discovery.type: single-node
  ports:
    - 9200:9200
    - 9300:9300
```

## Kibana
データの確認などをするため、Kibanaも入れます。

### Dockerfile
こちらも、Dockerfileでは公式のイメージを入れるだけです。

```dockerfile
FROM docker.elastic.co/kibana/kibana:7.3.0
```

### docker-compose.yml
Kibanaに関しては、 `docker-compose.yml` でも特別なことはしません。

```yml
kibana:
  build: ./kibana
  container_name: kibana
  ports:
    - 5601:5601
```

## MySQL
Elasticsearchへ移すデータを入れます。

### Dockerfile
今回は初期データを入れたいため、[MySQLが公式で提供しているサンプルデータ](https://dev.mysql.com/doc/index-other.html)である `world.sql` の入った `initdb.d` ディレクトリを、 `/docker-entrypoint-initdb.d` ディレクトリとしてコピーするようにします。
DockerのMySQLは、 `/docker-entrypoint-initdb.d` に入っているsqlファイルを起動時に実行してくれます。

```dockerfile
FROM mysql:8.0.17

COPY ./initdb.d /docker-entrypoint-initdb.d
```

https://github.com/takayuki-miura0203/docker-elastic-sample/tree/master/mysql/initdb.d

### docker-compose.yml
ROOTユーザのパスワードを適当に決め、TZも一応入れておきます。

```yml
mysql:
  build: ./mysql
  container_name: mysql
  environment:
    MYSQL_ROOT_PASSWORD: root
    TZ: 'Asia/Tokyo'
  ports:
    - 3306:3306
    - 33060:33060
```

## Logstash
DatabaseからElasticsearchへデータを移すアプリケーションです。

### Dockerfile
公式イメージの `pull` に加え、データを移す処理を書いた設定ファイルを内包する `pipeline` ディレクトリを所定のディレクトリにコピーします。
この際、PCが重くなる対策として、デフォルトで存在した設定ファイルは削除しておきます。
また今回はDBからデータを取得するため、必要となるJDBCライブラリ（ `mysql-connector-java-8.0.17.jar` ）も公式サイトからダウンロードしておき、イメージにコピーします。
なお、どうやらJDK11への対応の関係でJDBCがうまく動かないようなので、Logstashは最新版ではなく少し昔のものを使っています。

```docker
FROM docker.elastic.co/logstash/logstash:6.8.2

RUN rm -rf /usr/share/logstash/pipeline
COPY pipeline /usr/share/logstash/pipeline
COPY mysql-connector-java-8.0.17.jar /usr/share/logstash/mysql-connector-java-8.0.17.jar
```

### pipeline
データを移す処理に関しては、今回はシンプルにDBの指定テーブルのデータをElasticsearchへ移すだけの処理を書きました。
なおElasticsearchのホストは、Dockerで名前を指定している `elasticsearch` です。

```
input {
  jdbc {
    jdbc_driver_library => "/usr/share/logstash/mysql-connector-java-8.0.17.jar"
    jdbc_driver_class => "com.mysql.cj.jdbc.Driver"
    jdbc_connection_string => "jdbc:mysql://mysql:3306/world"
    jdbc_default_timezone => "Asia/Tokyo"
    jdbc_user => "root"
    jdbc_password => "root"
    jdbc_default_timezone => "Asia/Tokyo"
    statement => "SELECT * FROM city"
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch"]
    index => "city"
  }
}
```

https://github.com/takayuki-miura0203/docker-elastic-sample/tree/master/logstash/pipeline

### docker-compose.yml
`docker-compose.yml` では、特別なことは何もしません。

```yml
logstash:
  build: ./logstash
  container_name: logstash
ports:
  - 5044:5044
  - 9600:9600
```

# 実行！
## コンテナ起動・実行
今回は実行も簡単にできるよう `Makefile` を作ってみました。

```make
.PHONY: start
start:
	docker-compose up --build

.PHONY: prune
prune:
	docker system prune -a --volumes
```

以下からコンテナの起動・処理の実行ができます。

```bash
make start
```

## 結果を見てみる
Kibana上で、以下のようにデータが入っていることが確認できました！

```json
{
  "took" : 836,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 4079,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "city",
        "_type" : "_doc",
        "_id" : "tB1NhmwB5V7ePKeX7Lgo",
        "_score" : 1.0,
        "_source" : {
          "@version" : "1",
          "countrycode" : "BRA",
          "district" : "Rio de Janeiro",
          "@timestamp" : "2019-08-12T14:48:57.725Z",
          "name" : "Macaé",
          "id" : 376,
          "population" : 125597
        }
      },
      {
        "_index" : "city",
        "_type" : "_doc",
        "_id" : "tR1NhmwB5V7ePKeX7Lgp",
        "_score" : 1.0,
        "_source" : {
          "@version" : "1",
          "countrycode" : "BRA",
          "district" : "Minas Gerais",
          "@timestamp" : "2019-08-12T14:48:57.725Z",
          "name" : "Teófilo Otoni",
          "id" : 377,
          "population" : 124489
        }
      },
      {
        "_index" : "city",
        "_type" : "_doc",
        "_id" : "th1NhmwB5V7ePKeX7Lgq",
        "_score" : 1.0,
        "_source" : {
          "@version" : "1",
          "countrycode" : "BRA",
          "district" : "São Paulo",
          "@timestamp" : "2019-08-12T14:48:57.725Z",
          "name" : "Moji-Guaçu",
          "id" : 378,
          "population" : 123782
        }
      },
      {
        "_index" : "city",
        "_type" : "_doc",
        "_id" : "tx1NhmwB5V7ePKeX7Lgq",
        "_score" : 1.0,
        "_source" : {
          "@version" : "1",
          "countrycode" : "BRA",
          "district" : "Tocantins",
          "@timestamp" : "2019-08-12T14:48:57.725Z",
          "name" : "Palmas",
          "id" : 379,
          "population" : 121919
        }
      },
      {
        "_index" : "city",
        "_type" : "_doc",
        "_id" : "uB1NhmwB5V7ePKeX7Lgq",
        "_score" : 1.0,
        "_source" : {
          "@version" : "1",
          "countrycode" : "BRA",
          "district" : "São Paulo",
          "@timestamp" : "2019-08-12T14:48:57.725Z",
          "name" : "Pindamonhangaba",
          "id" : 380,
          "population" : 121904
        }
      }
    ]
  }
}
```

# さいごに
Elasticsearch、Kibana、MySQL、Databaseという、それだけ聞くとなかなかに実装が重そうな内容でしたが、やってみたら案外あっさりできました（typoに気づかず悩んだりはしましたが…w）。
Production用に作ったわけではないので色々と簡素ですが、これなら今後Dockerで色々と遊べそうです。

なおこれを動かすと処理の負荷が結構大きいので、PCが重くなったりなどは注意が必要です。

# 参考文献
以下のサイトを参考にさせていただきました。
ありがとうございました！

https://catalina1344.hatenablog.jp/entry/2019/01/20/170821
https://www.elastic.co/guide/en/elasticsearch/reference/7.3/docker.html#_pulling_the_image
https://www.elastic.co/guide/en/logstash/current/docker.html
https://www.elastic.co/guide/en/kibana/current/docker.html
https://qiita.com/sugikeitter/items/f3b2c57bf8bbdc47a8bc
https://qiita.com/shione/items/dfa956a47b6632d8b3b3
https://qiita.com/astrsk_hori/items/e3d6c237d68be1a6f548
https://budougumi0617.github.io/2018/05/20/create-instant-mysql-by-docker/
https://qiita.com/A-Kira/items/f401aea261693c395966
https://qiita.com/S-T/items/923cf689ee5f44525457
https://qiita.com/NagaokaKenichi/items/ae037963b33a85df33f5
https://github.com/dimMaryanto93/docker-logstash-input-jdbc-plugin-mysql