Apache > Hadoop > ZooKeeper
 

ZooKeeper

ZooKeeper: 分散アプリケーションのための分散コーディネーションサービス

ZooKeeper は、分散アプリケーションのためのオープンソースの分散コーディネーションサービスです。分散アプリケーションでは、ZooKeeper に用意されている一連のシンプルなプリミティブを使うことで、同期化、設定保守、およびグループと名前付けのための高度なサービスを実装することができます。ZooKeeper はプログラミングが容易に行えるように設計されており、誰でも知っているファイルシステムのディレクトリツリー構造に似たデータモデルを採用しています。ZooKeeper は Java で動作し、Java と C のためのバインディングが用意されています。

コーディネーションサービスを適切に動作させることが難しいのはよく知られています。特に問題なのは、レースコンディションやデッドロックといったエラーが起こりやすいことです。このようなコーディネーションサービスを、分散アプリケーションの開発者がゼロから作成しなくても済むようにしたい、これが ZooKeeper 開発の出発点です。

設計目標

ZooKeeper はシンプルです。ZooKeeper を利用すれば、ファイルシステムに似た構造を持つ階層的な共有名前空間を通じて、複数の分散プロセスが互いに協調しあうことができます。名前空間はデータレジスタ (ZooKeeper 用語では znode と呼びます) から構成されていますが、これらのデータレジスタがファイルとディレクトリに相当します。データの保管を目的として設計された一般的なファイルシステムとは異なり、ZooKeeper のデータはメモリ内に保持されます。ZooKeeper が高いスループットと低遅延を実現しているのもそのためです。

ZooKeeper の実装では、高いパフォーマンス、高可用性、厳密に順序付けられたアクセスが重視されています。ZooKeeper はパフォーマンスが高いので、大規模な分散システムで使用できます。ZooKeeper は信頼性が高いので、単一障害点 (SPOF) になりません。ZooKeeper では厳密な順序付けを行うので、高度な同期プリミティブをクライアント側で実装できます。

ZooKeeper はレプリケートされます。ZooKeeper は、サービスの提供対象である分散プロセスがそうであるのと同様に、一連のホスト (これらのホストをまとめてアンサンブルと呼びます) を対象にレプリケートされることを想定して作成されています。

ZooKeeper サービス

ZooKeeper サービスを構成するサーバー群は、いずれも互いのことを知っています。サーバー群は、状態についてのイメージをメモリ内に保持しており、トランザクションログとスナップショットは永続的ストアに格納します。サーバーの過半数が動作している限り、ZooKeeper サービスも利用可能です。

クライアントは、1 つの ZooKeeper サーバーに接続します。クライアントは TCP 接続を保持し、この接続を通じてリクエストを送信し、レスポンスを取得し、ウォッチイベントを取得し、ハートビートを送信します。サーバーへの TCP 接続が切断されると、クライアントは別のサーバーに接続します。

ZooKeeper は順序付けされています。ZooKeeper は、すべての ZooKeeper トランザクションの順序を表す番号を付けて更新を行います。以後の操作では、この番号を利用することで、同期化プリミティブなどの高度な抽象化を実装することができます。

ZooKeeper は高速です。負荷が「読み込み主体」の場合には、特に高速です。ZooKeeper アプリケーションは数千台のマシンでも実行でき、読み込みが書き込みより多く、その割合が 10:1 の場合に最も高いパフォーマンスを発揮します。

データモデルと階層的な名前空間

ZooKeeper が提供する名前空間は、一般的なファイルシステムに非常によく似ています。名前は、スラッシュ (/) で区切られたパス要素の並びです。ZooKeeper の名前空間のすべてのノードは、パスによって識別されます。

ZooKeeper の階層的な名前空間

ノードとエフェメラルノード

一般的なファイルとは異なり、ZooKeeper 名前空間内の各ノードは、そのノードに関連付けられたデータのほかに、複数の子を持つことができます。たとえて言えば、ファイルが同時にディレクトリにもなれるファイルシステムのようなものです。(ZooKeeper はコーディネーションデータ、すなわちステータス情報、設定、ロケーション情報などを格納する目的で設計されているので、各ノードに格納されるデータは一般に小さく、バイトからキロバイトのオーダーです。) ZooKeeper のドキュメントでは、ZooKeeper のデータノードに関する記述であることを明確に示すために、znode という用語を使います。

znode が保持する Stat 構造体には、データ変更と ACL 変更のバージョン番号に加え、タイムスタンプが含まれており、ZooKeeper ではこれらの値を利用することで、キャッシュの有効性検証と更新のコーディネートが可能になっています。znode のデータが変更されると、そのたびにバージョン番号は増えていきます。たとえば、クライアントがデータを取得するとき、クライアントはデータのほかにデータのバージョンも一緒に受け取ります。

