ZooKeeperクラスタのアーキテクチャと構築
ノードの役割分担
ZooKeeperアンサンブルを構成するサーバーノードは、主に以下の3つのロールに分類されます。
- Leader(リーダー):クラスタ内で唯一存在し、全ての書き込みリクエスト(トランザクション)を処理および調整するマスターノードです。
- Follower(フォロワー):読み取りリクエストを処理します。また、Leaderの選出投票に参加し、Leaderからのトランザクション提案を受信してコミット処理を行います。
- Observer(オブザーバー):読み取りリクエストの処理能力を拡張するために存在しますが、Leader選出における投票権を持ちません。これにより、選出プロセスのオーバーヘッドを増やさずにクラスタのスケーラビリティを向上させることが可能です。
クラスタ環境の構築手順
1. 一意識別子(myid)の設定
各ノードが自身を識別するためのIDファイルを作成します。ここでは、/opt/zookeeper-clusterディレクトリ配下に設定します。
# データディレクトリの作成とIDの付与
mkdir -p /opt/zookeeper-cluster/node-01/data
echo "1" > /opt/zookeeper-cluster/node-01/data/myid
mkdir -p /opt/zookeeper-cluster/node-02/data
echo "2" > /opt/zookeeper-cluster/node-02/data/myid
mkdir -p /opt/zookeeper-cluster/node-03/data
echo "3" > /opt/zookeeper-cluster/node-03/data/myid
# オブザーバーノード用
mkdir -p /opt/zookeeper-cluster/node-04/data
echo "4" > /opt/zookeeper-cluster/node-04/data/myid
2. 設定ファイル(zoo.cfg)の作成
各ノードごとに設定ファイルを定義します。以下は主要な設定項目の例です。ポイントとして、1つのノード(node-04)をオブザーバーとして設定します。
# node-01用設定 (zoo-1.cfg)
tickTime=2000
dataDir=/opt/zookeeper-cluster/node-01/data
clientPort=2181
initLimit=10
syncLimit=5
# server.[ID]=[ホストIP]:[選出ポート]:[コミュニケーションポート]
server.1=127.0.0.1:2888:3888
server.2=127.0.0.1:2889:3889
server.3=127.0.0.1:2890:3890
server.4=127.0.0.1:2891:3891:observer
他のノード(node-02, node-03, node-04)についても、dataDirとclientPortを重複しないように変更して同様のファイルを作成します。オブザーバーノードの設定ファイルにはpeerType=observerを明記する必要があります。
3. サービスの起動
設定ファイルを指定して、各ノードのZooKeeperサーバーを起動します。
./bin/zkServer.sh start ../conf/zoo-1.cfg
./bin/zkServer.sh start ../conf/zoo-2.cfg
./bin/zkServer.sh start ../conf/zoo-3.cfg
./bin/zkServer.sh start ../conf/zoo-4.cfg
4. ノード状態の確認
各ノードが正常に起動し、クラスタが形成されているかを確認します。
./bin/zkServer.sh status ../conf/zoo-1.cfg
出力結果には、そのノードが「mode: leader」または「mode: follower」(あるいは「mode: observer」)として機能していることが表示されます。
5. クライアントからの接続
クラスタ内のいずれかのアドレス、またはカンマ区切りで全アドレスを指定して接続を試みます。
./bin/zkCli.sh -server 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183,127.0.0.1:2184
ZABプロトコル(ZooKeeper Atomic Broadcast)の仕組み
ZooKeeperが分散環境でデータ整合性を維持するために採用しているコアプロトコルがZAB(ZooKeeper Atomic Broadcast)です。このプロトコルは、主に「クラスタの障害復旧(クラッシュリカバリ)」と「Leader/Follower間でのデータ同期」の2つの問題を解決します。
ノードの状態遷移
ZABプロトコルにおけるサーバーノードは、以下の4つの状態のいずれかをとります。
- Looking:クラスタのリーダーを選出している最中の状態。
- Leading:リーダーとして選出され、リクエストを処理している状態。
- Following:リーダー選出が完了し、フォロワーとして稼働している状態。
- Observing:オブザーバーとして稼働している状態。
リーダー選出プロセス
ZooKeeperクラスタでは、オブザーバーを除く有効ノード数(投票権を持つノード)を奇数にすることが推奨されます。これは、過半数(クォーラム)の取得を容易にするためです。リーダー選出は、クラスタ起動時や現在のリーダーがダウンした際に実行されます。
クラッシュリカバリ時の挙動
リーダーは定期的にフォロワーへハートビート(生存確認信号)を送信します。もしリーダーが障害で停止し、フォロワーがソケット接続の切断を検知すると、フォロワーはLooking状態へ移行し、新しいリーダーの選出を開始します。
なお、選出プロセスが実行されている期間は、一時的にクォーラムが満たされないため、クラスタは外部に対して書き込みサービスを提供できなくなります。これはRedisのSentinel(哨兵)メカニズムにおけるフェイルオーバー期間と類似しています。
データ同期と一貫性
クライアントがデータの書き込みを要求する場合、接続先がフォロワーであっても、そのリクエストはリーダーへ転送されます。リーダーは全ての書き込みリクエストをシリアライズし、トランザクションとして全フォロワーにブロードキャストします。これによりデータの整合性を担保します。読み取りリクエストについては、接続先のローカルノード(フォロワーまたはリーダー)で直接処理されます。データのコミットには「2相コミット(Two-Phase Commit)」の仕組みが用いられます。
ZooKeeperにおけるI/Oモデルの使い分け
ZooKeeperは用途に応じて通信方式を使い分けています。
- NIO(Non-blocking I/O):クライアントからのデフォルト接続ポート(例:2181)での通信に使用されます。また、クライアントが
Watch(イベント監視)を登録した際のサーバーからのコールバック通知も、NIOを用いて効率的に行われます。 - BIO(Blocking I/O):クラスタ内部でのリーダー選出時におけるノード間の投票通信や、初期接続確立のフェーズでは、信頼性重視のBIOが採用されることがあります。