Dockerを使用してマルチノードでOpen Distro for Elasticsearchのローカル開発環境を構築

今回の記事にするOpen Distro for Elasticsearch（以下odfe）は、[Amazon Elasticsearch Service（以下Amazon ES）のアップストリーム](https://aws.amazon.com/jp/elasticsearch-service/faqs#Open_Distro_for_Elasticsearch)です。

将来的にAmazon ESで運用する予定のため開発環境をodfeで構築しています。
マルチノードでodfeの開発環境を構築するときに詰まった箇所があるので、今回はそのことについてまとめていきたいと思います。

## 環境構築
環境構築は基本的にはodfeの公式ドキュメントに準じて行っています。
https://opendistro.github.io/for-elasticsearch-docs/docs/install/docker/

### 詰まった箇所①クラスター内に3つのマスター適格ノードを含める
ですが、公式ドキュメントではクラスターを2ノードで構成しており、ドキュメントに沿って2ノードで作成することは耐障害性の面で非推奨です。
Elastic社から以下のように説明されています。

>通常は、クラスター内に3つのマスター適格ノードを含めることが推奨されます。ノードの1つに障害が発生しても、その他の2つで安全にクォーラムを形成し、続行できるからです。クラスター内のマスター適格ノードが2つ以下の場合は、そのいずれの障害にも耐えられません。それとは逆に、クラスター内にマスター適格ノードが4つ以上ある場合は、マスターの選出およびクラスターステータスの更新に時間がかかる可能性があります。（[新世代Elasticsearchクラスターコーディネーション](https://www.elastic.co/jp/blog/a-new-era-for-cluster-coordination-in-elasticsearch)）

なので、今回はクラスターを3つのマスター適格ノードで構成するようdocker-composeファイルに設定を書いています。
参考記事：[Important discovery and cluster formation settings](https://www.elastic.co/guide/en/elasticsearch/reference/7.0/discovery-settings.html#initial_master_nodes)

### 補足
また、バージョン6系まではマスター適格ノードを偶数台にしないよう設定する必要があったのですが、バージョン7系から新たなクラスターコーディネーションの仕組みが導入されて、偶数台構成であってもElasticsearchが自動で調整しスプリットブレインの発生を回避してくれるようになっています。

詳細はElastic Stack実践ガイド（Elasticsearch/Kibana編）筆者のQiitaがとても分かりやすいので、ご覧ください。[「ElasticsearchのMasterノードって偶数台で構成してもいいんですか？」（2020年版）](https://qiita.com/tetsuyasodo/items/3182476f327be6d8cb28)

### dockerファイル
今回作成したDockerファイルとdocker-composeファイルは以下です。

~~~Dockerfile.es
FROM amazon/opendistro-for-elasticsearch:1.11.0

RUN /usr/share/elasticsearch/bin/elasticsearch-plugin install --batch analysis-kuromoji
RUN /usr/share/elasticsearch/bin/elasticsearch-plugin install --batch analysis-icu
~~~


~~~docker-compose.yml
version: "3"
services:
  elasticsearch-node1:
    build:
      context: .
      dockerfile: dockerfiles/Dockerfile.es
    container_name: elasticsearch-node1
    environment:
      - cluster.name=elasticsearch-cluster
      - node.name=elasticsearch-node1
      - discovery.seed_hosts=elasticsearch-node1,elasticsearch-node2,elasticsearch-node3
      - cluster.initial_master_nodes=elasticsearch-node1,elasticsearch-node2,elasticsearch-node3
      - bootstrap.memory_lock=true # along with the memlock settings below, disables swapping
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m" # minimum and maximum Java heap size, recommend setting both to 50% of system RAM
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536 # maximum number of open files for the Elasticsearch user, set to at least 65536 on modern systems
        hard: 65536
    volumes:
      - elasticsearch-data1:/usr/share/elasticsearch/data
    ports:
      - 9200:9200
      - 9600:9600 # required for Performance Analyzer
    networks:
      - elasticsearch-net
  elasticsearch-node2:
    build:
      context: .
      dockerfile: dockerfiles/Dockerfile.es
    container_name: elasticsearch-node2
    environment:
      - cluster.name=elasticsearch-cluster
      - node.name=elasticsearch-node2
      - discovery.seed_hosts=elasticsearch-node1,elasticsearch-node2,elasticsearch-node3
      - cluster.initial_master_nodes=elasticsearch-node1,elasticsearch-node2,elasticsearch-node3
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    volumes:
      - elasticsearch-data2:/usr/share/elasticsearch/data
    networks:
      - elasticsearch-net
  elasticsearch-node3:
    build:
      context: .
      dockerfile: dockerfiles/Dockerfile.es
    container_name: elasticsearch-node3
    environment:
      - cluster.name=elasticsearch-cluster
      - node.name=elasticsearch-node3
      - discovery.seed_hosts=elasticsearch-node1,elasticsearch-node2,elasticsearch-node3
      - cluster.initial_master_nodes=elasticsearch-node1,elasticsearch-node2,elasticsearch-node3
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    volumes:
      - elasticsearch-data3:/usr/share/elasticsearch/data
    networks:
      - elasticsearch-net
  kibana:
    image: amazon/opendistro-for-elasticsearch-kibana:1.11.0
    container_name: elasticsearch-kibana
    ports:
      - 5601:5601
    expose:
      - "5601"
    environment:
      ELASTICSEARCH_URL: https://elasticsearch-node1:9200
      ELASTICSEARCH_HOSTS: https://elasticsearch-node1:9200
    networks:
      - elasticsearch-net

volumes:
  elasticsearch-data1:
  elasticsearch-data2:
  elasticsearch-data3:

networks:
  elasticsearch-net:
~~~

設定値についても[odfeの公式ドキュメント](https://opendistro.github.io/for-elasticsearch-docs/docs/install/docker/)で説明されているので参照されると良いと思います。

Amazon ESでは、デフォルトで[すべてのドメインにICU AnalysisとJapanese (kuromoji) Analysisのプラグインが含まれている](https://docs.aws.amazon.com/ja_jp/elasticsearch-service/latest/developerguide/aes-supported-plugins.html)ため、odfeの開発環境にも該当のプラグインを入れています。

## 詰まった箇所②マルチノードで立ち上げると一部のコンテナが立ち上がらない
docker-composeでコンテナを起動する時に、マルチノードで立ち上げようとすると、一部のコンテナが`exit with 137`で終了してしまいました。

`exit with 137`で終了し、一部のコンテナが立ち上がらない場合、Docker Engineのメモリ不足の可能性が考えられます。

[Issue](https://github.com/elastic/elasticsearch/issues/51196)にも上がっているので、自分と同様に一部のコンテナが立ち上がらなかった方は、Docker Desktopの設定画面からDocker Engineのメモリを増やす対応を取ってみてください。
自分の環境ではこの対応で`exit with 137`が出なくなりすべてのコンテナが立ち上がりました。

## おわりに
マルチノードでodfeの開発環境を構築するときに詰まった箇所を紹介しました。
私自身Elasticsearchの利用ははじめてなので、設定に不備がある場合はコメント、編集リクエストをいただけますと幸いです。

明日のアドベントカレンダーは[@mt-kage](https://qiita.com/mt-kage)が担当します！お楽しみに〜！