EC2 Discovery Pluginを使ってElasticsearch7のクラスタを構築してみた話

## はじめに
ElasticsearchのクラスタをEC2で構築する場合、**EC2 Discovery Plugin**は便利ですよね！

Elasticsearchの7系ではクラスタ構成時のノード検出(Discovery)方式が変わっています。

そこで、7系でEC2 Discovery Pluginを利用した場合に`elasticsearch.yml`の設定が
どのようになるのか改めて確認してみました。本投稿は、その際の備忘録になります。

## 7系におけるノード検出について
6系までは、`discovery.zen.minimum_master_nodes`で
クラスタを維持する上で最低限必要なマスター適合ノードの数を指定していました。

7系では、クラスタの起動チェックプロセスにおいて
`cluster.initial_master_nodes`で指定したマスター適合ノードの
ホスト名もしくはIPアドレスを見に行くため、明示的に指定する必要があります。
※ローリングアップデートのようにクラスタを絶対に落とさない場合は設定がなくても動作します。

また、Zen Discoveryのパラメータ名が下記のように変わっています。
※ 今回は、`discovery.seed_providers`を利用します。

| 6系まで | 7系から |
|--------|--------|
| discovery.zen.ping.unicast.hosts | discovery.seed_hosts |
| discovery.zen.hosts_provider | discovery.seed_providers |
| discovery.zen.no_master_block | cluster.no_master_block |

**【参考】**
・ [新世代Elasticsearchクラスターコーディネーション][00]
・ [7系におけるノード検出方法の変更点][01]
・ [Bootstrapping a cluster][02]

## EC2 Discovery Pluginとは
複数台のEC2でElasticsearchのクラスタを構成する際
`discovery.seed_hosts`を使わずにプラグインでマスター適合ノードを検出します。

ノード検出には、EC2に付与した**タグ**や**セキュリティグループ**を使うことができます。
(今回は、セキュリティグループを利用した設定を確認しています)

**【参考】**
・ [EC2 Discovery Plugin][03]

## 利用環境
| 項目 | 内容 |
|-----|----|
| Elasticsearch | 7.12.1 |
| OpenJDK (同梱) | AdoptOpenJDK (build 16+36) |
| OS | Amazon Linux 2 |
| AMI | ami-0cf6f5c8a62fa5da6 |
| Instance Type | t3.medium (2vCPU,４GB) |
| EBS | 20GB (gp2) |
| Region | us-west-2 |

**【構成図】**
・3つのAZ(2a、2b、2c)にそれぞれ1台ずつ、全部で3台のEC2を用意します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211371/c2e3da46-2313-e507-3b37-b6f91d2c58aa.png)

**【補足】**
・ インストールに必要なパッケージダウンロードのため、Publicに配置しています。
・ Elasticsearchは３台ともマスター兼データノードの役割としています。
・ Elasticsearchのクラスタ間のDiscover通信は9300-9305を利用します。(デフォルトは9300)
・ EC2のIPアドレスは事前に`ip addr`コマンドで確認済みとしています。

## 実施手順
1. IAMロールの作成
2. セキュリティグループの作成
3. Elasticsearchのインストール
4. discovery-ec2 pluginのインストール
5. クラスタの設定
6. 動作確認

**【補足】**
・ 本投稿では、VPC、サブネット、EC2は事前に作成済みとします。
・ EC2の操作を全てSystems Managerのセッションマネージャー経由としています。

### 1. IAMロールの作成
- AWSマネージメントコンソールにログインします。
- `IAM` > `ポリシー`で**ポリシーの作成**を実施します。
- JSONタブを開き、下記のポリシーを貼り付けます。

```json:AmazonEC2RoleforES
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "ec2:DescribeInstances"
      ],
      "Effect": "Allow",
      "Resource": [
        "*"
      ]
    }
  ]
}
```

※ Elasticsearchがノード検出時にEC2のメタ情報を参照するため

- 次に作成したIAMポリシーをIAMロールに割り当てます。
- `IAM` > `ロール`で**ロールの作成**を実施します。
- 下記内容で作成します。

| 項目 | 内容 |
|------|-----|
| 信頼されたエンティティの種類 | AWSサービス |
| ユースケースの選択 | EC2 |
| Attachアクセス権限ポリシー | AmazonEC2RoleforSSM、AmazonEC2RoleforES |
| ロール名 | AmazonEC2RoleforES |

- 作成したIAMロールを3台のEC2に割り当てます。

### 2. セキュリティグループの作成
- `EC2` > `セキュリティグループ`で**セキュリティグループを作成**を実施します。
- 下記内容で作成します。

