こんにちは、fukuです。
フロムスクラッチでは主にアーキテクチャー周りの仕事に従事しています。
今回から数回に分けてApache ZooKeeper(以下ZooKeeper)について記事を書いて行こうと思います。ゆくゆくはソースコードレベルで解説を行なっていけたら良いかなと思っています(不安...)。1回目の今回はZooKeeperの紹介を行なっていきます。
昨今ではApache Hadoopをはじめとし、さまざまな分散処理フレームワークのOSSがありますが、そのいくつかの分散処理のシステム管理はZooKeeperを用いて行われています。
ZooKeeperとは

分散処理のアプリケーションではアプリケーションのロジックだけでなく、分散処理されるアプリケーションプロセス同士を協調させながら処理を行うことが重要になってきます。
ZooKeeperではそのような分散処理における協調動作を行うための分散コーディネーションサービスを提供します。
ZooKeeperはApacheのトッププロジェクトの1つであり、元々はGoogleのChubbyを元にYahooによって開発が行われました。
ZooKeeperではアプリケーションに対してAPIを提供して、APIを通して分散システムにおける構成管理や、リーダー選出、分散ロック、メッセージキューなどの用途に利用されます。
分散システムの制御
ここではZooKeeperを離れて、少し 分散システムの制御について記述します。下記の図のような一般的なHadoopなどのマスタ・ワーカの分散システムにては、以下のような事に関しての問題を考える必要があります。
マスタ障害
マスタに障害がおこると、ワーカ対するタスクの割り当てなどが行えなくなります。マスタに障害が発生した場合には、バックアップのマスタが処理を元のマスタの役割を引き継ぐ必要があります。さらにクラスタ状態の障害前の状態に戻すためには、元のマスタの状態を知る必要があります。当然、障害がおきたマスタ上でデータを管理していると、元のマスタの状態を知ることができなくなってしまいます。
ワーカ障害
ワーカの障害では、そのワーカに割り当てられているタスクに実行が完了しなくなります。そのためマスタはワーカの障害を検知して、障害がおこったワーカに割り当てていたタスクを、別のワーカに再度割り当てる必要があります。
また部分的にタスクが実行されていたりしていると、ワーカの障害がおこった場合に、部分的に実行された内容に関するリカバリなどを考慮する必要がある場合も考えられます。
通信障害
通信障害ではネットワークが分断されてワーカが マスタから切り離されることが考えられます。この場合に問題になるのはロックなどの同期処理への影響を考える必要があることです。分断されたワーカが何かしらの資源に対する排他ロックを行なっていた場合には対象のリソースが永久に解放されなくなってしまうかもしれません。
これらの分散システムにおける問題を扱うために ZooKeeperではさまざまな機能を提供しています。次はZooKeeperの基本的な機能について見ていきましょう。
ZooKeeperの基本
ZooKeeperではデータツリーと呼ばれるファイルシステムのような階層的なツリー構造を構成しています。ツリー構造の各ノードはznodeと呼ばれ、各znodeノードの名前は、スラッシュ(/)で区切られるパス要素によって識別されます。
以下にデータツリーの例を示します。図からわかるように一般的な木構造のデータ構造をとっており、ルートのznodeは"/app1"と"/app2"の2つの子ノードを持ってることがわかります。

さらに各znodeにはデータを格納することができ、データはバイト配列として格納されます。そのため多くのアプリケーションではProtocol BuffersやMessage Packなどのシリアライズ を行うためのパッケージを利用してデータを格納することが多いです。
znodeのモード
znodeにはモードと呼ばれる、znodeの振る舞いを変える機構があります。代表的なモードとして以下の4つがあります。
- PERSISTENT
- EPHEMERAL
- CONTAINER
- PERSISTENT_TTL
ここではそれぞれのモードの特徴について見ていきましょう。
PERSISTENT
PERSISTENTモードは永続化されるノードです。PERSISTENTなノードを削除する場合にはノードを削除するAPIを実行して、明示的に削除を行うしかありません。
PERSISTENTなノードはアプリケーションに関わるデータを保持して、ノードを作成したプロセスがクラッシュ・終了した場合でもデータを保持しなければならない場合などに用いられます。
EPHEMERAL
EPHEMERALモードは一時的なノードとして作成されます。ノードを作成したクライアントがクラッシュ(セッションが切れる)した場合や、ZooKeeperへのセッションをクローズしたりした場合に自動的に削除されます。
EPHEMERALなノードはノードを作成したセッションが維持されて間だけ存在するアプリケーション情報データを格納する場合に利用します。具体的にはマスタ・ワーカアーキテクチャ型のシステムにおけるマスタ情報などです。マスタはEPHEMERALなノードを作成しておくことで、マスタの生存を対象のノードが存在するかどうかのチェックを行うことで行うことができます。
CONTAINER
CONTAINERモードはバージョン3.5.1から提供されており、ガベージコレクションの機能をもつノードとなります。具体的にはCONTAINERなノードの子ノードがなくなると、ZooKeeperによって対象ノードは自動的に削除されます。
クラスターに参加するノード情報をCONTAINERなノードの子のEPHEMERAノードとして管理しておき、対象のクタスター全体を終了した場合などに、クラスタ情報全体を削除するといった利用方法が考えられます。
PERSISTENT_TTL
PERSISTENT_TTLモードはバージョン3.5.3から提供されており、期限付きの一時ノードとして作成されます。ノードに設定された期限がきた場合に自動的に削除されます(子ノードがない場合などの制限はあります)。
TTLなノードを利用することによって、タイムアウトをもつセッション情報を管理したりすることができます。
上記のモードを上手に使い分けながら、分散システムの状態を管理するのは、ZooKeeperを利用するアプリケーションの役割となります。
znodeの監視と通知
znodeの存在の有無や、データの変更などデータツリーの状態をチェックするために、毎回クライアントがアクセスを行うことを考えると、ZooKeeperに対して負荷がかかることが考えられます。
そのためZooKeeperではクライアントが状態をポーリングしなくてもよいように、データツリーの変更をクライアントに対して通知する機構があります。クライアントは特定のznodeに対しての変更などを通知してもらうために、監視(Watc
her)の設定を行う必要があります。ここで注意しなければならないのは、設定された監視は1度しか通知が行われないことです。そのため何度も通知を受けたい場合などは、通知を受け取ったのちに新しい監視情報を登録する必要があります。

ここで不安になるのが監視を設定して通知をされて時に、再度監視を設定する間に端子対象のznodeに対して変更などがあった場合です。

監視の設定では、監視を設定した時のznodeの状態も取得できるため、上記のようなパターンでは2度目の監視の設定の時に取得できるznodeの状態を見ることによって、対象のznodeの状態が変更されているかをチェックすることができます。
おわりに
今回はZooKeeperの概要から、データノードに関しての説明を行いました。ZooKeeperではあくまで分散システムの強調動作を実現するための基本機能の実装しか行われていないので、制御のためのロジックを実装するのはアプリケーション側の役割となります。
次回は実際にZooKeeperを動かしながらZooKeeperに対して、できることを紹介していく予定です。
ではでは...