Google Compute EngineでオートスケールするElasticsearchを構築した


GCE上にオートスケールするElasticsearchクラスタを構築したのでまとめます。
基本的にはClick to DeployのElasticsearchから何をしてるのかを見て、そこからオートスケールできるように色々やった感じです。
オートスケールしないなら、Click to Deployでよいと思います
本当はオートスケールじゃなくて後でサーバー増やしたいときに融通が利かなかったためです。

説明で使用するファイルとスクリプトは[Github](https://github.com/shabutora/elasticsearch-gcp)にあげてます。

Developer Consoleでも以下のクラスタはできますけど、基本は全部コマンドラインで実行できるようにしてます。
Githubにもあるので、参考にしてください。

## ネットワーキング設定をする

インスタンスを作成する前にあらかじめクラスタ組んだ際のネットワーク設定を作成する必要があります。

### 外部IPアドレスを作成する

負荷分散のための静的IPアドレスを予約します。
リージョンは好きなのを指定してください。

```
$ gcloud compute addresses create "elasticsearch-ip" \
                                  --region "us-central1"
```

### ファイアウォールルールを作成する

Elasticsearchの9200と9300のポートを通すファイアウォールを作りします。
適用タグは`elasticsearch`にしてます。
`--srouce-ranges`でファイアウォールの範囲を指定します。
ここでは全部許可にしてます。

```
$ gcloud compute firewall-rules create "default-allow-elasticsearch" \
                                       --allow tcp:9200,tcp:9300 \
                                       --network "default" \
                                       --source-ranges "0.0.0.0/0" \
                                       --target-tags "elasticsearch"
```

### ヘルスチェックを作成する

クラスタの死活監視に使用するヘルスチェックを作成します。
コマンドの閾値はデフォルト値のままなので適宜変えてください。

```
$ gcloud compute http-health-checks create "elasticsearch-health-check" \
                                           --port "9200" \
                                           --request-path "/" \
                                           --check-interval "5" \
                                           --timeout "5" \
                                           --unhealthy-threshold "2" \
                                           --healthy-threshold "2"
```

### ネットワーク負荷分散を作成する

#### ターゲットプールを作成する

```
$ gcloud compute target-pools create "elasticsaerch-lb-pool" \
                                    --region "us-central1" \
                                    --health-check "elasticsearch-health-check" \
```

#### 転送ルールを作成する

`--address`は[外部IPアドレスを作成する](#外部IPアドレスを作成する)で作成したIPを指定してください。

```
$ gcloud compute forwarding-rules create "elasticsearch-lb-rule" \
                                         --region "us-central1" \
                                         --address "${ip_address}" \
                                         --ip-protocol "TCP" \
                                         --port-range "9200" \
                                         --target-pool "elasticsaerch-lb-pool"
```

これでネットワークの設定は完了です。

## インスタンステンプレートを作る

### スタートアップスクリプトでElasticsearchの設定をする

インスタンスのスタートアップスクリプトで起動時にElasticsearchのクラスタ設定します。
ファイルは[コチラ](https://github.com/shabutora/elasticsearch-gcp/blob/master/setup/elastic-startup.sh)

#### ちょっとスクリプトの説明

普通にElasticsearchダウンロードしてインストールして、プラグイン入れてます。
Cloud Monitoringがいらない人はその辺りの記述を消して頂ければいいと思います。
最後に、自身のインスタンスをクラスタネットワークに自分で参加するようにしています。

```
gcloud compute target-pools add-instances $TARGET_POOL --instances $NODE_NAME --zone $ZONE
```

ターゲットプールの設定にインスタンスグループを指定できるので、そっちをやってもよかったんですが、
作成していく順序とか考えて自分で参加する形式にしました。


#### スタートアップスクリプトをGCSにアップロードする

はい、アップロードしてください。
とりあえずGitリポジトリのやつをアップしてます。
GCSにアップしなくてもstartup-script-urlの値をGithubのURLでもかまわないです。

```
gsutil cp setup gs://${bucket_name}/scripts
```

### インスタンステンプレートを作る

`--machine-type`はお好きなインスタンスのマシンタイプを指定してください。
`--scopes`はコマンドだと全権付けてますが、セットアップには最低限GCSの読み取り権限とGCEの権限があればいけます。
GCEの権限は自分を負荷分散のターゲットプールに参加させるために必要です。
その他ディスクサイズなどはご自由に指定してください。

あとは`--metadata`ですね。
metadataって改行入れらんないかな…

`${startup-script-url}` `${shutdown-script-url}`には、先述したスタートアップスクリプトのGCSのURLを入れてください。
`${bucket_name}`には、先述のGCSバケットの名前を入れてください。

```
$ gcloud compute instance-templates create "elasticsearch-template" \
                                           --machine-type "g1-small" \
                                           --network "default" \
                                           --metadata "startup-script-url=${startup-script-url},shutdown-script-url=${shutdown-script-url},CLUSTER_NAME=${cluster_name},TARGET_POOL=elasticsaerch-lb-pool,GCS_BUCKET=${bucket_name}" \
                                           --maintenance-policy "MIGRATE" \
                                           --scopes "https://www.googleapis.com/auth/cloud-platform" \
                                           --tags "elasticsearch" \
                                           --image "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20151104" \
                                           --boot-disk-size "300" \
                                           --boot-disk-type "pd-standard" \
                                           --boot-disk-device-name "elasticsearch-template"

```

## インスタンスグループを作る

まずは普通にインスタンスグループ作成。

```
$ gcloud compute instance-groups managed create "elasticsearch-group" \
                                                --zone "us-central1-f" \
                                                --base-instance-name "elasticsearch-group" \
                                                --template "elasticsearch-template" \
                                                --size "1"
```

オートスケールの設定
閾値の設定はとりあえず適当

```
gcloud compute instance-groups managed set-autoscaling "elasticsearch-group" \
                                                       --zone "us-central1-f" \
                                                       --cool-down-period "60" \
                                                       --max-num-replicas "10" \
                                                       --min-num-replicas "1" \
                                                       --target-cpu-utilization "0.6"
```

実際には自分はオートスケールはオフにしてます。（題名詐欺）
インスタンスグループのインスタンス数はあとからでも変更できます。

これで最初に作成した静的IPでアクセスしてください。

## 課題

### データの永続化

Elasticsearchのデータは今のところブートディスクに置いているので、PersistentDiskに置きたいですね。

普通にインスタンステンプレート→インスタンスグループの場合、ReadonlyなPersistentDiskは指定して共有できますが、書き込みはできないので、
書き込み可能なPersistentDiskを使用したい場合は、工夫が必要です。
startup-scriptでディスクを見て、なかったら作成して、とかやらないとダメかな。

これができると、インスタンスをプリエンプティブにしてお安くできるかもしれないですね。
インスタンスの増減が激しくて安定しないかもしれないけど…。

### オートスケールとElasticsearchの分散の問題

実は今の設定だけだと、オートスケールの最小と最大の差をけっこう大きくして、クラスタの台数がたくさんで分散した場合、データロストする可能性があります。
オートスケールによってスケールダウンした場合、特定のノードだけが特定のデータを持っててそいつが縮小する場合、他にデータ持ってるノードがいなくてロストしてしまう可能性が…。
Elasticsearchのシャードの設定を増やせば、確率はだいぶ下がります。
緩やかにインスタンス減ってくれればまあなんとかいけるか。

この部分、うまい方法があったら教えて頂きたいです。