フロムスクラッチ開発者ブログ

from scratch Engineers' Blog

Apache ZooKeeperの内部解析してみる vol.2 ~ZooKeeper操作編~

こんにちは、fukuです。

前回に引き続きApache ZooKeeperについて書いていきます。前回ではZooKeeperの概要の説明を行いました。ZooKeeperではznodeから構成されるデータノードを管理しており、クラアントはAPIを通じてデータノードの操作を行うことができます。 今回は実際にZooKeeperを操作することでZooKeeperのAPIなどについての理解を深めていきましょう。

インストール

ZooKeeperのダウンロードページApache ZooKeeper - Releasesからダウンロードを行います。今回はZooKeeperのバージョン3.5.3-beta(何事も先取って検証)を対象とし、macOS Sierra(バージョン10.12.6)のOS環境上で検証を行いました。 ダウンロードしたzookeeper-3.5.3-beta.tar.gzファイルを任意のディレクトリに置き、以下のコマンド群を実行することで、ZooKeeperの起動の準備をします。

$ tar zxvf zookeeper-3.5.3-beta.tar.gz
$ cd zookeeper-3.5.3-beta.tar.gz
$ cp conf/zoo_sample.cfg conf/zoo.cfg

上記のコマンドを実行でZooKeeperが起動できるようになったので、以下のコマンドからスタンドアローンモードでZooKeeperをバックグラウンド(デフォルトポート2181)で起動することができます。

$ bin/zkServer.sh start

またサブコマンドを変更することでフォアグラウンドで実行することも可能です。

$ bin/zkServer.sh start-foreground

サーバを停止する時は、以下のコマンドを実行してください。

$ bin/zkServer.sh stop

ZooKeeperの利用

サーバを立ち上げた状態で、ZooKeeperのディレクトリにおいて以下のコマンドを実行することで、クライアントを起動することができます。

$ ./bin/zkCli.sh

無事に接続が確立すると、クライアントコマンドの待ち受け状態になります。ここでhelpを入力して、コマンド一覧を表示してみましょう(実際にはhelpというコマンドは存在せず、存在しないコマンドを入力することで以下のようなUSAGE情報が表示されます)。

[zk: localhost:2181(CONNECTED) 0] help
ZooKeeper -server host:port cmd args
    addauth scheme auth
    close 
    config [-c] [-w] [-s]
    connect host:port
    create [-s] [-e] [-c] [-t ttl] path [data] [acl]
    delete [-v version] path
    deleteall path
    delquota [-n|-b] path
    get [-s] [-w] path
    getAcl [-s] path
    history 
    listquota path
    ls [-s] [-w] [-R] path
    ls2 path [watch]
    printwatches on|off
    quit 
    reconfig [-s] [-v version] [[-file path] | [-members serverID=host:port1:port2;port3[,...]*]] | [-add serverId=host:port1:port2;port3[,...]]* [-remove serverId[,...]*]
    redo cmdno
    removewatches path [-c|-d|-a] [-l]
    rmr path
    set [-s] [-v version] path data
    setAcl [-s] [-v version] path acl
    setquota -n|-b val path
    stat [-w] path
    sync path
Command not found: Command not found help

今回はいくつかのコマンドを実際に実行することで、ZeeKeeperで行えることをみていきましょう。

データツリーの操作

まずlsコマンドを使ってZooKeeperのデータノードを覗いてみましょう。

[zk: localhost:2181(CONNECTED) 1] ls /
[zookeeper]

上記から、データノードのルートノードには[zookeeper]のznodeがあることがわかります。[zookeeper]ノードはZooKeeperのシステム情報を管理するためのノードとなります。

それでは次にcreateコマンドからznodeを作成してみましょう。

[zk: localhost:2181(CONNECTED) 2] create /clients ""
Created /clients
[zk: localhost:2181(CONNECTED) 3] ls /              
[zookeeper, clients]

このようにcreateコマンドの第1引数にznodeの名前、第2引数でznodeに格納するデータを指定することで、znodeが作成されます。 ちなみに今回のcreateコマンドでできたznodeはPERSISTENTノードとなります。

getコマンドではznodeの情報を参照することができます。

[zk: localhost:2181(CONNECTED) 4] get /clients

