Elasticsearch/Kibana on AWS ECSをTerraformで サクッと構築

# はじめに
こんにちは! [Wano株式会社](https://wano.co.jp/)でエンジニアをやっている[nari](https://twitter.com/fukubaka0825)と申します。
Wanoグループアドベントカレンダー初日の担当として書かせていただきます。

今回は、少し前まで着手していた共通検索基盤の構築（WIP)の際に、[この記事](https://medium.com/@devfire/deploying-the-elk-stack-on-amazon-ecs-dd97d671df06)を参考にElasticsearch/Kibana on AWS ECSを構築しましたが、

- **ECSはGUIでの操作がやっぱりしんどい(設定部分が散らばりすぎている）ので、TerraformでIaCした話**
- **ElasticSearch/Kibana 5,6系を対象とした記事だったので、７系で構築する場合のトラブルシューティング**

をまとめていこうと思います。

# TL;DR
- **ECSでお手軽にELK(Logstashは今回なし。Elasticsearchとkibanaのみ)を動かせるサンプルを作ったのでその構築方法と概要をまとめた**
  - [GitHub - fukubaka0825/terraform-elasticsearch-and-kibana-on-ecs: terraform-elasticsearch-and-kibana-on-ecs](https://github.com/fukubaka0825/terraform-elasticsearch-and-kibana-on-ecs)のReadme.mdを読んでもらってもまとまってる
- **その際に5、６系 -> 7系の差異で少し詰まったので、その際の解決法をまとめた**
  - Elasticserachをシングルノードで設定したいなら、`discovery.type: single-node`
  - kibanaは`ELASTICSEARCH_URL`でElasticsearchのURLを設定する
　 

# システム全体像
- とりあえず、HA(高可用性)な構成を取らずにシングルノードでやっていく形を取っています
- 弊グループサービス([Tuncore](https://www.tunecore.co.jp/?via=115&gclid=CjwKCAiA5o3vBRBUEiwA9PVzavWTEtYT976EI8nvPoakRvEo_NusszF90zD4cEF_QOpyTtlwLIzXHhoCT0sQAvD_BwE))のアーティスト/リリース/曲名の全文検索機能のデータソースとして想定

![Untitled Diagram (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321850/b3f6379f-c41d-52e4-4345-adda9e82aa5d.png)


# 設定環境
- kibana:7.4.0
- elasticsearch:7.4.0
- terraform:0.12.6

# なぜ Elasticsearch/Kibana on AWS ECS　なのか
- 今回の要件では、逐次登録されていくアーティスト/曲名をユーザー定義辞書として管理し、検索精度を担保してあげる必要がある.
(ex: 「きゃ」 と検索して、「きゃりーぱみゅぱみゅ」がヒットして欲しくない。)
  - **Amazon Elasticsearch Serviceがユーザー定義辞書の設定ができない**
     - これ、だいぶ前からユーザー待望のアップデートだと思うので、re:inventで発表されたりしないかな
  - **Elastic Cloud(ELASTICSEARCH SERVICE)のユーザー定義辞書の更新も、GUIを通してしかできない**
     - 問い合わせたが、APIやプログラマブルなハックができる仕組みは存在しないという回答があった

- 上記によって、**フルマネージのサービスを使用できなさそうなので、自前でクラスター管理をする必要が出てきた**が、直近のプロジェクトでECSを使っていたのもあって、ECSで構築することにした
  

# Terraformの設定ファイル全容/構築方法/ハマりどころとその解決方法
## Terraformの設定ファイル全容
分量が非常に多いので、 [GitHub - fukubaka0825/terraform-elasticsearch-and-kibana-on-ecs: terraform-elasticsearch-and-kibana-on-ecs](https://github.com/fukubaka0825/terraform-elasticsearch-and-kibana-on-ecs)に置いておきました。詳しくはそちらを参照ください。

```sh
-> % tree -L 2
.
├── README.md
├── elasticsearch
│   ├── Dockerfile
│   ├── dummy.pub
│   ├── elasticsearch.yml
│   ├── es_task_container_definitions.json
│   ├── kibana_task_container_definitions.json
│   ├── main.tf
│   ├── outputs.tf
│   ├── userData.sh
│   └── variables.tf
└── modules
    ├── ecr
    ├── ecs
    ├── iam_role
    └── sg
```

## どう構築し、使用するか
### 1. 上記のサンプルのcloneしてくる
### 2. AWSクレデンシャルを自分のアカウントのものをセットし、variables.tfのnetwork周りの変数を、自身のアカウントのものに変更する(vpc,subnet)

```hcl:variables.tf
variable "vpc_id" {
 default = "YOUR_VPC_ID"
}
variable "subnet_id" {
  default = "YOUR_SUBNET_ID"
}
```
### 3. ecrリソースのみをデプロイする
```sh
- terraform init
- terraform apply --target=module.hoge_test_es_app_ecr
```
### 4. Dockerfileのelasticsearchイメージをbuildし、2でデプロイしたecrにpushする

```sh
- cd ./elasticsearch
- docker build -t es-test:latest .
- docker tag es-test:latest ${YOUR_AWS_ACCOUNT_ID}.dkr.ecr.${YOUR_REGION}.amazonaws.com/es-test:latest
- (aws ecr get-login --region ap-northeast-1)
- docker push ${YOUR_AWS_ACCOUNT_ID}.dkr.ecr.${YOUR_REGION}.amazonaws.com/es-test:latest
```
### 5. es_task_container_definitions.jsonのimageを、ecrのimageへの参照に変更し、その他のリソースをデプロイする
```json:es_task_container_definitions.json
[
  {
    "name": "ec-test",
    "image": "${YOUR_AWS_ACCOUNT_ID}.dkr.ecr.${YOUR_REGION}.amazonaws.com/es-test:latest",
    "cpu": 0,
    "memory": 60000,
    "memoryReservation": 60000,
    "portMappings": [
      {
        "containerPort": 9200,
        "hostPort": 9200,
        "protocol": "tcp"
      },
      {
        "containerPort": 9300,
        "hostPort": 9300,
        "protocol": "tcp"
      }
    ],
    "essential": true,
    "environment": [
      {
        "name": "ES_JAVA_OPTS ",
        "value": "-Xms8g -Xmx8g "
      },
      {
        "name": "REGION",
        "value": "ap-northeast-1"
      }
    ],
    "mountPoints": [
      {
        "sourceVolume": "esdata",
        "containerPath": "/usr/share/elasticsearch/data/"
      }
    ],
    "volumesFrom": [],
    "disableNetworking": false,
    "readonlyRootFilesystem": false,
    "ulimits": [
      {
        "name": "nofile",
        "softLimit": 65536,
        "hardLimit": 65536
      }
    ],
    "logConfiguration": {
      "logDriver": "awslogs",
      "options": {
        "awslogs-group": "/ecs/es-test",
        "awslogs-region": "ap-northeast-1",
        "awslogs-stream-prefix": "ecs"
      }
    }
  }
]
```
```sh
- terraform deploy
```
### 6. http://${CONTAINER_INSTANCE_PUBLIC_IP}:5601 にアクセス
kibanaのdev-toolでElasticsearchのデータをいじっていく(いちいちCurlせずに、クエリを実行できるのでとても便利)
<img width="735" alt="スクリーンショット 2019-12-01 17.29.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321850/5cd4af86-5542-b6d0-378c-c1e17e2020a7.png">

## 私のハマりどころとその解決方法
### 1. Elasticsearch 7系からの必須設定項目の変更
自分が参考にした、[ELK on ecsの記事](https://medium.com/@devfire/deploying-the-elk-stack-on-amazon-ecs-dd97d671df06)は５、６系対応の記事だったので、今回の設定バージョンの7.4.0でその通りの設定でやると以下のようなエラーが出ます。

```
the default discovery settings are unsuitable for production use;
at least one of [discovery.seed_hosts, discovery.seed_providers, cluster.initial_master_nodes] must be configured
```

そこで、今回はシングルノードでやりたかったので次のようにelasticsearch.ymlの、discovery.type設定を追加した

```yml:elasticsearch.yml

cluster.name: "es-test"
bootstrap.memory_lock: false
network.host: 0.0.0.0
//以下を追加
discovery.type: single-node
xpack.security.enabled: false

```

### 2. kibana 7系からの必須パラメタの変更
6.7系までは、kibanaにelasticsearchのurlをenvで設定するのに、`ELASTICSEARCH_URL`を使用していましたが、7系以降は`ELASTICSEARCH_HOSTS`を使用して設定します。

```json:kibana_task_container_definitions.json
[
  {
    "name": "kibana-test",
    "image": "docker.elastic.co/kibana/kibana:7.4.0",
    "cpu": 0,
    "memoryReservation": 1024,
    "portMappings": [
      {
        "containerPort": 5601,
        "hostPort": 5601,
        "protocol": "tcp"
      }
    ],
    "essential": true,
    "environment": [
      {
        "name": "ELASTICSEARCH_HOSTS",
        "value": "http://localhost:9200/"
      }
    ],
    "mountPoints": [],
    "volumesFrom": []
  }
]
```
# 終わりに
現在、大きめなの海外プロジェクトのプライオリティが高まり、こちらのタスクはpendingの状況ですが、少し余裕ができたらまた再開して行きたいです。(ESとマスターデータのsync周り,辞書更新＋index再構築の自動化,検索精度/速度改善まとめ、などなどやって行きたいトピックが盛りだくさん)

**明日の担当は @fujitayy  さんの「自作キーボードを組み立てた話」です!自作キーボード僕も作ってみたいので読むの楽しみ！！**

今回のアドベントカレンダーは、非常に多くの人に記事執筆協力をしていただけるので、多種多様で魅力的なカレンダーになっていて今から読むのが非常に楽しみです。

また、**今回の弊社アドベントカレンダーの参加者は、打ち上げで焼肉が食べられる事になっています**🎉🎉
恵比寿で焼肉をリサーチしよっと。安定のトラジかな？？
みんなで無事完走して、美味しいお肉食べましょう！！

# 参考文献

- [Deploying the ELK stack on AWS ECS, Part 1: Introduction & First Steps](https://medium.com/@devfire/deploying-the-elk-stack-on-amazon-ecs-dd97d671df06)
- [Elasticsearchのkuromojiの検索で重要な辞書（dictionary）と類義語（synonym）の設定 - デベロッパー・コラボ](https://developer-collaboration.com/2019/01/30/elasticsearch-kuro-dictionary-synonym/)
- [Elasticsearch 7.0.0 がリリースされるもアップグレードでハマる – papalagi.org](https://papalagi.org/blog/archives/437)
- [Running Kibana on Docker \| Kibana Guide [7.4] | Elastic](https://www.elastic.co/guide/en/kibana/current/docker.html)
- [elasticsearch.hosts or elasticsearch.url are not picked up at all in version 6.7.0 · Issue #140 · elastic/kibana-docker · GitHub](https://github.com/elastic/kibana-docker/issues/140)