ZooKeeper Ensemble を Vagrant で構築した


[前回](https://qiita.com/maitake9116/items/e76e70d306dd60443fcd) ZooKeeper を Standalone で構築しました．
次は ZooKeeper も冗長構成を取れるように ZooKeeper Ensemble を構築します．
ZooKeeper のバージョンは 3.4.14 です．
Vagrant の使い方は [Vagrant 入門](https://qiita.com/maitake9116/items/be27706a15f3a96a2209) をご覧ください．

## ZooKeeper Ensemble

ZooKeeper の可用性を高めるために複数台で Ensemble 構成を取ります．
ZooKeeper では Ensemble の過半数が動作していることがサービスの継続条件のため，奇数台で構築するのがベストなようです．

**例**

4 台のマシンで ZooKeeper Ensemble を構築した場合，1 台のマシンがダウンしても残り 3 台で過半数となるためサービスを継続することができますが，
2 台のマシンがダウンすると残り 2 台で過半数にならないためサービスを継続できなくなってしまいます．
つまり 1 / 4，25 % のダウンを許容します．


![zke4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246211/a82901d0-5a90-55ac-6a37-7f1bfb011a68.png)


5 台のマシンで ZooKeeper Ensemble を構築した場合，2 台のマシンがダウンしても残り 3 台で過半数となるためサービスを継続することができますが，
3 台のマシンがダウンすると残り 2 台で過半数にならないためサービスを継続できなくなってしまいます．
つまり 2 / 5，40 % のダウンを許容します．

![zke5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246211/cefa7d41-3120-1f75-80d0-8552a0bf8d18.png)


6 台のマシンで ZooKeeper Ensemble を構築した場合，2 台のマシンがダウンしても残り 4 台で過半数となるためサービスを継続することができますが，
3 台のマシンがダウンすると残り 3 台で過半数にならないためサービスを継続できなくなってしまいます．
つまり 2 / 6，33 % のダウンを許容します．

![zke6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246211/983b6031-7688-3f53-8940-fa5dc4c92444.png)


このことから奇数台で構築したほうがマシンを有効活用できると言えそうです．


## 構成

相変わらずシェルでどうにかします．

```text
D:\vagrant\zkcluster
┣Vagrantfile
┗create_zkcluster.sh
```

### Vagrantfile

- OS は CentOS 7 を使用します．
- VM 間で通信できるように hosts や no_proxy を設定します．
- 任意の数 ZooKeeper を作成できるようにします．

```ruby
# ZooKeeper数
zk_cnt = 3

Vagrant.configure("2") do |config|
  config.vm.box = "centos/7"

  #--- Proxy設定 ---#
  no_proxy_address = "localhost,127.0.0.1"
  (1..zk_cnt).each do |i|
    no_proxy_address += ",zk#{i},192.168.33.1#{i}"
  end
  config.proxy.enabled  = true
  config.proxy.http     = "http://user_id:password@proxy_address:proxy_port"
  config.proxy.https    = "http://user_id:password@proxy_address:proxy_port"
  config.proxy.no_proxy = no_proxy_address
  #--- /Proxy設定 ---#
  
  #--- ZooKeeper Ensemble 構築 ---#
  (1..zk_cnt).each do |i|
    config.vm.define "zk#{i}" do | zk |
      zk.vm.hostname = "zk#{i}"
      zk.vm.network "private_network", ip: "192.168.33.4#{i}", virtualbox__intnet: "solrcloud"
      zk.vm.provision :hosts, :sync_hosts => true
      zk.vm.provision :shell, path: "./create_zkensemble.sh", args: "#{i} #{zk_cnt}"
    end
  end
  #--- /ZooKeeper Ensemble 構築 ---#

end
```

### create_zkensemble.sh

ZooKeeper で Ensemble 構成をとるには以下の設定が必要です．

- zoo.cfg に Ensemble を構成するサーバーの情報を「server.id=host:port:port」の形式で記載する．
- dataDir 配下に id(1～255の数字) が記載されたファイル myid を用意する．

```bash
# Java をインストールする
yum install -y java-1.8.0-openjdk

# ZooKeeper を構築する
cd /var/tmp
yum install -y wget
wget http://ftp.jaist.ac.jp/pub/apache/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz
tar xvf zookeeper-3.4.14.tar.gz
cp -ar zookeeper-3.4.14 /opt/
ln -s /opt/zookeeper-3.4.14 /opt/zookeeper

# ZooKeeper 用のユーザを作成する
groupadd zookeeper
useradd -g zookeeper -d /opt/zookeeper -s /sbin/nologin zookeeper
chown -R zookeeper.zookeeper /opt/zookeeper/*

# ZooKeeperの設定ファイルを用意する
cd /opt/zookeeper/conf
cp zoo_sample.cfg zoo.cfg

# dataDir を tmp から var に変更する
mkdir /var/lib/zookeeper
chown zookeeper.zookeeper /var/lib/zookeeper
cd /opt/zookeeper/conf
sed -i -e 's#dataDir=.*$#dataDir=/var/lib/zookeeper#' zoo.cfg

# ZooKeeper アンサンブルの設定を追記する
# $1 : ZooKeeper のノードID
# $2 : ZooKeeper アンサンブルの構成ノード数
echo "#ZooKeeper Ensemble" >> /opt/zookeeper/conf/zoo.cfg
for i in `seq 1 $2`
do
  if [ $i = $1 ] ; then
  # ノードIDが自分のものと一致する場合は 0.0.0.0 とする．
    echo "server.${i}=0.0.0.0:2888:3888" >> /opt/zookeeper/conf/zoo.cfg
  else
    echo "server.${i}=192.168.33.1${i}:2888:3888" >> /opt/zookeeper/conf/zoo.cfg
  fi
done
# dataDir にノードIDが記載された myid ファイルを用意する必要がある
echo $1 | sudo tee /var/lib/zookeeper/myid

# ZooKeeper を起動する
cd /opt/zookeeper
bin/zkServer.sh start

# Port を開放する
systemctl enable firewalld.service
systemctl start firewalld.service
firewall-cmd --zone=public --add-port=2181/tcp --add-port=2888/tcp --add-port=3888/tcp --permanent
firewall-cmd --reload

# 終了ステータスが 0 でない場合に Vagrant が異常終了とみなし VM が連続生成されないことがあるので明示する
exit 0
```


## ZooKeeper Ensemble を構築する

```powershell
> pwd
D:\vagrant\zkensemble
> vagrant up
```

## 動作確認

ZooKeeper のステータスを見てみます．

```powershell
> vagrant ssh zk1 -c "sudo /opt/zookeeper/bin/zkServer.sh status"
ZooKeeper JMX enabled by default
Using config: /opt/zookeeper/bin/../conf/zoo.cfg
Mode: follower
> vagrant ssh zk2 -c "sudo /opt/zookeeper/bin/zkServer.sh status"
ZooKeeper JMX enabled by default
Using config: /opt/zookeeper/bin/../conf/zoo.cfg
Mode: leader
> vagrant ssh zk3 -c "sudo /opt/zookeeper/bin/zkServer.sh status"
ZooKeeper JMX enabled by default
Using config: /opt/zookeeper/bin/../conf/zoo.cfg
Mode: follower
```

leader や follower が自動で選出されました．

## 可用性

では leader を停止してみます．

```powershell
> vagrant zk2 halt
```

再度 ZooKeeper のステータスを見ます．

```powershell
> vagrant ssh zk1 -c "sudo /opt/zookeeper/bin/zkServer.sh status"
ZooKeeper JMX enabled by default
Using config: /opt/zookeeper/bin/../conf/zoo.cfg
Mode: follower
> vagrant ssh zk3 -c "sudo /opt/zookeeper/bin/zkServer.sh status"
ZooKeeper JMX enabled by default
Using config: /opt/zookeeper/bin/../conf/zoo.cfg
Mode: leader
```

無事 leader が選出されました．
このように Ensemble 構成を取ることで ZooKeeper 自体の可用性が向上します．