Elasticsearchのストレージを追加する方法について

Elasticsearchに使うストレージを増やしたかったのですが、不安な点があったのでまとめました。


## 環境について

**複数台で** Elasticsearchのクラスターが組まれている環境で、1インデックスに複数のシャードが設定されている状態を想定しています。
また、レプリケーションは設定していませんが「データが消えると地味に痛い」環境で考えています。

その他の構成では結果が変わる可能性があります💦


## 新しいストレージを追加して `data.path` に追加する方法

ストレージを追加する各マシンでElasticsearchを終了して、 `./config/elasticsearch.yml` の `data.path` に追加する方法です。

**`data.path` を新しく追加しても、既存のインデックスやシャードは即座に新しい場所に再配置されません。** そのため、今のインデックスが巨大になってストレージを圧迫しているという状況には適しません。

 - Elasticsearch multi data path disk full - Stack Overflow https://stackoverflow.com/questions/56523276/elasticsearch-multi-data-path-disk-full

何もしない場合、ストレージの使用率がwatermark( `cluster.routing.allocation.disk.watermark.high` )を超えてはじめて別のノード(ストレージ)に移動されます。
ノードに向けての移動になるため「このストレージにそのシャードを移動したい」と狙って移動させることはできません。 

Disk-based shard allocation 
https://www.elastic.co/guide/en/elasticsearch/reference/current/disk-allocator.html

> [2019-12-04T16:16:49,472][WARN ][o.e.c.r.a.DiskThresholdMonitor] [elasticsearch-node1] high disk watermark [90%] exceeded on [-LcqY8CedtSkufWq0ba28w][elasticsearch-node1][/mnt/elasticsearch/data/nodes/0] free: 8.4gb[8.4%], shards will be relocated away from this node
> [2019-12-04T16:16:49,472][INFO ][o.e.c.r.a.DiskThresholdMonitor] [elasticsearch-node1] rerouting shards: [high disk watermark exceeded on one or more nodes]

また、`elasticsearch increase storage` `elasticsearch add data.path` などで調べても `data.path` を追加してストレージが追加された例が乏しかったです。

 - Elasticsearchを終了して `data.path` を追加すればいいと「思うよ」 https://discuss.elastic.co/t/adding-another-disk-to-an-es-node/10425
 - Elasticsearchの `data.path` を追加したけど問題が起きなかったよ https://github.com/elastic/elasticsearch/issues/13210#issuecomment-136353406

古いバージョンでありますが `data.path` を追加したことによる不具合の例が上がっています。

 - ノードの状態が RED のままになって使えなくなった https://stackoverflow.com/questions/33345002/elasticsearch-issue-when-adding-new-path-data

 - シャードが消失した(不具合だと考えている)  https://github.com/elastic/elasticsearch/issues/13210


### `data.path` を追加するとどうなるか

**localhostでシャード数が12のインデックスを作って試したところ** 、 `data.path` を追加しても状態が RED になることやデータの消失などはなく通常通り使えました。
(複数台用意しての検証はできていません)

次の環境を使い、以下の手順で確認できます。

```bash
$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.13.6
BuildVersion:	17G7024

$ curl "http://localhost:9200/"
{
  "version" : {
    "number" : "5.6.8",
    "lucene_version" : "6.6.1"
  },
  "tagline" : "You Know, for Search"
}
```

1.`path.data` となるディレクトリを3つ用意する

```bash
mkdir data1 data2 data3
```

2.`elasticsearch.yml` の `data.path` にディレクトリを2つ指定する

```yaml
path.data: /path/to/data1,/path/to/data2
```

3.Elasticsearchを起動して、 **適当にインデックスやドキュメントを追加してから** 終了する。そして `path.data` に `data3` のディレクトリを追加する

```yaml
path.data: /path/to/data1,/path/to/data2,/path/to/data3
```

再びElasticsearchを起動すると、上記のような「状態がREDのままになること」や「データが消失する」ということは起きず通常通り使用できました。