1 つの名前空間内の各 znode に格納されたデータの読み書きはアトミックに行うことができます。読み取りでは、1 つの znode に関連付けられているすべてのデータバイトが取得され、書き込みではすべてのデータが置き換えられます。各ノードはアクセス制御リスト (ACL) を持っており、このアクセス制御リストで、誰がどんな操作を行えるかが制限されます。

ZooKeeper には、エフェメラルノード (ephemeral node: 一時ノード) という概念もあります。これらの znode は、znode を作成したセッションがアクティブになっている間だけ存在します。セッションが終了すると、znode は削除されます。エフェメラルノードは、[準備中] を実装するときに便利です。

条件付き更新とウォッチ

ZooKeeper はウォッチのコンセプトをサポートしています。クライアントは znode にウォッチを設定できます。znode に変更があると、ウォッチがトリガされ、その後ウォッチは削除されます。ウォッチがトリガされると、クライアントは znode が変更されたことを示すパケットを受け取ります。また、クライアントといずれかの ZooKeeper サーバーとの間の接続が切断された場合、クライアントはローカルな通知を受け取ります。これらのウォッチは、[準備中] に使用できます。

保証

ZooKeeper は非常に高速であると同時にきわめてシンプルです。ただし、ZooKeeper の目的は、同期化などのもっと複雑なサービスを構築するための基礎を提供することなので、いくつか保証されていることがあります。具体的には次のとおりです。

  • 順序一貫性 - クライアントによる複数の更新は、これらの更新が送信された順に適用されます。

  • アトミック性 - 更新は成功するか、失敗するかのいずれかです。その中間の結果になることはありません。

  • シングルシステムイメージ - どのサーバーに接続しているかに関係なく、クライアントから見えるサービスのイメージは同じです。

  • 信頼性 - いったん更新が適用されたら、その時点から次にクライアントがその更新を上書きするまで、更新内容は存在し続けます。

  • 適時性 - クライアントから見えるシステムは、一定期限内において最新のものであることが保証されます。

これらの保証内容の詳細と、どのような利用方法があるかについては、[準備中] を参照してください。

シンプルな API

ZooKeeper の設計目標の 1 つは、きわめてシンプルなプログラミングインタフェースを提供することです。このため、ZooKeeper では、次の操作だけをサポートしています。

create

ツリー内のある場所にノードを作成します。

delete

ノードを削除します。

exists

ある場所にノードが存在するかどうかテストします。

get data

ノードからデータを読み取ります。

set data

ノードにデータを書き込みます。

get children

ノードの子のリストを取得します。

sync

データが伝播するまで待ちます。

これらの API の詳細と、これらの API を利用して高度な操作を実装する方法については、[準備中] を参照してください。

実装

次に示す ZooKeeper のコンポーネントの図は、ZooKeeper サービスのハイレベルなコンポーネントを示したものです。このうちリクエストプロセッサだけは例外ですが、ZooKeeper サービスを構成するサーバーのそれぞれは、各コンポーネントの独自のコピーをレプリケートして持っています。

ZooKeeper のコンポーネント

レプリケートされたデータベースは、データツリー全体を含むインメモリデータベースです。更新は、あとで修復できるようにディスクに記録され、書き込みはディスクに直列化されてから、インメモリデータベースに適用されます。

どの ZooKeeper サーバーもクライアントにサービスを提供します。クライアントが接続するサーバーは必ず 1 つに決まっており、接続した先のサーバーにリクエストを送信します。読み込みリクエストに対しては、各サーバーデータベースのローカルレプリカからレスポンスが返されます。サービスの状態を変更するリクエスト、書き込みリクエストは、合意プロトコルによって処理されます。

合意プロトコルの一部として、クライアントからのすべての書き込みリクエストは 1 つのサーバーに転送されます。このサーバーは、リーダーと呼ばれます。残りのすべての ZooKeeper サーバーはフォロワーと呼ばれ、リーダーからのメッセージ提案を受け取って、メッセージ配信について合意します。メッセージングレイヤは、障害発生時のリーダーの交代と、フォロワーとリーダーとの同期化を担当するレイヤです。

ZooKeeper では、独自のアトミックなメッセージングプロトコルを使います。メッセージングレイヤがアトミックなので、ZooKeeper ではローカルレプリカに決して矛盾が生じないことを保証できます。書き込みリクエストを受け取ったリーダーは、書き込みが適用されたときのシステムの状態を計算し、これをトランザクションに変換して、新しい状態を取り込みます。

