ZooKeeperをDistributedModeで動かす

#ZooKeeperをDistributedModeで動かす

動作環境

|**Component**|**Version**|
|---|---|
| Centos | 6.6 |
| Oracle JDK | 7 |
[Oracle JDK7をインストールするにはこちら](http://qiita.com/soushiy/items/b664f67b7383edc2d7db)

##1 Zookeeperをインストール
###0.0 dockerコンテナから起動する場合、必要なツールをインストールします。
- 1 dockerコンテナを起動します。

~~~sh
docker run -itd --net=host -v /etc/localtime:/etc/localtime:ro -v /etc/zookeeper/config:/usr/local/zookeeper/conf -v /var/zookeeper/data:/usr/local/zookeeper/data -v /var/log/zookeeper:/usr/local/zookeeper/log --name storm  centos:6 /bin/bash;
docker attach storm;
~~~

- 2 javaなど必要なツールをインストールします。

~~~sh
su
yum -y install wget
yum -y install tar
~~~

###1.1 ダウンロードおよび解凍
~~~sh
mkdir /usr/local/download
cd /usr/local/download
wget http://www.webhostingreviewjam.com/mirror/apache/zookeeper/stable/zookeeper-3.4.6.tar.gz
tar xvf zookeeper-3.4.6.tar.gz
yes | mv -f /usr/local/download/zookeeper-3.4.6 /usr/local/download/zookeeper;
yes | cp -r -f /usr/local/download/zookeeper /usr/local/;
yes | rm -r -f /usr/local/download/zookeeper;
echo "export PATH=\$PATH:/usr/local/zookeeper/bin" >> /etc/profile;
export PATH=$PATH:/usr/local/zookeeper/bin
~~~

###1.2 Zookeeperの設定を行う(zoo.cfg)
~~~sh
cd /usr/local/zookeeper/conf/
yes | cp -p zoo_sample.cfg zoo.cfg
~~~

####1.2.1 データ保存ディレクトリの変更(zoo.cfg)
※後で移動しやすいようにデータも含めてzookeeper配下に作成するよう修正します。

~~~sh
sed -i -e 's/^dataDir/#dataDir/g' /usr/local/zookeeper/conf/zoo.cfg;
sed -i -e '/^#dataDir/a dataDir=\/usr\/local\/zookeeper\/data' /usr/local/zookeeper/conf/zoo.cfg;
~~~

####1.2.2 待ち受けポートの変更(zoo.cfg)

~~~sh
sed -i -e 's/^clientPort/#clientPort/g' /usr/local/zookeeper/conf/zoo.cfg;
sed -i -e '/^#clientPort/a clientPort=2181' /usr/local/zookeeper/conf/zoo.cfg;
~~~

###1.3 ログ出力ファイルを修正(usr/local/zookeeper/conf/log4j.properties)
次はログ出力設定ファイル（log4j.properties）を修正します。
なにせ、初期表示だとコンソールにしかメッセージが出ないからです。

とりあえず下記のように設定しました。

~~~sh
cp log4j.properties log4j.properties.bak
# zookeeper.root.logger=INFO, CONSOLE
sed -i -e 's/^zookeeper\.root\.logger/#zookeeper\.root\.logger/g' /usr/local/zookeeper/conf/log4j.properties;
sed -i -e '/^#zookeeper\.root\.logger/a zookeeper\.root\.logger=INFO, CONSOLE' /usr/local/zookeeper/conf/log4j.properties;

# zookeeper.console.threshold=INFO
sed -i -e 's/^zookeeper\.console\.threshold/#zookeeper\.console\.threshold/g' /usr/local/zookeeper/conf/log4j.properties;
sed -i -e '/^#zookeeper\.console\.threshold/a zookeeper\.console\.threshold=INFO' /usr/local/zookeeper/conf/log4j.properties;

# zookeeper.log.dir=/usr/local/zookeeper/log/
sed -i -e 's/^zookeeper\.log\.dir/#zookeeper\.log\.dir/g' /usr/local/zookeeper/conf/log4j.properties;
sed -i -e '/^#zookeeper\.log\.dir/a zookeeper\.log\.dir=\/usr\/local\/zookeeper\/log\/' /usr/local/zookeeper/conf/log4j.properties;


# zookeeper.log.file=zookeeper.log
sed -i -e 's/^zookeeper\.log\.file/#zookeeper\.log\.file/g' /usr/local/zookeeper/conf/log4j.properties;
sed -i -e '/^#zookeeper\.log\.file/a zookeeper\.log\.file=zookeeper.log' /usr/local/zookeeper/conf/log4j.properties;

# zookeeper.log.threshold=DEBUG
sed -i -e 's/^zookeeper\.log\.threshold/#zookeeper\.log\.threshold/g' /usr/local/zookeeper/conf/log4j.properties;
sed -i -e '/^#zookeeper\.log\.threshold/a zookeeper\.log\.threshold=DEBUG' /usr/local/zookeeper/conf/log4j.properties;

# zookeeper.tracelog.dir=/usr/local/zookeeper/log/
sed -i -e 's/^zookeeper\.tracelog\.dir/#zookeeper\.tracelog\.dir/g' /usr/local/zookeeper/conf/log4j.properties;
sed -i -e '/^#zookeeper\.tracelog\.dir/a zookeeper\.tracelog\.dir=\/usr\/local\/zookeeper\/log\/' /usr/local/zookeeper/conf/log4j.properties;

# zookeeper.tracelog.file=zookeeper_trace.log
sed -i -e 's/^zookeeper\.tracelog\.file/#zookeeper\.tracelog\.file/g' /usr/local/zookeeper/conf/log4j.properties;
sed -i -e '/^#zookeeper\.tracelog\.file/a zookeeper\.tracelog\.file=zookeeper_trace.log' /usr/local/zookeeper/conf/log4j.properties;

# DEFAULT: console appender only
#log4j.rootLogger=${zookeeper.root.logger}
sed -i -e 's/^log4j\.rootLogger/#log4j\.rootLogger/g' /usr/local/zookeeper/conf/log4j.properties;

# Rolling log file
sed -i -e 's/^#log4j\.rootLogger=DEBUG, CONSOLE, ROLLINGFILE/log4j\.rootLogger=DEBUG, CONSOLE, ROLLINGFILE/' /usr/local/zookeeper/conf/log4j.properties;

~~~
###1.4 データ保存用フォルダおよびログ出力用フォルダを作成
~~~sh
mkdir /usr/local/zookeeper/data/
mkdir /usr/local/zookeeper/log/
~~~

###1.5 設定が終わったので起動
※パスが通っているので、直接zkServer.shで起動できる。

~~~sh
zkServer.sh start
#>JMX enabled by default
#>Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
#>Starting zookeeper ... STARTED
~~~

###1.6 正常に起動できたかを確認する
※ncコマンドが無い場合は、yumでインストールする。

~~~sh
yum -y install nc
~~~

####1.6.1 ncコマンドを使って、zookeeperの待ち受けポートである2181番ポートに対して、「ruok(Are you OK?)」というメッセージを送って動作確認をする。
※imokというメッセージが帰ってくればOK

~~~sh
echo ruok | nc localhost 2181
#>imok
~~~

#2 ZooKeeperをDistributed modeで起動する
- *ローカル環境(例:Macbook)で実行すること。*

###2.1 Zookeeperの設定を変更する。
こちらを[参照 ZooKeeper管理者ガイド](http://oss.infoscience.co.jp/hadoop/zookeeper/docs/r3.3.1/zookeeperAdmin.html)

####2.1.1 Zookeeper設定ファイル(zoo.cfg)をローカルにダウンロードします。

~~~sh
mkdir ~/tmp/
scp agent-master1:/etc/zookeeper/config/zoo.cfg ~/tmp/zoo.cfg
~~~

####2.1.2 zoo.cfgに、アンサンブルを構成する全サーバーの場所を入力する。
ZooKeeper アンサンブルを構成する各マシンは、アンサンブル内のほかのすべてのマシンについても知っている必要があります。これを行うのが、server.id=host:port:port という形式で記述された一連の行です。パラメータ host と port については、説明の必要はないでしょう。各マシンにサーバー id を割り当てるには、サーバーごとに 1 つ、myid という名前のファイルを、そのサーバーのデータディレクトリに作成します。データディレクトリは、設定ファイルのパラメータ dataDir で指定します。

####2.1.2.1 環境変数にサーバーのIPアドレスを入力する。

~~~sh
SERVER_1=agent-master1:192.168.56.201:2888:3888
SERVER_2=agent-master2:192.168.56.202:2888:3888
SERVER_3=agent-master3:192.168.56.203:2888:3888
SERVER_4=agent-cluster1:192.168.56.171:2888:3888
SERVER_5=agent-cluster2:192.168.56.172:2888:3888
SERVER_6=agent-cluster3:192.168.56.173:2888:3888
SERVER_7=agent-cluster4:192.168.56.174:2888:3888
~~~

####各マシンの/usr/local/zookeeper/conf/zoo.cfgの設定
####注意！IPアドレスの代わりにホスト名を入れてはいけません。

~~~sh
echo "server.1=`echo $SERVER_1 | awk -F ':' '{print $2":"$3":"$4}'`" >> ~/tmp/zoo.cfg
echo "server.2=`echo $SERVER_2 | awk -F ':' '{print $2":"$3":"$4}'`" >> ~/tmp/zoo.cfg
echo "server.3=`echo $SERVER_3 | awk -F ':' '{print $2":"$3":"$4}'`" >> ~/tmp/zoo.cfg
echo "server.4=`echo $SERVER_4 | awk -F ':' '{print $2":"$3":"$4}'`" >> ~/tmp/zoo.cfg
echo "server.5=`echo $SERVER_5 | awk -F ':' '{print $2":"$3":"$4}'`" >> ~/tmp/zoo.cfg
echo "server.6=`echo $SERVER_6 | awk -F ':' '{print $2":"$3":"$4}'`" >> ~/tmp/zoo.cfg
echo "server.7=`echo $SERVER_7 | awk -F ':' '{print $2":"$3":"$4}'`" >> ~/tmp/zoo.cfg
~~~

#####server.x=[hostname]:nnnnn[:nnnnn], etc
(No Java system property)
servers making up the ZooKeeper ensemble. When the server starts up, it determines which server it is by looking for the file myid in the data directory. That file contains the server number, in ASCII, and it should match x in server.x in the left hand side of this setting.
The list of servers that make up ZooKeeper servers that is used by the clients must match the list of ZooKeeper servers that each ZooKeeper server has.
There are two port numbers nnnnn. The first followers use to connect to the leader, and the second is for leader election. The leader election port is only necessary if electionAlg is 1, 2, or 3 (default). If electionAlg is 0, then the second port is not necessary. If you want to test multiple servers on a single machine, then different ports can be used for each server.
electionAlgのデフォルト値は3です。

####2.1.3 zoo.cfgをscpで他のサーバーにコピーする。

~~~sh
echo "1" > ~/tmp/myid; scp ~/tmp/myid `echo $SERVER_1 | awk -F ':' '{print $1}'`:/var/zookeeper/data/myid;
scp ~/tmp/zoo.cfg `echo $SERVER_1 | awk -F ':' '{print $1}'`:/etc/zookeeper/config/zoo.cfg;

echo "2" > ~/tmp/myid; scp ~/tmp/myid `echo $SERVER_2 | awk -F ':' '{print $1}'`:/var/zookeeper/data/myid;
scp ~/tmp/zoo.cfg `echo $SERVER_2 | awk -F ':' '{print $1}'`:/etc/zookeeper/config/zoo.cfg;

echo "3" > ~/tmp/myid; scp ~/tmp/myid `echo $SERVER_3 | awk -F ':' '{print $1}'`:/var/zookeeper/data/myid;
scp ~/tmp/zoo.cfg `echo $SERVER_3 | awk -F ':' '{print $1}'`:/etc/zookeeper/config/zoo.cfg;

echo "4" > ~/tmp/myid; scp ~/tmp/myid `echo $SERVER_4 | awk -F ':' '{print $1}'`:/var/zookeeper/data/myid;
scp ~/tmp/zoo.cfg `echo $SERVER_4 | awk -F ':' '{print $1}'`:/etc/zookeeper/config/zoo.cfg;

echo "5" > ~/tmp/myid; scp ~/tmp/myid `echo $SERVER_5 | awk -F ':' '{print $1}'`:/var/zookeeper/data/myid;
scp ~/tmp/zoo.cfg `echo $SERVER_5 | awk -F ':' '{print $1}'`:/etc/zookeeper/config/zoo.cfg;

echo "6" > ~/tmp/myid; scp ~/tmp/myid `echo $SERVER_6 | awk -F ':' '{print $1}'`:/var/zookeeper/data/myid;
scp ~/tmp/zoo.cfg `echo $SERVER_6 | awk -F ':' '{print $1}'`:/etc/zookeeper/config/zoo.cfg;

echo "7" > ~/tmp/myid; scp ~/tmp/myid `echo $SERVER_7 | awk -F ':' '{print $1}'`:/var/zookeeper/data/myid;
scp ~/tmp/zoo.cfg `echo $SERVER_7 | awk -F ':' '{print $1}'`:/etc/zookeeper/config/zoo.cfg;
~~~

###2.2 クラスタの起動

####2.2.1 Zookeeperを起動する。
#####Zookeeperのコマンドの説明は[こちら](http://blog.withsin.net/tag/zookeeper-2/page/2/)が詳しい。
---
###*クライアントとなるvm2〜vm5上で実行すること。*
---

~~~sh
zkServer.sh start
echo ruok | nc localhost 2181
#>imok
~~~

####2.2.2 ZooKeeper状態確認方法
#####2.2.2.1 4文字コマンドを使う
使い方

~~~sh
echo ruok | nc localhost 2181
#>imok
~~~

####Zookeeperの状態を知るための4文字コマンドリスト
コマンド集は[こちら](http://oss.infoscience.co.jp/hadoop/zookeeper/docs/r3.3.1/zookeeperAdmin.html#sc_zkCommands)

|**command**|**description**|
|-----------|---------------|
| conf |**New in 3.3.0:** Print details about serving configuration.|
| cons |**New in 3.3.0**: List full connection/session details for all clients connected to this server. Includes information on numbers of packets received/sent, session id, operation latencies, last operation performed, etc...|
| crst |**New in 3.3.0**: Reset connection/session statistics for all connections.|
| dump |Lists the outstanding sessions and ephemeral nodes. This only works on the leader.|
| envi |Print details about serving environment|
| **ruok** |Tests if server is running in a non-error state. The server will respond with imok if it is running. Otherwise it will not respond at all. A response of "imok" does not necessarily indicate that the server has joined the quorum, just that the server process is active and bound to the specified client port. Use "stat" for details on state wrt quorum and client connection information.|
| srst |Reset server statistics.|
| srvr |**New in 3.3.0:** Lists full details for the server.|
| stat |Lists brief details for the server and connected clients.|
| wchs |**New in 3.3.0:** Lists brief information on watches for the server.|
| wchc |**New in 3.3.0:** Lists detailed information on watches for the server, by session. This outputs a list of sessions(connections) with associated watches (paths). Note, depending on the number of watches this operation may be expensive (ie impact server performance), use it carefully.|
| wchp |**New in 3.3.0:** Lists detailed information on watches for the server, by path. This outputs a list of paths (znodes) with associated sessions. Note, depending on the number of watches this operation may be expensive (ie impact server performance), use it carefully.|

#####2.2.2.2 zkServer.shのstatusコマンドを使う
VM1の場合、Mode: leaderとなっている。

~~~sh
zkServer.sh status
#>JMX enabled by default
#>Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
#>Mode: leader
~~~
VM2〜5の場合、Mode: followerとなっている。

~~~sh
zkServer.sh status
#>JMX enabled by default
#>Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
#>Mode: follower
~~~

#####2.2.2.3 zkCli.shで同期されているかを確認
#####2.2.2.3.1 ZooKeeperのシェルにログイン
~~~sh
zkCli.sh -server localhost:2181
#Zookeeperのシェルに入る。(下記がシェルに入った状態)
[zk: localhost:2181(CONNECTED) 0]
~~~

#####2.2.2.3.2 lsコマンドで、現在同期中のフォルダをls

~~~sh
[zk: localhost:2181(CONNECTED) 0]ls /
[storm, zookeeper]
~~~
#####2.2.2.3.3 createコマンドで、testフォルダを追加

~~~ sh
[zk: localhost:2181(CONNECTED) 1]create /test dummy
Created /test
[zk: localhost:2181(CONNECTED) 2] ls /
[storm, test, zookeeper]
~~~

#####2.2.2.3.4 他の端末(例:VM2)で、同様にlsコマンドを打ってみる
※こちらに、testが追加されていれば、同期成功

~~~sh
[zk: localhost:2181(CONNECTED) 0]ls /
[storm, test, zookeeper]
~~~

#####2.2.2.3.5 最後に、deleteコマンドで、フォルダを削除(後片付け)

~~~sh
[zk: localhost:2181(CONNECTED) 0]delete /test
~~~

---
####Zookeeperがうまく動いていない場合の確認項目
---
- SELINUX,iptablesがオフになっていること。
- 時刻があっているか(ntpd)
- vm1上で、/usr/local/zookeeper/conf/zoo.cfgのserver.?=IPアドレスとなっているかどうか。(ホスト名ではうまく動かない可能性有(要確認))
- コンテナの場合や、複数のI/Fを持っている場合に、zookeeperがbindするアドレスを指定してあるかどうか。

/usr/local/zookeeper/conf/zoo.cfg

~~~sh
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/usr/local/zookeeper/data
clientPort=2181
clientPortBindAddress=192.168.56.201
server.1=192.168.56.201:2888:3888
server.2=192.168.56.202:2888:3888
server.3=192.168.56.203:2888:3888
~~~

- vm2〜5上に、/usr/local/zookeeper/data/myidファイルを作成すること。(myidファイルの中身は、server.1で指定した。ノード番号(例:server.1のマシンの場合、1))

/usr/local/zookeeper/data/myid

~~~sh
1
~~~

---
#全ての実行コード
- 第2章以降は、手動で行うこと。(別のVM上で操作しなければいけない)