Elastic StackをDocker上で実行してログモニタリングサービスを作成するまで① - Elasticsearch編

## 背景
業務でElasticsearchを可視化するKibanaというツールを利用してログをモニタリングしているのですが、裏でどのような技術がどのように使われているのか理解していなかったためプロトタイプを実際に開発してみることで理解を深めようという目的で作ってみました。
意外と自分が欲しかった以下の２点が明瞭に記載された情報を探すのに苦労したので今回投稿させていただきます。

- Spring bootアプリケーションのログをKibanaで可視化させるまでの一連の流れ
- Docker環境でfilebeatを用いたログの具体的な収集方法

とりあえず完成品使ってすぐ動かしてみたいという方はこちらの記事をどうぞ。
[Elastic StackをDocker上で実行してログモニタリングサービスを作成するまで - デモ編](https://qiita.com/sgr0416st/items/6409fb84f5463cd2112a)

## できること
この投稿は全４回に渡ってシリーズ化する予定です！
ほかの記事もよろしければぜひご覧ください。

1. **[Elasticsearch編](https://qiita.com/sgr0416st/items/d0d00723b49c3935137e): ElasticsearchをDocker上で起動して、実際に動かしてみる**
2. [Kibana編](https://qiita.com/sgr0416st/items/28dc2df02bb946f94209): KibanaをDocker上で起動して、データを可視化してみる
3. [Filebeat編](https://qiita.com/sgr0416st/items/74723ba2bb5667f5411a): Filebeatを利用して、ログファイルをKibanaで表示してみる
4. [Spring Boot編](https://qiita.com/sgr0416st/items/3bb415783f1464aa7df6): 簡単なSpring Webアプリケーションをローカルで実行、ログを作成してそれをKibanaで可視化する

[デモ編](https://qiita.com/sgr0416st/items/6409fb84f5463cd2112a): とりあえず完成品を動かしてみる

## Elasticsearchとは
https://www.elastic.co/jp/what-is/elasticsearch
> Elasticsearchは、分散型で無料かつオープンな検索・分析エンジンです。テキスト、数値、地理空間情報を含むあらゆる種類のデータに、そして構造化データと非構造化データの双方に対応しています。Apache Luceneをベースに開発されたElasticsearchは、2010年にElasticsearch N.V.（Elasticの前身となる企業）がはじめてリリースしました。シンプルなREST APIや分散設計、スピードとスケールの優位性で広く浸透したElasticsearchは、現在もElastic Stackの中核となるプロダクトです。Elastic Stackはデータ投入からエンリッチメント、保管、分析、可視化までを実現する無料かつオープンなツール群です。Elasticsearch、Logstash、Kibanaの頭文字をとった"ELK Stack"の愛称でも知られています。Elasticsearchにデータを送る軽量なシッピングエージェント、Beatsも加わったことでElastic Stackになりました。

今回の目的に限って言えば、ざっくりデータを保存・検索・分析する、Elastic Stackのコアサービスだと思ってもらえれば大丈夫です。

## 手順
それでは早速ElasticsearchをDocker上で起動していきます。
今回は最終的に、複数のサービスを同時に動かしていくため、docker-composeを利用して開発していきます。

#### 前提
- docker, docker-composeがインストール済み 
- curlコマンドがインストール済み

１. **ファイルの準備**   
まず、元となるプロジェクトフォルダを作成し以下の様なフォルダ構成にします。プロジェクト名は今回はelastic-docker-demoとしましたが、変えていただいても構いません。elasticsearch.yml, docker-compose.ymlは空でオッケーです。
   
- **/elastic-docker-demo/**
    - **elasticsearch/**
        - **elasticsearch.yml**
    - **docker-compose.yml**

![create elastic structure.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271486/c8f105a4-a35b-8313-5cfd-19fc2825fc57.png)

２. **dockerの準備**
docker-compose.ymlファイルを以下の様に編集します

```docker-compose.yml
version: '3.2'
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.13.0
    hostname: elastic101
    container_name: cont-elasticsearch
    ports:
      - 9200:9200
    mem_limit: 1g
    volumes:
      - type: bind
        source: ./elasticsearch/elasticsearch.yml
        target: /usr/share/elasticsearch/config/elasticsearch.yml
        read_only: true
      - type: volume
        source: elasticsearch-data
        target: /usr/share/elasticsearch/data

volumes:
    elasticsearch-data:
        driver: local
```

ポイントとなる点を軽く説明していきます。

- imageを指定して、どのイメージからコンテナを作成するか決定しています。
- hostnameを指定することで、ほかの場所からもホスト名でこのコンテナにアクセスできるようになります。
- volumesを設定することで、以下の２つを実現しています。
    - ホストに存在するelasicsearchの設定ファイルを共有
    - elasticsearchコンテナ内のデータを永続化、コンテナ起動時に以前のデータ存在していたら再利用

volumeについてより詳しく知りたい方はこちらをどうぞ。(https://y-u-y-a-blog.com/tech/docker/set-up-myself-volume/)

３. **elasticsearch の設定ファイルの準備**
elasticsearch.yml を以下の様に編集します

```elasticsearch.yml
cluster.name: logging-cluster # default value: elasticsearch
node.name: logging-node # default: the hostname of the machine
network.host: elastic101
discovery.seed_hosts: 
  - elastic101
cluster.initial_master_nodes: 
  - logging-node
``` 
Elasticsearchの各ノードごとにこのような設定ファイルを用意できますが、今回は１つのノードのみで動かすため１つだけで大丈夫です。
cluster.name, node.name は自由に設定し、cluster.initial_master_nodesに自身のノードネームを設定してください。
network.host, discovery.seed_hostsにはdocker-compose.ymlで設定したelasticsearchのhostnameを指定してください。

これはElasticsearchを試運転する上で必要最低限の設定しかしていません。
本番環境で運用するにはセキュリティの設定や複数ノードでの設定等、さらに込み入った設定が必要になります。
気になる方は公式のドキュメントをご覧いただくと良いかと思います。
https://www.elastic.co/guide/en/elasticsearch/reference/7.13/security-minimal-setup.html
https://www.elastic.co/guide/en/elasticsearch/reference/current/important-settings.html

おめでとうございます！！
これで設定はとりあえず完了したので、次のセクションで実際にうまく起動しているか確認していきましょう。

４. **実行されているか確認する**
以下のコマンドを実行したあと、Elasticsearch (http://localhost:9200) にアクセスしてみてください。(起動まで1,2分かかります)

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

以下のようなJSONが返ってきたら成功です。

```
{
  "name" : "logging-node",
  "cluster_name" : "logging-cluster",
  "cluster_uuid" : "LSQzRQSZRPSuwLc9e0yW0Q",
  "version" : {
    "number" : "7.13.0",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "5ca8591c6fcdb1260ce95b08a8e023559635c6f3",
    "build_date" : "2021-05-19T22:22:26.081971330Z",
    "build_snapshot" : false,
    "lucene_version" : "8.8.2",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}
```

***Tips***: アクセスに失敗した時は以下のコマンドから起動が成功していそうか、ログを確認してみましょう。
現在のコンテナの状況がわかる手助けになるはずです。

```
docker-compose logs -f
```

- ログが現在進行形で出ている --> 起動の途中かも、少し待ってリトライする
- コンテナが全く起動してない・すぐに終了している --> Dockerの設定ミスの可能性あり
- 起動はしているがエラーが出る・ネットワークにアクセス出来ない --> elasticserachの設定ミスの可能性あり

５. **実際に動かしてみる**
続いて、実際にデータを入れてもう少し動かしてみましょう。
ElasticsearchはRest APIを提供しており、GET, POSTなどのHTTPリクエストにより通信が可能になっています。
[公式のチュートリアル](https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started.html)を参考に、まずはデータが入っていない状態でElasticsearch内を検索してみます。
以下のコマンドはElasticsearch内にある"logs-my_app-default"というインデックス内に存在するデータを検索するリクエストです。

```shell
curl -X GET "localhost:9200/logs-my_app-default/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match_all": { }
  },
  "sort": [
    {
      "@timestamp": "desc"
    }
  ]
}
'
```
当たり前ですがlogs-my_app-defaultというインデックスを登録していないため、データ(index)が見つからないという趣旨のエラーメッセージ(index_not_found_exception)が返ってきますね。
では早速データを追加してみましょう。
以下のリクエストは
"192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736"
というeventをlogs-my_app-defaultというインデックスに保存するリクエストです。このインデックスは今存在していないため、自動的に作成されます。

```shell
curl -X POST "localhost:9200/logs-my_app-default/_doc?pretty" -H 'Content-Type: application/json' -d'
{
  "@timestamp": "2099-05-06T16:21:15.000Z",
  "event": {
    "original": "192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736"
  }
}
'
```

続いて先ほどエラーが出た検索リクエストをもう一度送ってみましょう。
すると、先ほどとは違い以下の様なデータがきちんと返ってきます。

```json
{
  "took" : 13,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [
      {
        "_index" : ".ds-logs-my_app-default-2021.06.26-000001",
        "_type" : "_doc",
        "_id" : "yhB_R3oB145PadIrNZ_a",
        "_score" : null,
        "_source" : {
          "@timestamp" : "2099-05-06T16:21:15.000Z",
          "event" : {
            "original" : "192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736"
          }
        },
        "sort" : [
          4081767675000
        ]
      }
    ]
  }
}

```

以上でelasticsearchがきちんと動作していることを確認出来たと思います。
最後に以下のコマンドを実行してコンテナを停止させておきましょう。

```
 docker-compose down -v --rmi local
```

続いてKibanaを設定していきましょう！
[Elastic StackをDocker上で実行してログモニタリングサービスを作成するまで② - Kibana編](https://qiita.com/sgr0416st/items/28dc2df02bb946f94209)

## 参考
- Docker and Volume: https://y-u-y-a-blog.com/tech/docker/set-up-myself-volume/
- Elasticsearch command: https://qiita.com/nskydiving/items/1c2dc4e0b9c98d164329
- Elasticsearch Settings: https://www.elastic.co/guide/en/elasticsearch/reference/current/important-settings.html
- Elasticsearch Security: https://www.elastic.co/guide/en/elasticsearch/reference/7.13/security-minimal-setup.html
- Elasticsearch get started: https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started.html