ここでは、出力としてcreate`コマンドで指定したデータ(今回は空文字列)が出力されます。

続いてsetコマンドで、znodeのデータを変更してみましょう。

[zk: localhost:2181(CONNECTED) 5] set /clients "hogehoge"
[zk: localhost:2181(CONNECTED) 6] get /clients           
hogehoge

上位のようにznodeに格納されているデータが空文字列から"hogehoge"に変わっていることがわかります。znodeのデータセットで注意しなければならないことは、データが置き換わるということです。そのためデータを追記したりすることはできません。

クライアントのセッションを終了するにはquitコマンドを実行します。

[zk: localhost:2181(CONNECTED) 7] quit
Quitting...
2017-12-30 20:32:17,295 [myid:] - INFO  [main:ZooKeeper@687] - Session: 0x160a6c05dcf0004 closed
2017-12-30 20:32:17,298 [myid:] - INFO  [main-EventThread:ClientCnxn$EventThread@520] - EventThread shut down for session: 0x160a6c05dcf0004

znodeのモード

ここでは前回記事で紹介したznodeのいろいろなモードを試していきたいと思います。createコマンドにはモードを指定するオプションが存在しており、オプションを指定する事によって、いろいろなモードのznodeを作成することができます。

PERSISTENT

PERSISTENTモードのznodeは、先ほどのようにcreateのオプションなしで作成することができます。

[zk: localhost:2181(CONNECTED) 0] create /clients/c1 "PERSISTENT"
Created /clients/c1

PERSISTENTモードではznodeが永続化されるので、セッションを終了してから、再度セッションを開始してもznodeがそのまま存在することが確認できます。

PERSISTENTモードのznodeを削除するにはdeleteコマンドによって、明示的に削除を行う必要があります。

[zk: localhost:2181(CONNECTED) 1] delete /clients/c1
[zk: localhost:2181(CONNECTED) 2] ls /clients
[]

EPHEMERAL

EPHEMERALモードではznodeの作成を行なったセッションが切断されると、znodeは自動的に削除されます。createコマンドで-eオプションの指定でEPHEMERALモードのznodeを作成することができます。ここではクライアントを2つ立ち上げてEPHEMERALモードのznodeをみていきます。

#  クライアント1
[zk: localhost:2181(CONNECTED) 0] create -e /clients/ephemeral-client "EPHEMERAL"
Created /clients/ephemeral-client

#  クライアント2
[zk: localhost:2181(CONNECTED) 0] ls /clients
[ephemeral-client]

#  クライアント1
[zk: localhost:2181(CONNECTED) 1] quit
Quitting...
2017-12-30 20:47:29,178 [myid:] - INFO  [main:ZooKeeper@687] - Session: 0x160a6c05dcf0006 closed
2017-12-30 20:47:29,180 [myid:] - INFO  [main-EventThread:ClientCnxn$EventThread@520] - EventThread shut down for session: 0x160a6c05dcf0006

# クライアント2
[zk: localhost:2181(CONNECTED) 1] ls /clients
[]

上記のようにクライアント1でEPHEMERALモードのznodeを作成すると、クライアント2でも作成したznodeの確認が行えます。次にクライアントのセッションを終了して、クライアント2でznodeの確認を行うとEPHEMERALモードのznodeのノードが削除されていることがわかります。

CONTAINER

CONTAINERモードのznodeは自身の子ノードが削除されると自動的にznodeが削除されます。createコマンドの-cオプションによって、CONTAINERモードを指定することができます。

[zk: localhost:2181(CONNECTED) 0] create -c /container ""
Created /container
[zk: localhost:2181(CONNECTED) 1] create  /container/child ""
Created /container/child
[zk: localhost:2181(CONNECTED) 2] delete /container/child 
[zk: localhost:2181(CONNECTED) 3] ls /
[clients, container, zookeeper]

<しばらくした後>

[zk: localhost:2181(CONNECTED) 4] ls /
[clients, zookeeper]

上記のようにCONTAINERモードのznode(/container)を作成して、子ノードを作成・削除します。その後にCONTAINERモードのznodeはすぐには削除されませんが、一定時間ののちにZooKeeperによって自動的に削除されます。

PERSISTENT_TTL

PERSISTENT_TTLモードがznodeの生存時間を指定することができ、指定時間後にZooKeeperによって自動的に削除されます。createコマンドの-tオプションによってPERSISTENT_TTLモードを指定することができます。

[zk: localhost:2181(CONNECTED) 0] create -t 600 /ttl_node ""
[zk: localhost:2181(CONNECTED) 1] ls /
[clients, ttl_node, zookeeper]

<しばらくした後(600ms以上)>

[zk: localhost:2181(CONNECTED) 2] ls /
[clients, zookeeper]

生存時間は-tオプションの後ろにミリ秒単位で指定します(上記は600ms)。作成直後はznodeが存在していることが確認できますが、600ms後しばらくしてから、確認するとznodeが削除されていることが確認できます。

ここで注意しなければいけないのは、ちょうど600ms後に削除されるのではなく、ZooKeeperの定期的にTTLモード(CONTAINERモードも同様)の削除を行うタイミングで削除が行われるという点です。そのため600ms後に初めてのZooKeeperの削除サイクルのタイミングが来た時に削除されます。デフォルトではZooKeeperの削除サイクルの時間間隔は2000msとなります。

長くなってしまったので、今回はこの辺で....

おわりに

今回は実際にZooKeeperをスタンドアローンモードで起動して、CLIからデータツリーに対するさまざまな操作を行いました。 次回はソースコードからデータツリーがどのように管理されているのかをみていこうと思います。

ではでは...

運用中のRedshiftを旧世代から最新世代に移行する方法

はじめまして。

フロムスクラッチでエンジニアをしている山崎と申します。
普段はAWSを用いたインフラの運用・構築、およびコスト管理を担当しています。

今後私からは、AWSを用いたインフラに関わる「技術」「コスト管理」について、他社の方もご興味ありそうなトピックを共有させて頂ければ幸いです。

 

さて1回目のテーマは、「運用中のRedshiftを旧世代から最新世代に移行する方法」です。

2017年10月にアナウンスがあり、Redshiftにて第二世代のdc2を冠した性能を選ぶことができるようになりました。

 

Amazon Redshiftに新世代のDC2ノードが追加 – 価格はそのままで最大2倍の性能向上

https://aws.amazon.com/jp/blogs/news/amazon-redshift-dc2/

 

価格はそのままで性能が向上するなら、そりゃdc1からdc2に移行したくなるのは当然ですね。問題は”どうやってサービス影響が最も小さくなるように移行するか”ということになります。

 

移行方法のアイディアは2つあります。

 

(1)dc1のRedshiftクラスタをリサイズしてdc2にする。

(2)dc1のRedshiftクラスタの更新を止め、スナップショットを取得して復元。その後名前を入れ替える。

 

具体的に中身を見ていきます。

(1)について

こちらはシンプルですね。以下の通り、クラスタの性能をdc2にそのまま変えるだけです。

 

Redshiftダッシュボード>クラスター>対象クラスタを選択>クラスター>サイズ変更>ノードの種類をdc2にする>サイズ変更 

f:id:daisuke-yamasaki:20180301170946p:plain

f:id:daisuke-yamasaki:20180301172324p:plain

 

ただし、リサイズ中はクラスタが”Read-Only”になるので注意しましょう。で、問題はリサイズにどのくらいの時間がかかるか。2017年11月に検証した時の情報ですが、非常に時間がかかりました。具体的には、約430GBのデータがあり3ノードで運用しているクラスタのdc1→dc2のリサイズに392分の時間がかかりました。とてもじゃないですが、この間”Read-Only”にしておけません。



(2)について

こちらは若干作業に手間がかかりますが、作業時間が読めるのでお勧めします。作業時間は約1時間、うちクラスタへの接続断時間は約2分程度でした。
ただし、クラスタのエンドポイントは移行前後で同じですが、クラスタのIPアドレスは変わると推測されるので、クラスタのIPアドレスを指定して接続するシステムの場合は作業前に充分な検討をしてください。

 

1.移行対象クラスタ(名前=クラスター識別子をAとする)への更新を止める。

 

2.クラスタ(A)のスナップショットを取得する。スナップショット識別子は分かり易い名前にしましょう。

f:id:daisuke-yamasaki:20180301172422p:plain

f:id:daisuke-yamasaki:20180301172425p:plain

 

3.2で取得したスナップショットを、「ノードタイプ:dc2、クラスター識別子:スナップショット取得元と別名(Bとする)、その他の設定:同じ」で復元する。

f:id:daisuke-yamasaki:20180301172630p:plain

f:id:daisuke-yamasaki:20180301172723p:plain

 

4.Aのクラスター識別子をCにする。

f:id:daisuke-yamasaki:20180301172901p:plain

f:id:daisuke-yamasaki:20180301172904p:plain

 

5.Bのクラスター識別子をAにする。

 

6.Aへのアプリからの接続がOKであることを確認する。

 

7.Cを削除する。

f:id:daisuke-yamasaki:20180301173037p:plain

 

8.Aへの更新を開始する。




以上、「運用中のRedshiftを旧世代から最新世代に移行する方法」について書いてきました。本内容はあくまで弊社で試した内容になりますので、実施する際は必ず事前検証をお願い致します。ご覧頂きありがとうございました。

Apache ZooKeeperを内部解析してみる vol.1 〜概要とデータノード編〜

こんにちは、fukuです。

フロムスクラッチでは主にアーキテクチャー周りの仕事に従事しています。

今回から数回に分けてApache ZooKeeper(以下ZooKeeper)について記事を書いて行こうと思います。ゆくゆくはソースコードレベルで解説を行なっていけたら良いかなと思っています(不安...)。1回目の今回はZooKeeperの紹介を行なっていきます。

昨今ではApache Hadoopをはじめとし、さまざまな分散処理フレームワークのOSSがありますが、そのいくつかの分散処理のシステム管理はZooKeeperを用いて行われています。

ZooKeeperとは

f:id:fuku_dw:20171227180655p:plain

分散処理のアプリケーションではアプリケーションのロジックだけでなく、分散処理されるアプリケーションプロセス同士を協調させながら処理を行うことが重要になってきます。

ZooKeeperではそのような分散処理における協調動作を行うための分散コーディネーションサービスを提供します。

ZooKeeperはApacheのトッププロジェクトの1つであり、元々はGoogleのChubbyを元にYahooによって開発が行われました。

ZooKeeperではアプリケーションに対してAPIを提供して、APIを通して分散システムにおける構成管理や、リーダー選出、分散ロック、メッセージキューなどの用途に利用されます。

分散システムの制御

ここではZooKeeperを離れて、少し 分散システムの制御について記述します。下記の図のような一般的なHadoopなどのマスタ・ワーカの分散システムにては、以下のような事に関しての問題を考える必要があります。

f:id:fuku_dw:20171227190148p:plain

マスタ障害

マスタに障害がおこると、ワーカ対するタスクの割り当てなどが行えなくなります。マスタに障害が発生した場合には、バックアップのマスタが処理を元のマスタの役割を引き継ぐ必要があります。さらにクラスタ状態の障害前の状態に戻すためには、元のマスタの状態を知る必要があります。当然、障害がおきたマスタ上でデータを管理していると、元のマスタの状態を知ることができなくなってしまいます。

ワーカ障害

ワーカの障害では、そのワーカに割り当てられているタスクに実行が完了しなくなります。そのためマスタはワーカの障害を検知して、障害がおこったワーカに割り当てていたタスクを、別のワーカに再度割り当てる必要があります。

また部分的にタスクが実行されていたりしていると、ワーカの障害がおこった場合に、部分的に実行された内容に関するリカバリなどを考慮する必要がある場合も考えられます。

通信障害

通信障害ではネットワークが分断されてワーカが マスタから切り離されることが考えられます。この場合に問題になるのはロックなどの同期処理への影響を考える必要があることです。分断されたワーカが何かしらの資源に対する排他ロックを行なっていた場合には対象のリソースが永久に解放されなくなってしまうかもしれません。

 これらの分散システムにおける問題を扱うために ZooKeeperではさまざまな機能を提供しています。次はZooKeeperの基本的な機能について見ていきましょう。

ZooKeeperの基本

ZooKeeperではデータツリーと呼ばれるファイルシステムのような階層的なツリー構造を構成しています。ツリー構造の各ノードはznodeと呼ばれ、各znodeノードの名前は、スラッシュ(/)で区切られるパス要素によって識別されます。

以下にデータツリーの例を示します。図からわかるように一般的な木構造のデータ構造をとっており、ルートのznodeは"/app1"と"/app2"の2つの子ノードを持ってることがわかります。

f:id:fuku_dw:20171227165956p:plain

さらに各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度しか通知が行われないことです。そのため何度も通知を受けたい場合などは、通知を受け取ったのちに新しい監視情報を登録する必要があります。

f:id:fuku_dw:20171227185311p:plain

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

f:id:fuku_dw:20171227185314p:plain

監視の設定では、監視を設定した時のznodeの状態も取得できるため、上記のようなパターンでは2度目の監視の設定の時に取得できるznodeの状態を見ることによって、対象のznodeの状態が変更されているかをチェックすることができます。

おわりに

今回はZooKeeperの概要から、データノードに関しての説明を行いました。ZooKeeperではあくまで分散システムの強調動作を実現するための基本機能の実装しか行われていないので、制御のためのロジックを実装するのはアプリケーション側の役割となります。

次回は実際にZooKeeperを動かしながらZooKeeperに対して、できることを紹介していく予定です。

ではでは...

参考文献: ZooKeeperによる分散システム管理(2014/10/8 オライリージャパン)