AWS EC2上でElasticsearchのクラスタ構成のノードの追加をオンラインでやってみた

少し前に、掲題の内容を対応をしたのですが、記事にするのを失念してました。。。。そのため、覚えてる範囲でですが、やった内容を共有したいと思います。。。
記載し忘れてしまっていることがありそうですが、そこはご了承ください。ｍｍ

# 参考記事
- [elasticsearch クラスタを EC2 上に構築する - ようへいの日々精進XP](http://inokara.hateblo.jp/entry/2014/01/19/080927)
- [Elasticsearchのクラスタにノードを追加するときにやっていること - すずけんメモ](http://suzuken.hatenablog.jp/entry/2014/04/17/134013)
- [Elasticsearch AWS で Multi-AZ 配置 — Hello! Elasticsearch. — Medium](https://medium.com/hello-elasticsearch/elasticsearch-aws-%E3%81%A7-multi-az-%E9%85%8D%E7%BD%AE-e638a0262916#.kesi8jiwe)
- [Elastic Searchで全てのデータ(index)を削除する - Qiita](http://qiita.com/shouta-dev/items/c2d2eb6cf61bb1fa8e1b)
- [Elasticsearch on AWS - High Availability and Security best practices](http://www.slideshare.net/bobeirasa/es-on-aws-rafael-lopes-2nd-meetup-elasticsearch-brasil)
- [AWS Cloud Plugin for ElasticSearchを使う – AWSで始めるElasticSearch(3) ｜ Developers.IO](http://dev.classmethod.jp/cloud/aws/use-elasticsearch-3-use-awscloudplugin/)
- [amazon web services - elasticsearch-cloud-aws plugin not working with IAM role - Stack Overflow](http://stackoverflow.com/questions/32103603/elasticsearch-cloud-aws-plugin-not-working-with-iam-role)

# 必要になった経緯
- Elasticsearchにデータが多く溜まり、複雑な集計クエリが実行されるとノードが１つでは辛くなってきた。。。
- そのため、担当システムでのアクセスが重くなるなどの障害が起き始めていたため、スケールする必要が出てきた

# 実現方法検討
- 現在の本番環境のElasticsearchインスタンスのAMIを取得して、同じサブネット内に設置すればいける？
    - できません。理由は、既存ノードがクラスタ構成となるように設定されて起動しているため、全く同じデータが入ってる状態でAMIから起動すると、重複データが入った状態になるためです。
    - そのため、同じスペックのデータやインデックスの存在しないノードを作成する必要があった。
- せっかくなので、Multi-AZ構成にしたいけど、そのままやればいけるの？
    - それだと、下記の問題が起きてしまい、良い状態とはなりません。(参考URL3つ目の引用)
        - Elasticsearchは受け付けた検索要求をすべてのシャードへ問い合わせる仕組みのため、マルチAZ配置にするとAZ間の通信が発生してしまうので、どうしてもパフォーマンスが落ちてしまいます。
    - そのため、参考URL3つ目の方がやられている通り、**「Shard Allocation Awareness」** という方法で解決しました。

# 対応手順
- 本番のElasticsearchインスタンスをAMIを取得。
- ネットワーク的に隔離された別のサブネットにて該当のAMIを起動。
- 起動後、インデックス、データ全てを削除するため、下記のコマンドを実行。

```bash
$ curl -XDELETE 'http://localhost:9200/*'
{"acknowledged":true}
```

- データが削除されたか確認し、確認できたら、再度インスタンスのAMIを取得。
- 取得後、本番のElasticsearchインスタンスのあるAZとは別のAZ内のサブネットでAMIからインスタンスを起動。Elasticsearchは起動しないようにインスタンスだけ起動します。
- AMIから起動する際に、該当のインスタンスのロールを「EC2のDescribe」権限のあるロールを設定してあげないとノードへ追加されない事象が起きます。お気を付け下さい。
- Elasticsearchの設定ファイルを修正。

```/etc/elasticsearch/elasticsearch.yml
# ap-northeast-1a 側のノード
cluster.name: XXXXX-XXXX-cluster
cluster.routing.allocation.awareness.attributes: aws_availability_zone
cluster.routing.allocation.awareness.force.aws_availability_zone.values: ap-northeast-1a,ap-northeast-1c
cloud.node.auto_attributes: true

node.zone: ap-northeast-1a

discovery.zen.ping.multicast.enabled: false
discovery.type: ec2
cloud.aws.region: ap-northeast-1

# ap-northeast-1c
cluster.name: XXXXX-XXXX-cluster
cluster.routing.allocation.awareness.attributes: aws_availability_zone
cluster.routing.allocation.awareness.force.aws_availability_zone.values: ap-northeast-1a,ap-northeast-1c
cloud.node.auto_attributes: true

node.zone: ap-northeast-1c

discovery.zen.ping.multicast.enabled: false
discovery.type: ec2
cloud.aws.region: ap-northeast-1
```

- 設定ファイルを修正したので、まずは、稼働中のElasticsearchサーバから、再起動する前に、参考URLの通り、「ノード追加前の準備」を実行しました。　※本当は再起動後が良かったかも？

```bash
$ curl -XPUT 'http://localhost:9200/_cluster/settings' -d '{
"persistent": {"cluster.routing.allocation.enable": "none"}
}'
{"acknowledged":true,"persistent":{"cluster":{"routing":{"allocation":{"enable":"none"}}}},"transient":{}}%
// minimum_master_nodesは、１ノードを追加で、２ノードになるが、「ノード数 / 2 + 1」を計算すると、１なので、デフォルトだったので、実行なしとしました。
```

- 上記実行後、稼働中のElasticsearchサーバを再起動。

```bash
$ sudo service elasticsearch status
$ sudo service elasticsearch restart
```

- 起動後、設定が反映されていることを確認。

```bash
$ curl -XGET 'http://localhost:9200/_nodes?pretty=true'
{
  "cluster_name" : "XXXX-XXXX-cluster",
  "nodes" : {
    "XXXXXXXXXXXXXXXXXXXXX" : {
      "name" : "Ch'od",
      "transport_address" : "inet[10.0.X.XXX/10.0.X.XXX:9300]",
      "host" : "localhost",
      "ip" : "127.0.0.1",
      "version" : "1.3.2",
      "build" : "dee175d",
      "http_address" : "inet[/10.0.XXX.XXX:9200]",
      "attributes" : {
        "aws_availability_zone" : "ap-northeast-1c",
        "zone" : "ap-northeast-1c"
      },
      "settings" : {
        "node" : {
          "zone" : "ap-northeast-1c"
        },
        "bootstrap" : {
          "mlockall" : "true"
        },
        "name" : "Ch'od",
        "pidfile" : "/var/run/elasticsearch/elasticsearch.pid",
        "path" : {
          "data" : "/XXX/elasticsearch/data",
          "work" : "/XXX/elasticsearch/work",
          "home" : "/usr/share/elasticsearch",
          "conf" : "/etc/elasticsearch",
          "logs" : "/XXX/elasticsearch/logs"
        },
        "cluster" : {
          "routing" : {
            "allocation" : {
              "awareness" : {
                "attributes" : "aws_availability_zone",
                "force" : {
                  "aws_availability_zone" : {
                    "values" : "ap-northeast-1a,ap-northeast-1c"
                  }
                }
              }
            }
          },
          "name" : "XXXX-XXXX-cluster"
        },
        "cloud" : {
          "aws" : {
            "region" : "ap-northeast-1"
          },
          "node" : {
            "auto_attributes" : "true"
          }
        },
...
```

- 確認できたら、もう１つのデータの入ってないElasticsearchサーバを起動し、上記同様に設定を確認しましょう。
- その後、無事ノードがクラスタに追加されたことを確認したら、シャード移動を許可するようにする。

```bash
$ curl -XPUT http://localhost:9200/_cluster/settings -d '{
"persistent": {"cluster.routing.allocation.enable": "all"}
}'
```

- それぞれのノードが aws_availability_zone の値に何が設定されているのか確認するには、次のコマンドを実行します。

```bash
$ curl localhost:9200/_nodes/transport?pretty
{
  "cluster_name" : "XXXX-XXXX-cluster",
  "nodes" : {
    "XXXXXXXXXX" : {
      "name" : "Ch'od",
      "transport_address" : "inet[10.0.XXX.XXX/10.0.XXX.XXX:9300]",
      "host" : "localhost",
      "ip" : "127.0.0.1",
      "version" : "1.3.2",
      "build" : "dee175d",
      "http_address" : "inet[/10.0.XXX.XXX:9200]",
      "attributes" : {
        "aws_availability_zone" : "ap-northeast-1c",
        "zone" : "ap-northeast-1c"
      },
      "transport" : {
        "bound_address" : "inet[/0:0:0:0:0:0:0:0:9300]",
        "publish_address" : "inet[10.0.XXX.XXX/10.0.XXX.XXX:9300]"
      }
    },
    "XXXXXXXXXXXX" : {
      "name" : "Danielle Moonstar",
      "transport_address" : "inet[/10.0.XXX.XXX:9300]",
      "host" : "localhost",
      "ip" : "127.0.0.1",
      "version" : "1.3.2",
      "build" : "dee175d",
      "http_address" : "inet[/10.0.XXX.XXX:9200]",
      "attributes" : {
        "aws_availability_zone" : "ap-northeast-1a",
        "zone" : "ap-northeast-1a"
      },
      "transport" : {
        "bound_address" : "inet[/0:0:0:0:0:0:0:0%0:9300]",
        "publish_address" : "inet[/10.0.XXX.XXX:9300]"
      }
    }
  }
}
```

以上です。何か間違っていましたらご指摘よろしくお願いいたします。ｍｍ