> [2019-06-19T11:36:45,726][INFO ][o.e.n.Node               ] [qp9CbAQ] starting ...
> [2019-06-19T11:36:45,939][INFO ][o.e.t.TransportService   ] [qp9CbAQ] publish_address {127.0.0.1:9300}, bound_addresses {[::1]:9300}, {127.0.0.1:9300}
> [2019-06-19T11:36:49,003][INFO ][o.e.c.s.ClusterService   ] [qp9CbAQ] new_master {qp9CbAQ}{qp9CbAQFQL6m45priWfGSw}{yXd2PI0RSJe7PBJfCCgQIg}{127.0.0.1}{127.0.0.1:9300}, reason: zen-disco-elected-as-master ([0] nodes joined)
> [2019-06-19T11:36:49,027][INFO ][o.e.h.n.Netty4HttpServerTransport] [qp9CbAQ] publish_address {127.0.0.1:9200}, bound_addresses {[::1]:9200}, {127.0.0.1:9200}
> [2019-06-19T11:36:49,027][INFO ][o.e.n.Node               ] [qp9CbAQ] started
> [2019-06-19T11:36:49,350][INFO ][o.e.g.GatewayService     ] [qp9CbAQ] recovered [1] indices into cluster_state
> [2019-06-19T11:36:51,371][INFO ][o.e.c.r.a.AllocationService] [qp9CbAQ] Cluster health status changed from [RED] to [GREEN] (reason: [shards started [[es_sample_data][10], [es_sample_data][4]] ...]).

```bash
$ curl "http://localhost:9200/_cat/shards"
es_sample_data 9  p STARTED 16124 161.1mb 127.0.0.1 qp9CbAQ
es_sample_data 8  p STARTED 16084 156.5mb 127.0.0.1 qp9CbAQ
es_sample_data 7  p STARTED 16155 163.6mb 127.0.0.1 qp9CbAQ
es_sample_data 6  p STARTED 15998 163.6mb 127.0.0.1 qp9CbAQ
es_sample_data 11 p STARTED 16348 162.7mb 127.0.0.1 qp9CbAQ
es_sample_data 10 p STARTED 16050 159.3mb 127.0.0.1 qp9CbAQ
es_sample_data 1  p STARTED 15869 158.4mb 127.0.0.1 qp9CbAQ
es_sample_data 4  p STARTED 16144 163.4mb 127.0.0.1 qp9CbAQ
es_sample_data 3  p STARTED 16210   162mb 127.0.0.1 qp9CbAQ
es_sample_data 2  p STARTED 16277   161mb 127.0.0.1 qp9CbAQ
es_sample_data 5  p STARTED 16340 159.8mb 127.0.0.1 qp9CbAQ
es_sample_data 0  p STARTED 16241 164.7mb 127.0.0.1 qp9CbAQ
```

新しく設定した `data3` には Elasticsearch 関連のファイルが配置されています。

```bash
$ tree data3
data3
└── nodes
    └── 0
        ├── _state
        │   ├── global-2.st
        │   └── node-1.st
        ├── indices
        │   └── C57jAJENRq2AheQLA1V7SA
        │       └── _state
        │           └── state-14.st
        └── node.lock
```


### `data.path` を追加したあとのシャードの再配置について

新しいストレージをマウントして `data.path` に追加するだけでは、(Elasticsearchに認識されるものの) 既存インデックスのシャードが **新しいストレージに再配置されず容量が活用されないため** 注意しましょう。
ストレージの使用率がwatermarkに達するまでシャードが保存されているストレージをずっと使いづつけます。
シャードが新しいストレージに自動で再配置されるため、マシンをクラスターに追加する方法を強くおすすめします。