| 項目 | 内容 |
|------|-----|
| セキュリティグループ名 | sg_es |
| 説明 | SGforES |
| VPC | ES_VPC |
| インバウンドルール1 | ポート: TCP9200、ソース: 10.0.0.0/16 |
| インバウンドルール2 | ポート: TCP9300、ソース: 10.0.0.0/16 |

- `セキュリティグループID`は後で利用するため控えておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211371/596286f2-cc00-901d-d134-df3141b54bf3.png)

- 作成したセキュリティグループを3台のEC2に割り当てます。

### 3. Elasticsearchのインストール
- Amazon Linux2は[公式サイトのRPMでの手順][04]でインストールします。
- 下記コマンドでPGP鍵をインポートします。

```console:PGP鍵インポート
$ sudo rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch
```

- 下記コマンドでRPMリポジトリ定義ファイルを作成します。

```console:RPMリポジトリ定義ファイル作成
$ sudo vi /etc/yum.repos.d/elastic.repo

[elastic]
name=Elasticsearch repository for 7.x packages
baseurl=https://artifacts.elastic.co/packages/7.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=0
autorefresh=1
type=rpm-md
```

- 下記コマンドでElasticsearchパッケージをインストールします。

```console:Elasticsearchインストール
$ sudo sudo yum install --enablerepo=elastic elasticsearch
```

- 下記コマンドでElasticsearchと同梱されているOpneJDKのバージョンを確認します。

```console:バージョン確認
$ yum list installed | grep elastic
$ /usr/share/elasticsearch/jdk/bin/java --version
```

- 下記コマンドでElasticsearchデーモンの自動起動設定をします。

```console:サービス自動起動設定
$ sudo systemctl daemon-reload
$ sudo systemctl enable elasticsearch
```

※ ここではデーモン起動はしません。
※ 本インストール手順を３台で同じ内容を実施します。

### 4. discovery-ec2 pluginのインストール
- 下記コマンドでプラグインをインストールします。

```console:プラグインインストール
$ sudo /usr/share/elasticsearch/bin/elasticsearch-plugin install discovery-ec2
```

### 5. クラスタの設定
- 下記コマンドで設定ファイルにクラスタ設定を行います。(3台とも同じ設定を行います)

```diff:Elasticsearchクラスタ設定
$ sudo vi /etc/elasticsearch/elasticsearch.yml

# リッスンするIPアドレスを指定
- #network.host: 192.168.0.1
+ network.host: _eth0_

# ノード検出方法でEC2 Discovery有効化
- discovery.seed_hosts: ["host1", "host2"]
+ discovery.seed_providers: ec2
+ discovery.ec2.groups: "sg-01b2b630a5385f442"
+ discovery.ec2.endpoint: "ec2.us-west-2.amazonaws.com"
+ discovery.ec2.availability_zones: ["us-west-2a", "us-west-2b", "us-west-2c"]

# クラスタ起動時のマスター参照設定 (bootstrapping a cluster)
+ cluster.initial_master_nodes: ["10.0.1.174", "10.0.2.92", "10.0.3.230"]
```

※ セキュリティグループIDとマスター適合ノードのIPアドレスは各自の環境に合わせてください。

- 下記コマンドでElasticsearchを起動します。(3台とも)

```console:Elasticsearch起動
$ sudo systemctl start elasticsearch
```

### 6. 動作確認
- 下記コマンドでクラスタ状態を確認します。

```console:クラスタ状態確認
$ curl http://<ElasticsearchのIPアドレス>:9200/_cluster/health?pretty

# 下記は正常時のレスポンスになります。
{
  "cluster_name" : "elasticsearch",
  "status" : "green",
  "timed_out" : false,
  "number_of_nodes" : 3,
  "number_of_data_nodes" : 3,
  "active_primary_shards" : 0,
  "active_shards" : 0,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 0,
  "delayed_unassigned_shards" : 0,
  "number_of_pending_tasks" : 0,
  "number_of_in_flight_fetch" : 0,
  "task_max_waiting_in_queue_millis" : 0,
  "active_shards_percent_as_number" : 100.0
}
```

※ `status`が**green**、`number_of_nodes`が**3**であればOKです。
※ `cluster_name`はデフォルトで**elasticsearch**となります。

- 下記コマンドはクラスタ内のノードの状態になります。(es02がマスターに選出されています)