使用

ZooKeeper のプログラミングインタフェースは、意図的にシンプルになっています。ただし、このプログラミングインタフェースを使うことで、同期化プリミティブ、グループメンバーシップ、オーナーシップなどの高度な操作を実装することができます。分散アプリケーションの中には、このインタフェースを使って [準備中: ホワイトペーパーと動画プレゼンテーションから使用例を追加] をしているものもあります。詳細については、[準備中] を参照してください。

パフォーマンス

ZooKeeper は高いパフォーマンスを発揮するよう設計されています。本当にそうでしょうか。Yahoo! Research の ZooKeeper 開発チームが調べた結果では、実際にそうであることが示されています (次の「読み取り/書き込み比率の変化による ZooKeeper スループットの変化」を参照してください)。ZooKeeper は、書き込みより読み込みの方が圧倒的に多いアプリケーションで特に高いパフォーマンスを発揮します。これは、書き込みではすべてのサーバーの状態を同期化する必要があるためです (通常、コーディネーションサービスの場合は、書き込みより読み込みの方が圧倒的に多くなります)。

読み取り/書き込み比率の変化による ZooKeeper スループットの変化

上の「読み取り/書き込み比率の変化による ZooKeeper スループットの変化」の図は、2GHz Dual Xeon と 2 台の SATA 15K RPM のドライブを使ったサーバー群で ZooKeeper リリース 3.2 を実行した場合のスループットの変化を示しています。ドライブのうち 1 台は、ZooKeeper 専用ログデバイスとして使っています。スナップショットは、OS ドライブに書き込んでいます。書き込みリクエストは 1 KB の書き込み、読み込みリクエストは 1 KB の読み込みでした。サーバー ("servers") は ZooKeeper アンサンブルの規模、すなわち、サービスを構成するサーバーの台数を示しています。これらのサーバー以外に、30 台ほどのサーバーを使って、クライアントをシミュレートしました。ZooKeeper アンサンブルは、リーダーがクライアントからの接続を許可しないように構成しました。

バージョン 3.2 では、その前の 3.1 リリースに比べ、読み取り/書き込みのパフォーマンスは 2 倍近く向上しました。

数々のベンチマークでは、信頼性も高いことが示されています。下の「エラー発生時の信頼性」の図は、さまざまな障害に対してデプロイメントがどう反応したかを示しています。図中の丸で囲んだ番号は、次のようなイベントに対応しています。

  1. フォロワー 1 台に障害が発生したのち復旧。

  2. 別のフォロワー 1 台に障害が発生したのち復旧。

  3. リーダーに障害が発生。

  4. 2 台のフォロワーに障害が発生したのち復旧。

  5. また別のリーダーに障害が発生。

信頼性

障害を起こしたときのシステムのふるまいを時間の経過とともに調べるために、7 台のマシンで構成された ZooKeeper サービスを実行しました。実行したのは、上と同じ飽和状態にするベンチマークですが、書き込みの比率は 30% に保たれるようにしました。この比率は、予想される負荷に相当する順当な値です。

エラー発生時の信頼性

図のグラフから、いくつか重要なことがみてとれます。まず、フォロワーに障害が発生して速やかに復旧した場合、障害が起きたにもかかわらず、ZooKeeper は高いスループットを維持できています。しかし注目すべきは、リーダー選挙アルゴリズムのおかげでシステムが十分高速に回復し、スループットが大幅に低下せずに済んでいることでしょう。記録によれば、ZooKeeper が新しいリーダーを選出するまで 200ms もかかっていません。第三に、ダウンしていたフォロワーが復旧して、リクエストを処理するようになると、ZooKeeper は再度スループットを向上させています。

ZooKeeper プロジェクト

ZooKeeper は、多くの商用アプリケーションで実際に使われています。Yahoo! では、Yahoo! Message Broker のコーディネーション・障害復旧サービスとして使われています。Yahoo! Message Broker というのは、無数のトピックを管理してレプリケーションとデータリカバリができるようにしている非常にスケーラブルな公開・登録システムです。また、Yahoo! クローラの Fetching Service でも使われており、障害復旧を管理しています。Yahoo! の多くの広告システムでも、ZooKeeper を使って信頼性の高いサービスを実装しています。

ZooKeeper のユーザーまたは開発者なら、ぜひコミュニティに参加して、自分の持っている優れた知識をプロジェクトのために役立ててください。詳細については、Apache の Zookeeper プロジェクトを参照してください。