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


はじめに

こんにちは! Wano株式会社でエンジニアをやっているnariと申します。
Wanoグループアドベントカレンダー初日の担当として書かせていただきます。

今回は、少し前まで着手していた共通検索基盤の構築(WIP)の際に、この記事を参考にElasticsearch/Kibana on AWS ECSを構築しましたが、

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

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

TL;DR

  • ECSでお手軽にELK(Logstashは今回なし。Elasticsearchとkibanaのみ)を動かせるサンプルを作ったのでその構築方法と概要をまとめた
  • その際に5、6系 -> 7系の差異で少し詰まったので、その際の解決法をまとめた
    • Elasticserachをシングルノードで設定したいなら、discovery.type: single-node
    • kibanaはELASTICSEARCH_URLでElasticsearchのURLを設定する  

システム全体像

  • とりあえず、HA(高可用性)な構成を取らずにシングルノードでやっていく形を取っています
  • 弊グループサービス(Tuncore)のアーティスト/リリース/曲名の全文検索機能のデータソースとして想定

設定環境

  • 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に置いておきました。詳しくはそちらを参照ください。

-> % 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)

variables.tf
variable "vpc_id" {
 default = "YOUR_VPC_ID"
}
variable "subnet_id" {
  default = "YOUR_SUBNET_ID"
}

3. ecrリソースのみをデプロイする

- terraform init
- terraform apply --target=module.hoge_test_es_app_ecr

4. Dockerfileのelasticsearchイメージをbuildし、2でデプロイしたecrにpushする

- 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への参照に変更し、その他のリソースをデプロイする

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"
      }
    }
  }
]
- terraform deploy

6. http://${CONTAINER_INSTANCE_PUBLIC_IP}:5601 にアクセス

kibanaのdev-toolでElasticsearchのデータをいじっていく(いちいちCurlせずに、クエリを実行できるのでとても便利)

私のハマりどころとその解決方法

1. Elasticsearch 7系からの必須設定項目の変更

自分が参考にした、ELK on ecsの記事は5、6系対応の記事だったので、今回の設定バージョンの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設定を追加した

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を使用して設定します。

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 さんの「自作キーボードを組み立てた話」です!自作キーボード僕も作ってみたいので読むの楽しみ!!

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

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

参考文献