docker image を使って Elasticsearch & Kibana を実行してみるまで

#### 1.はじめに

Elasticsearch は、分散型の RESTful 検索/分析エンジンです。Kibana は Elasticsearch でインデックスされたデータに、検索や可視化の機能を提供するフロントエンドアプリケーションです。
本格的に使うためには、複数のノードからなる Elasticsearch クラスタの構築が必要となりますが、手元のローカル環境でお試しに実行してみることも可能です。
Kibana と共に構築することで、Elasticsearch に対するクエリを簡単に実行することが出来ます。
今回は、Elasticsearch と Kibana の Docker Image を使用し、ローカル環境で Elasticsearch と Kibana を実行していきます。

#### 2.(参考) 実行環境

##### 2-1. docker と docker-compose がインストールされている

それぞれがインストールされていることが前提になっています。

```bash
$ docker -v
Docker version 19.03.6, build 369ce74a3c
```

```bash
$ docker-compose -v
docker-compose version 1.17.1, build unknown
```

##### 2-2. スペック
メモリはトータルで8GB搭載されているマシンを使用しました。（コンテナを計4つ起動させるため、4GB程度では厳しいかもしれません）

```bash
$ cat /etc/issue
Ubuntu 18.04.2 LTS
```

```bash
$ cat /proc/meminfo | grep Mem
MemTotal:        8168284 kB
MemFree:         6812556 kB
MemAvailable:    7545960 kB
```

#### 3. docker network の作成
専用の docker network を使用し、コンテナに割り当てるIPアドレスを固定させるため、以下のコマンドで定義します。

```bash
$ docker network create spark-nw --subnet=172.30.0.0/16 --gateway=172.30.0.254
```

将来的に Apache spark のアプリケーションと連携させることを想定しているため、spark-nw という名前に個人的にしていますが、実際にところは何でも構わないです。

#### 4. docker-compose の準備
以下のようなファイル(docker-compose.yml)を用意します。
Elastic社は、Elasticsearch や kibana 用の Docker Image を公式に提供してくれていますので、今回はそれを使います。
執筆時点で最新の 7.8.1 を指定していますが、最近の Elasticsearch は非常に頻繁に更新されています。
バージョンが 8.x.x などになると、インタフェースなどの仕様が大きく変更されているかもしれません。

```docker-compose.yml
version: '2'
services:
    elasticsearch1:
        image: docker.elastic.co/elasticsearch/elasticsearch:7.8.1
        hostname: doc-elastic101
        container_name: es01
        environment:
            - cluster.name=es-docker-cluster
            - network.host=0.0.0.0
            - node.name=es01
            - node.master=true
            - node.data=true
            - discovery.seed_hosts=es02,es03
            - cluster.initial_master_nodes=es01,es02,es03
            - bootstrap.memory_lock=true
            - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
        ulimits:
            memlock:
                soft: -1
                hard: -1
        mem_limit: 1g
        ports:
            - "9200:9200/tcp"
        networks:
            spark-nw:
                ipv4_address: 172.30.10.1
        volumes:
            - elasticsearch1-data:/usr/share/elasticsearch/data
        extra_hosts:
            - "doc-elastic102:172.30.10.2"
            - "doc-elastic103:172.30.10.3"
            - "doc-kibana101:172.30.20.1"

    elasticsearch2:
        image: docker.elastic.co/elasticsearch/elasticsearch:7.8.1
        hostname: doc-elastic102
        container_name: es02
        environment:
            - cluster.name=es-docker-cluster
            - network.host=0.0.0.0
            - node.name=es02
            - node.master=true
            - node.data=true
            - discovery.seed_hosts=es01,es03
            - cluster.initial_master_nodes=es01,es02,es03
            - bootstrap.memory_lock=true
            - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
        ulimits:
            memlock:
                soft: -1
                hard: -1
        mem_limit: 1g
        networks:
            spark-nw:
                ipv4_address: 172.30.10.2
        volumes:
            - elasticsearch2-data:/usr/share/elasticsearch/data
        extra_hosts:
            - "doc-elastic101:172.30.10.1"
            - "doc-elastic103:172.30.10.3"
            - "doc-kibana101:172.30.20.1"
    
    elasticsearch3:
        image: docker.elastic.co/elasticsearch/elasticsearch:7.8.1
        hostname: doc-elastic103
        container_name: es03
        environment:
            - cluster.name=es-docker-cluster
            - network.host=0.0.0.0
            - node.name=es03
            - node.master=true
            - node.data=true
            - discovery.seed_hosts=es01,es02
            - cluster.initial_master_nodes=es01,es02,es03
            - bootstrap.memory_lock=true
            - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
        ulimits:
            memlock:
                soft: -1
                hard: -1
        mem_limit: 1g
        networks:
            spark-nw:
                ipv4_address: 172.30.10.3
        volumes:
            - elasticsearch3-data:/usr/share/elasticsearch/data
        extra_hosts:
            - "doc-elastic101:172.30.10.1"
            - "doc-elastic102:172.30.10.2"
            - "doc-kibana101:172.30.20.1"

    kibana:
        image: docker.elastic.co/kibana/kibana:7.8.1
        hostname: doc-kibana101
        container_name: kibana1
        environment:
            SERVER_NAME: "kibana"
            ELASTICSEARCH_HOSTS: "http://doc-elastic101:9200"
            ELASTICSEARCH_REQUESTTIMEOUT: "60000"
        ports:
            - "5601:5601/tcp"
        mem_limit: 1g
        networks:
            spark-nw:
                ipv4_address: 172.30.20.1
        extra_hosts:
            - "doc-elastic101:172.30.10.1"
            - "doc-elastic102:172.30.10.2"
            - "doc-elastic103:172.30.10.3"
        depends_on:
            - elasticsearch1
            - elasticsearch2
            - elasticsearch3

volumes:
    elasticsearch1-data:
        driver: local
    elasticsearch2-data:
        driver: local
    elasticsearch3-data:
        driver: local

networks:
    spark-nw:
        external: true
```
こちらの docker-compose.yml ファイルからは、3つの elasticsearch[1-3] と 1つの kibana コンテナが生成されることになります。
本来、elasticsearch.yml や kibana.yml を設定ファイルとして使用しますが、今回は設定ファイルを用意する手間を省くため、docker-compose 内の **environment から定義** しています。