```console:ノード一覧
$ sudo curl http://<ElasticsearchのIPアドレス>:9200/_cat/nodes?v

# 下記は正常時のレスポンスになります。
ip         heap.percent ram.percent cpu load_1m load_5m load_15m node.role   master name
10.0.2.92            33          64   4    0.02    0.15     0.11 cdfhilmrstw *      ip-10-0-2-92.us-west-2.compute.internal
10.0.3.230            8          63   4    0.05    0.22     0.15 cdfhilmrstw -      ip-10-0-3-230.us-west-2.compute.internal
10.0.1.174           45          63   4    0.01    0.08     0.05 cdfhilmrstw -      ip-10-0-1-174.us-west-2.compute.internal
```

- 余談ですが、`cluster.initial_master_nodes`を設定しないと下記エラーログが出力されます。

```text:エラーログ
$ sudo tail -f /var/log/elasticsearch/elasticsearch.log
2021-05-05T23:53:31,994][WARN ][o.e.c.c.ClusterFormationFailureHelper] [ip-10-0-1-174.us-west-2.compute.internal] master not discovered yet, this node has not previously joined a bootstrapped (v7+) cluster, and [cluster.initial_master_nodes] is empty on this node: have discovered [{ip-10-0-1-174.us-west-2.compute.internal}{ctcTgFMWQGiCqi0dWIr9Ow}{7yMMGhinTViqK2eoeL_wzg}{10.0.1.174}{10.0.1.174:9300}{cdfhilmrstw}{ml.machine_memory=4073328640, xpack.installed=true, transform.node=true, ml.max_open_jobs=20, ml.max_jvm_size=1073741824}, {ip-10-0-3-230.us-west-2.compute.internal}{9Xpmo5b6SqKlloXUWRzeGg}{J9Ag6Ng3QXuwqCxMZGVj6g}{10.0.3.230}{10.0.3.230:9300}{cdfhilmrstw}{ml.machine_memory=4073328640, ml.max_open_jobs=20, xpack.installed=true, ml.max_jvm_size=1073741824, transform.node=true}, {ip-10-0-2-92.us-west-2.compute.internal}{2V-zcyrySuy13tlNBtRskQ}{xqPqlOH7Sp-pMEGFTGDOSA}{10.0.2.92}{10.0.2.92:9300}{cdfhilmrstw}{ml.machine_memory=4073328640, ml.max_open_jobs=20, xpack.installed=true, ml.max_jvm_size=1073741824, transform.node=true}]; discovery will continue using [127.0.0.1:9300, 127.0.0.1:9301, 127.0.0.1:9302, 127.0.0.1:9303, 127.0.0.1:9304, 127.0.0.1:9305, [::1]:9300, [::1]:9301, [::1]:9302, [::1]:9303, [::1]:9304, [::1]:9305, 10.0.2.92:9300, 10.0.1.174:9300, 10.0.3.230:9300] from hosts providers and [{ip-10-0-1-174.us-west-2.compute.internal}{ctcTgFMWQGiCqi0dWIr9Ow}{7yMMGhinTViqK2eoeL_wzg}{10.0.1.174}{10.0.1.174:9300}{cdfhilmrstw}{ml.machine_memory=4073328640, xpack.installed=true, transform.node=true, ml.max_open_jobs=20, ml.max_jvm_size=1073741824}] from last-known cluster state; node term 0, last-accepted version 0 in term 0
```

やはり、プラグインだけではダメで、`cluster.initial_master_nodes`の設定が必要なようです。

## まとめ
以上がEC2でのクラスタ構成設定手順でしたが、いかがでしたでしょうか？

プラグイン使うことでマスター適合ノードのIPアドレスを意識しなくて良くなると思ったんですが
`cluster.initial_master_nodes`で指定する必要があるため、嬉しみ半減でした。

これ、プラグイン必要ないんじゃないか、、、と思ってしまいましたが
無事に7系でもEC2でクラスタを組めたので、良しとしましょう^^

[00]:https://www.elastic.co/jp/blog/a-new-era-for-cluster-coordination-in-elasticsearch "新世代Elasticsearchクラスターコーディネーション"
[01]:https://www.elastic.co/guide/en/elasticsearch/reference/7.12/breaking-changes-7.0.html#breaking_70_discovery_changes "7系におけるノード検出方法の変更点"
[02]:https://www.elastic.co/guide/en/elasticsearch/reference/7.12/modules-discovery-bootstrap-cluster.html "Bootstrapping a cluster"
[03]:https://www.elastic.co/guide/en/elasticsearch/plugins/7.12/discovery-ec2.html "EC2 Discovery Plugin"
[04]:https://www.elastic.co/guide/en/elasticsearch/reference/7.12/rpm.html "Install Elasticsearch with RPM"