GCPの環境でGCE discovery pluginを使ってみた！

## はじめに

ElasticsearchのクラスタをGCPで構築する際に使った方が良いプラグインを紹介したいと思います。
それは、GCE discovery pluginです。
このプラグインを使用することで、Unicastでホスト名やIPアドレスを指定することなく、よしなにクラスタを組んでくれます。

以前に[EC2 discovery pluginの紹介](https://qiita.com/micci184/items/395684da24a319fc2b1e)をしましたが、インスタンスがダウンした際にノードIDの重複問題を抱えてました。
そこの改善も含めて今回記載させて頂きます。

それでは早速ですが、構築していきます( ﾟДﾟ)ゞﾋﾞｼｯ

## Setup Firewall Rules

ファイアウォールルールを作成します。
ノード間で通信するために9200-9300でポートを解放します。
また、9200でHTTP通信を受け付けるため解放します。

```bash
$ gcloud compute firewall-rules create elasticsearch --direction=INGRESS \
 --priority=1000 --network=default --action=ALLOW \
 --rules=tcp:9200,tcp:9300-9400 --source-ranges=0.0.0.0/0
```

## Create an instance using gcloud SDK

インスタンスを構築する準備ができましたので、インスタンスを構築します。
gcloudが実行できる環境を前提に進めます。
もし、gcloudの実行環境がない場合は、[Cloud SDKインストール](https://cloud.google.com/sdk/downloads?hl=ja)のサイトを参考にしてください。

```bash
$ gcloud compute instances create es-gce-discovery \
  --machine-type=n1-standard-2 --subnet=default \
  --min-cpu-platform=Automatic \
  --tags=elasticsearch,http-server,https-server \
  --image=ubuntu-1604-xenial-v20180306 \
  --image-project=ubuntu-os-cloud \
  --boot-disk-size=10GB --boot-disk-type=pd-standard \
  --boot-disk-device-name=sgce-discovery --scopes=compute-ro
```

インスタンスが立ち上がったのでSSHで接続します。

```bash
gcloud compute ssh es-gce-discovery
```

## Install Java

インスタンスにJDK 8をインストールします。

```bash
$ sudo apt-get update
$ sudo add-apt-repository ppa:webupd8team/java
$ sudo apt-get update
$ sudo apt-get install oracle-java8-installer
$ sudo apt-get install oracle-java8-set-default
$ java -version
java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)
```

## Install Elasticsearch and GCE discovery plugin

Elasticsearchをインストールするため、PGP-keyをインストールします。
[Install Elasticsearch with Dibian Packages](https://www.elastic.co/guide/en/elasticsearch/reference/6.2/deb.html)

```bash
$ wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
```

次に"apt-transport-https"をインストールします。

```bash
$ sudo apt-get install apt-transport-https
```

リポジトリを追加します。

```bash
$ echo "deb https://artifacts.elastic.co/packages/6.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-6.x.list
```

Elasticsearchをインストールします。

```bash
$ sudo apt-get update && sudo apt-get install elasticsearch
$ sudo /usr/share/elasticsearch/bin/elasticsearch -version
Version: 6.2.4, Build: ccec39f/2018-04-12T20:37:28.497551Z, JVM: 1.8.0_171
```

次に"GCE discovery plugin"をインストールします。

```bash
$ sudo /usr/share/elasticsearch/bin/elasticsearch-plugin install discovery-gce
```

インストールが完了したので、Elasticsearchの設定ファイルに設定を追加します。

```bash
$ sudo vim /etc/elasticsearch/elasticsearch.yml
### クラスタ名はデフォルト"elasticsearch"
cluster.name: lannister
### ノード名を動的に設定するため、${HOSTNAME} を使用する
node.name: ${HOSTNAME}
### GCEのホスト名をネットワークサービスにバインド
network.host: _gce:hostname_
### スプリットブレインを避けるため、適正なノード数にする
discovery.zen.minimum_master_nodes: 2
### "GCE discovery plugin"の設定をする
cloud.gce.project_id: "your Project-Name"
cloud.gce.zone: us-east1-b
discovery.zen.hosts_provider: gce
```

Elasticsearchを起動します。

```bash
$ sudo /etc/init.d/elasticsearch start
```

curlコマンドでサービス起動の確認をします。

```bash
$ curl ${HOSTNAME}:9200
```

起動確認できたので、インスタンスからログオフします。

ちなみにですが、Elasticsearch起動後にログを確認すると永遠と"WARN"が出力されますが、無視してください。
1台しかないから当たり前ですが、ノード数が足りないために発生しています。

```bash
$ sudo tail -f /var/log/elasticsearch/lannister.log
[2018-05-13T07:45:11,358][WARN ][o.e.d.z.ZenDiscovery     ] [es-gce-discovery] not enough master nodes discovered during pinging (found [[Candidate{node={es-gce-discovery}{SAFhkm66S7eGgURWMHS3Ww}{EHnLunWFQTuYHgVJ-WXDkA}{es-gce-discovery.c.winter-jet-194500.internal}{10.142.0.2:9300}, clusterStateVersion=-1}]], but needed [2]), pinging again
```

## Create a GCE custom image

ローカル環境からディスクイメージを保管するバケットを作成します。

```bash
$ gsutil mb -p "your Project-Name" -c regional -l us-east1 gs://es-test-bucket/
```

Elasticsearchをインストールしたインスタンスを停止します。

```bash
$ gcloud compute instances stop es-gce-discovery
```

停止したインスタンスのスナップショットを取得します。

```bash
$ gcloud compute disks snapshot es-gce-discovery \
  --snapshot-names=es-gce-discovery-snapshot
```

取得したスナップショットで、GCEディスクを作成します。

```bash
$ gcloud compute disks create es-gce-discovery-disk \
  --source-snapshot es-gce-discovery-snapshot
```

元のイメージディスクより50%より大きい一時ディスクを作成します。
今回は、元のディスクが10GBなため、一時ディスクは15GBで作成します。

```bash
$ gcloud compute disks create es-gce-discovery-temp-disk --size 15GB
```

スコープのstorage-rwを使用し、二つのディスクをマウントした一時的なインスタンスを作成します。

```bash
$ gcloud compute instances create es-temp-instance --scopes storage-rw \
  --disk name=es-gce-discovery-disk,device-name=es-gce-discovery-disk \
  --disk name=es-gce-discovery-temp-disk,device-name=es-gce-discovery-temp-disk
```

作成できたら一時インスタンスにアクセスします。

```bash
$ gcloud compute ssh sherry-temp-instance
```

ディスクを確認します。

```bash
$ ls /dev/disk/by-id/
google-es-gce-discovery-disk        google-persistent-disk-0                           scsi-0Google_PersistentDisk_es-gce-discovery-disk-part1  scsi-0Google_PersistentDisk_persistent-disk-0-part1
google-es-gce-discovery-disk-part1  google-persistent-disk-0-part1                     scsi-0Google_PersistentDisk_es-gce-discovery-temp-disk
google-es-gce-discovery-temp-disk   scsi-0Google_PersistentDisk_es-gce-discovery-disk  scsi-0Google_PersistentDisk_persistent-disk-0
```

次にディスクフォーマットし、マウントします。

```bash
$ sudo mkdir /mnt/tmp
$ sudo mkfs.ext4 -F /dev/disk/by-id/google-es-gce-discovery-temp-disk 
$ sudo mount -o discard,defaults /dev/disk/by-id/google-es-gce-discovery-temp-disk /mnt/tmp
```

イメージをマウントします。

```bash
$ sudo mkdir /mnt/image-disk
$ sudo mount /dev/disk/by-id/google-es-gce-discovery-disk-part1 /mnt/image-disk
```

Elasticsearchのデータディレクトリを削除します。
Elasticsearchが初めて開始すると新しいインスタンス毎にElasticsearchのデータディレクトリが作成されます。

```bash
$ sudo rm -fR /mnt/image-disk/var/lib/elasticsearch/nodes
```

SSHキーを削除します。

```bash
$ sudo rm -f /mnt/image-disk/home/micci184/.ssh/authorized_keys
```

イメージをアンマウントします。

```bash
$ sudo umount /mnt/image-disk/
```

一時ディスク上のイメージディスクからdisk.rawを作成します。
disk.rawという名前である必要があります。

```bash
$ sudo dd if=/dev/disk/by-id/google-es-gce-discovery-disk of=/mnt/tmp/disk.raw bs=4096
```

disk.rawファイルのtarファイルを作成します。

```bash
$ cd /mnt/tmp
$ sudo tar czvf es-gce-discovery-disk.tar.gz disk.raw
```

バケットにtarファイルをコピーします。

```bash
$ gsutil cp /mnt/tmp/es-gce-discovery-disk.tar.gz gs://es-test-bucket/
```

ファイル転送が完了したら、一時インスタンスをログオフします。
最後に新しいインスタンスを作成するためのカスタムイメージを作成します。

```bash
$ gcloud compute images create es-gce-discovery-image \
  --source-uri=https://storage.googleapis.com/es-test-bucket/es-gce-discovery-disk.tar.gz
```

## Create instances based on our custom image

3つの新しいインスタンスを作成します。

```bash
$ gcloud compute instances create es-instance-1 \
  --machine-type=n1-standard-2 --subnet=default \
  --min-cpu-platform=Automatic --tags=elasticsearch,http-server,https-server \
  --image=es-gce-discovery-image --image-project=winter-jet-194500 \
  --boot-disk-size=20GB --boot-disk-type=pd-standard \
  --boot-disk-device-name=es-instance-1 --scopes=compute-ro

$ gcloud compute instances create es-instance-2 \
  --machine-type=n1-standard-2 --subnet=default \
  --min-cpu-platform=Automatic --tags=elasticsearch,http-server,https-server \
  --image=es-gce-discovery-image --image-project=winter-jet-194500 \
  --boot-disk-size=20GB --boot-disk-type=pd-standard \
  --boot-disk-device-name=es-instance-2 --scopes=compute-ro

$ gcloud compute instances create es-instance-3 \
  --machine-type=n1-standard-2 --subnet=default \
  --min-cpu-platform=Automatic --tags=elasticsearch,http-server,https-server \
  --image=es-gce-discovery-image --image-project=winter-jet-194500 \
  --boot-disk-size=20GB --boot-disk-type=pd-standard \
  --boot-disk-device-name=es-instance-3 --scopes=compute-ro
```

3台のインスタンスが作成できたら、Elasticsearchのサービスを起動します。
自動起動させたい場合は、[Elasticsearchの公式サイト](https://www.elastic.co/guide/en/elasticsearch/reference/current/deb.html)を参考にして設定してください。

```bash
$ gcloud compute ssh es-instance-1
$ sudo /etc/init.d/elasticsearch start
$ exit
$ gcloud compute ssh es-instance-2
$ sudo /etc/init.d/elasticsearch start
$ exit
$ gcloud compute ssh es-instance-3
$ sudo /etc/init.d/elasticsearch start
$ exit
```

es-instance-1インスタンスから、curlコマンドでクラスタの確認をします。
クラスタ内の3つのノードが表示されれば完了です。

```bash
$ curl es-instance-1:9200/_cat/nodes
10.142.0.4 12 30 4 0.09 0.14 0.08 mdi - es-instance-1
10.142.0.5 14 30 8 0.31 0.28 0.13 mdi * es-instance-2
10.142.0.6 15 30 8 0.34 0.29 0.13 mdi - es-instance-3
```

いかがでしたか？
GCP環境でもElasticsearchクラスタの環境を柔軟に構築できることがわかったかと思います。
ありがとうございました！

## 参考

[Getting Started with the GCE Discovery Plugin on Google Cloud](https://www.elastic.co/blog/getting-started-gce-discovery-plugin-on-google-cloud)
[Install Elasticsearch with Debian Package](https://www.elastic.co/guide/en/elasticsearch/reference/current/deb.html)
[Cloud SDKのインストール](https://cloud.google.com/sdk/downloads?hl=ja)