#### 5. コンテナのビルド・開始
docker-compose.yml が配置されているディレクトリで以下のコマンドを実行します。

```bash
$ docker-compose up --build -d
Pulling elasticsearch1 (docker.elastic.co/elasticsearch/elasticsearch:7.8.1)...
7.8.1: Pulling from elasticsearch/elasticsearch
（省略）
Creating es03 ... 
Creating es01 ... 
Creating es02 ... 
Creating es03
Creating es02
Creating es01 ... done
Creating kibana1 ... 
Creating kibana1 ... done
```

コマンドで確認すると、docker-compose.yml 内で指定した docker image がローカルにダウンロードされたことが確認できます。

```bash
$ docker images
REPOSITORY                                      TAG                 IMAGE ID            CREATED             SIZE
docker.elastic.co/kibana/kibana                 7.8.1               22bc1dd9a48a        3 weeks ago         1.3GB
docker.elastic.co/elasticsearch/elasticsearch   7.8.1               a529963ec236        3 weeks ago         811MB
```

デフォルトでは、kibana の方が elasticsearch の Image よりもサイズが大きいようです。Plugin などを含めるとサイズは大きく変わると思います。

また、それぞれのコンテナ(elasticsearch 3つ、kibana 1つ)が起動されていることが確認できます。

```bash
$ docker ps --format "{{.Names}}"
kibana1
es01
es02
es03
```

Webブラウザもしくは、curl コマンドなどで Elasticsearch にアクセスしてみます。
cluster_name("es-docker-cluster") や、Elasticsearch のバージョン("7.8.1")など、正しく返ってきているようです。

```bash
$ curl http://localhost:9200/
{
  "name" : "es01",
  "cluster_name" : "es-docker-cluster",
  "cluster_uuid" : "FYnfjxEpQ6OFvFc5_NdT5A",
  "version" : {
    "number" : "7.8.1",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "b5ca9c58fb664ca8bf9e4057fc229b3396bf3a89",
    "build_date" : "2020-07-21T16:40:44.668009Z",
    "build_snapshot" : false,
    "lucene_version" : "8.5.1",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}
```

#### 6. Kibana からクエリを実行
##### Dev tools 画面を開く
Web ブラウザから http://localhost:5601 にアクセスすると、Kibana コンソールが起動します。
![kibana001.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63951/40b5a09e-812e-a304-deb4-c46610baf50b.png)
※kibana is not ready yet が表示された場合は、しばらく経ってから再度アクセスしてみます。実行マシンのスペックによって、時間がかかるもしくは起動できない場合があります。

左サイドのメニューから
![kibana002.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63951/f921a0ff-e3fb-638b-790c-c7cbbcfae8f4.png)

"Dev tools" を開きます。
![kibana003.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63951/92f72d73-4694-47d8-6776-d29b718531d9.png)

こちらの画面から Elasticsearch に対して、クエリを実行することが出来ます。
![kibana004.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63951/25826200-11d5-6729-84af-1906913d3f7f.png)

##### Cluster の状態チェック
```bash
GET /_cluster/health
```
![kibana005.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63951/ed9828c9-20fa-d51e-d9f4-20da364ec7ae.png)

"number_of_nodes" : 3 とあるように、Elasticsearch のノードが3つ(Docker container 3つ）存在していることが分かります。

##### node や インデックス情報を取得する _cat API
クエリは次の行以降に続けて書くことができます。また、**?v** を付けることで各情報を意味するヘッダー付きで出力されます。

```bash
GET /_cat/nodes?v
```
![kibana006.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63951/2908e197-123e-a162-e7bf-0af0d10d9a48.png)

es01 が Master として動作していることが分かります。

##### インデックスを調べる
```bash
GET /_cat/indices?v
```

![kibana007.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63951/80970c2e-2f23-26a9-6774-3c08d9783042.png)
この画面上には多くのインデックスが既に存在していますが、実際に初めて起動した段階では、数個のインデックス（Kibana が管理用にデフォルトで作成するもの）のみが存在しているはずです。
インデックス内のドキュメント数とサイズが確認できるので、将来的なクラスタのサイズ設計に役立ちます。

#### 7. さいごに
Elasticsearch はRESTful 検索/分析エンジンと呼ばれるだけあって、様々なAPIが用意されています。クエリは curl コマンドから実行することも可能ですが、**Kibana Dev tool は見易いクエリ実行環境を提供**してくれます。
今回は GET クエリしか実行していませんが、PUT クエリからサンプルデータを投入し、その内容をすぐに GET で取り出すなど可能です。
Kibana はデータの Visualize Tool に特化しているだけでなく、**開発時の補助ツール**として使用することが検討できます。
docker image を使用することで、最短5分ほどで Elasticsearch, Kibana の実行環境まで用意することができます。