ストレージだけを追加して `data.path` を追加する方法で増設した場合は、(正しいreroute APIの使い方ではないかもしれませんが)他のノードからストレージを増設したノードにシャードを移動させることで新しいストレージを使用することができます。
手動でもシャードを特定のストレージに移動させる方法はありません😢

 `/_cat/indices?v`, `/_cat/shards/{index name}?v`, `/_cat/segments?v` と `/path/to/data/nodes/0/indices/{indicesのuuid}/{シャード番号の数字}/index` ディレクトリの中身を確認して **再配置するシャードを決めてから** 次のコマンドを用いて、別のノードに新しくシャードを再配置させます。

繰り返しになりますがこのコマンドは別のマシンにシャードを移動するものです。Elasticsearchで認識された新しいストレージが使われますが、使用率の状況によっては希望したストレージにシャードが移動されない場合もあります。
 **また、(別のシャードが交換されるかたちで)移動先のノードから現在のノードにシャードが来ることもあります。**

インデックス名の指定にエイリアスは使えません。エイリアスを指定してもシャードが見つからない旨のエラーが出ます。

```bash
curl -XPOST 'nodes-1:9200/_cluster/reroute?pretty' -d '{
    "commands" : [ {
        "move" :
            {
              "index" : "es_sample", "shard" : {shard id},
              "from_node" : "nodes-1", "to_node" : "nodes-2"
            }
        }
    ]
}'
```

## Elasticsearchのクラスターにノードを追加する方法

こちらでは検証していませんが、Elasticsearchのクラスターにノード(マシン)を追加するとクラスター全体のストレージ容量が増えることが確認されています。
この方法では既存のElasticsearchのノードを一旦終了させる必要がなく無停止でストレージを増設できます。

(マシンが増えることを許容するのであれば)こちらの方法が実績があり無停止で増設できるメリットを受けられます。
この場合は、シャードの再配置による負荷の上昇やスプリットブレインの防止についてなど考慮する必要があります。

Elasticsearchのクラスタにノードを追加するときにやっていること
https://suzuken.hatenablog.jp/entry/2014/04/17/134013

新しいマシンでElasticsearchを設定してクラスター名( `cluster.name` )を既存のクラスター名と同じにするだけで、新しいマシンがクラスターに追加されます。
データの再配置も自動的に実行されます。

 - Adding nodes to your cluster | Elasticsearch Reference [7.1] | Elastic 
https://www.elastic.co/guide/en/elasticsearch/reference/current/add-elasticsearch-nodes.html
 - Elasticsearchクラスタにノードを追加してみる - Qiita https://qiita.com/tsukapah/items/7b976cb2c59941875ec2


### ノード追加時の `zen.ping.unicast.hosts` の設定について

基本的に `elasticsearch.yml` の `zen.ping.unicast.hosts` はクラスター内のノードをすべて設定する必要はありません。マスターノードがクラスターの情報を全部配ってくれるので **マスターノードになりうるノードだけを指定します。**

 - https://www.elastic.co/jp/blog/how-to-configure-elasticsearch-cluster-better ->  誤解：全てのノードのzen.ping.unicast.hostsにはクラスターに接続するノードを「全て」記述しないといけない


`node.master` を明示的に設定しないと、デフォルトが `true` のため一つのノードがダウンしたらどれが新しくマスターノードになるかわからない設定になり `zen.ping.unicast.hosts` にノードを全部書く必要がありそうです。

> Master-eligible node
> A node that has node.master set to true (default), which makes it eligible to be elected as the master node, which controls the cluster.
> 
> https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-node.html


## まとめ

Elasticsearchでストレージを追加するには次の方法が使えそうですね ☺️

 - 新しいストレージをマウントしてから、Elasticsearchを終了し、`elasticsearch.yml` に `data.path` を追加して再起動する
   - ただし、同一ノードにストレージを追加しているのでノードが壊れてしまうとおしまいです
   - また、既存のインデックス・シャードは新しい場所に再配置されません。新しいインデックス用に容量を確保する場合しか使えないでしょう
 - 新しいマシンでElasticsearchをセットアップしてクラスターにノードを追加する
   - 管理するマシンは増えてしまいますが、シャードの再配置が自動的に実行されるため、インデックスが巨大になってストレージを圧迫している状況に有効でしょう