So-net無料ブログ作成
検索選択

インフラエンジニアのためのCassandra情報 ZooKeeperその2 [Cassandra]

ZooKeeperのプログラミングについては、以下のURLに簡単なチュートリアルが
あります。
http://hadoop.apache.org/zookeeper/docs/r3.1.1/zookeeperTutorial.html
今回は、ここに載っているサンプルを使って、カウンターを実現してみます。
ZooKeeperはznodeと呼ばれるツリー構造のデータ構造を持っています。znodeのそれぞれに
対応するオブジェクト(または値)を保存することができます。znodeには、登録、更新時に
一意のバージョン番号が付与されるので、これを使うことで、CAS操作が実現できそうです。
そこで、「/app1/counter」というznodeを作成して、これにカウンター用の数値を代入、参照
するようにします。
カウントアップ時には、CAS操作を行っているので、複数プロセスからの同時書込み時でも値
の一意性が保証されます。
チュートリアルを参考にして、まず「/app1]」の作成部分は、
this.root = root_name; ← 「/app1」
counter = counter_name;
// Create ZK node name
if (zk != null) {
        try {
                Stat s = zk.exists(root, false);
                if (s == null) {
                        zk.create(root, new byte[0], Ids.OPEN_ACL_UNSAFE,
                                        CreateMode.PERSISTENT);
                }

となり、「/counter」の部分は、
b.putInt(0);    ← 初期値として0を入れる。
value = b.array();
zk.create(znode, value, Ids.OPEN_ACL_UNSAFE,
                        CreateMode.PERSISTENT); ← znode = /app1/counter

となります。この段階で既に変数として使えるようになっているので、カウンターの
初期値として「0」を入れておきます。
そして、CAS操作を実現するには、バージョン番号を取得し、カウントアップ後に
バージョン番号と一緒にznodeを更新します。このとき他のプロセスが更新していたら
Exceptionが発生するので、はじめからやり直します。
コードは以下のようになります。
int value = 0;
int retry = 0;

do {
        try {
                Stat stat = zk.exists(znode, false); ←バージョン番号を含むメタデータを取得
                byte[] b = zk.getData(znode,false, stat); ←現在の値を取得
                ByteBuffer buffer = ByteBuffer.wrap(b);
                value = buffer.getInt();

                value++;  ←カウントアップ

                ByteBuffer b1 = ByteBuffer.allocate(6);
                b1.putInt(value);
                b = b1.array();
                zk.setData(znode, b, stat.getVersion()); ←バージョン番号と一緒に、更新した値をセット
                retry = 0;
        } catch ( KeeperException e) { ←バージョン番号が変わっている場合は、KeeperExceptionが発生する
                retry = 1; ←一からやり直し
        }
}while(retry > 0);

出来上がったカウンターのソース(CounterPrimitive.java)は
https://github.com/so-net-developer/Cassandra/blob/master/zookeeper/CounterPrimitive.java
にあります。
使用方法は、以下のとおり。
ZookeeperライブラリをCLASSPATHに指定します。
$ export CLASSPATH=./:/usr/lib/zookeeper-3.3.1/zookeeper-3.3.1.jar:/usr/lib/zookeeper-3.3.1/lib/log4j-1.2.15.jarog4j-1.2.15.jar

コンパイル
$ javac CounterPrimitive.java

使い方は、
カウンタの作成:
java CounterPrimitive [Zookeeperノードアドレス] n
カウンタのインクリメント
java CounterPrimitive [Zookeeperノードアドレス] i [増分]
カウンタの削除
java CounterPrimitive [Zookeeperノードアドレス] d

使用例)
$ java CounterPrimitive localhost n
Input: localhost
New Counter

$ java CounterPrimitive localhost i 10
Input: localhost
Item: 10

$ java CounterPrimitive localhost i 10
Input: localhost
Item: 20          <- カウントアップされている。

$ java CounterPrimitive localhost d
Input: localhost
Deleted

参考までに、rubyでのサンプルも
https://github.com/so-net-developer/Cassandra/blob/master/zookeeper/CounterPrimitive.rb
に載せておきます。rubyに馴染みのひとはこちらのほうが見やすいかも。

インフラエンジニアのためのCassandra情報 ZooKeeperその1 [Cassandra]

CassandraはCASやロックをサポートしていないため、複数のプロセス(またはノード)
から参照して更新されるような一貫性のある共有変数が作れません。
例えばカウンターがその例です。RDBMSであればフィールドをauto incrementにしておけば
複数プロセスからinsertされても常に一意の値が得られます。
Cassandraを使用するアプリケーションでカウンターが必要になったら、そのためだけに
別途MySQL等を使う手もありますが、それではせっかく単一障害点のないCassandraを使って
いても、アプリケーションからするとMySQLのカウンターが単一障害点になってしまいます。
そこで、複数ノードで一貫性と可用性を確保する共有変数領域としてZooKeeperを使ってみます。
ZooKeeperはそれ自体が何かを実行してくれるアプリケーションではなく、分散アプリケーション
を構築するためのサービスとライブラリで構成されています。
このライブラリを使用してアプリケーションを構築することで、ロック、排他制御、同期や
死活監視等いろいろ作ることができます。
サービスはこれら機能を実現するために必要な一貫性と信頼性を持ったデータ領域を提供します。今回は、このデータ領域にカウンターを実装してみます。
ZooKeeperの構造について詳しくは、以下のURLを参照してください。
http://oss.infoscience.co.jp/hadoop/zookeeper/docs/current/zookeeperOver.html
まずは使ってみましょう。
単一ノード(スタンドアロン)でのインストールについては、
http://oss.infoscience.co.jp/hadoop/zookeeper/docs/current/zookeeperStarted.html
を参照すれば簡単に行えますが、複数ノードで動かすことが前提だとちょっと面倒です。
そこで、バイナリ本体、複数ノードで動かす場合の設定サンプル、起動スクリプトを含めた
rpmパッケージを以下に置いておきます。
https://github.com/so-net-developer/Cassandra/tree/master/zookeeper/
ただし、CentOSでしか確認してません。ご利用は自己責任でお願いします。
自分の環境用に作り直したい場合は、SPECファイルも置いておくので、参考までに。
作り方は、Gangliaと同様です。
http://so-net-developer.blog.so-net.ne.jp/hadoop-15-ganglia_1
以降は、このrpmをインストールした環境を前提に説明します。
rpmのインストール。
$ sudo rpm -ivh zookeeper-3.3.1-0.noarch.rpm

インストールすると、/usr/lib/zookeeper-3.3.1にバイナリ本体、
/usr/lib/zookeeper-3.3.1/confに設定サンプル、/etc/init.dに起動スクリプトが
コピーされます。
また、ユーザzookeeperが追加されます。
/var/zookeeperが作られ、データ、pidファイル、myidファイルがここに置かれるように
なります。
複数ノードでクラスタ構成に設定します。
クラスタノードを指定(3台の場合)。
$ cd /usr/lib/zookeeper-3.3.1/conf
$ sudo vi zoo.cfg
server.1=zookeeper1:2888:3888
server.2=zookeeper2:2888:3888
server.3=zookeeper3:2888:3888

zookeeper1,zookeeper2,zookeeper3のところをノードのホストアドレスに変更します。
例)
server.1=xxx.example.com:2888:3888
server.2=yyy.example.com:2888:3888
server.3=zzz.example.com:2888:3888

「server.」の後ろに連番を付けますが、順番には意味はなく各ノードを区別するための番号
となっているようです。
さらにノードを追加する場合は、
server.n=nnn.example.com:2888:3888

と、列を増やしていきます。
ここで定義されたノードのうちのどれかが、ZooKeeperのリーダー役として選出されて、中心的な
役割を果たします。ただし、アプリケーションがこのリーダーを意識することはなく、障害など
ではリーダーが適時切り替わります。
リーダーは、ポート2888を開いて他のノードと通信し、他のノードは3888でお互いに通信を行う。クライアントからの接続は、2181で受け付けます。
zoo.confは全てのノードで同じ内容のものを使用します。起動する前にコピーしておきましょう。次に、さきほど付けたノードの番号をノード自身と結びつけるためのファイル「myid」を
/var/zookeeper以下に作成しておきます。
$ cd /var/zookeeper/
$ echo "1" | sudo tee -a myid

他のノードでも同様に、2,3,・・・の文字を含むmyidファイルを/var/zookeeper以下に作成して
おきます。
では起動してみましょう。
/etc/init.dに起動スクリプトが登録されているので、起動はサービスとして起動します。
$ sudo /sbin/service zookeeper start

全てのノードで同じように起動します。順番は気にしなくても良いです。
起動確認します。2818番にtelnetして「ruok」とコマンドすると「imok」と返してきます。
$ telnet localhost 2181
Escape character is '^]'.
ruok
imokConnection closed by foreign host.
$

次回はカウンターを作ります。

インフラエンジニアのためのCassandra情報 監視その2 [Cassandra]

Nagiosを使ってCassandraのノードを監視してみましょう。
CactiのようにノードのJMXの値を詳細に監視することは出来ませんが、各ノードが
動いているかどうかを確認するだけであれば、ここで紹介する方法が簡単です。
NagiosでCassandraノードの状態を確認するには、やはりJMXを使用します。
Nagiosサーバ側から、Cassandra各ノードのJMXに接続、JMXメトリックスから
「LiveNodes」アトリビュートを取得してノードのアドレスが含まれていれば、ノードは
活性状態であると判断できます。
Cassandraノードである「hogehost」から「LiveNodes」アトリビュートを取得する例:
String user = "cassandra";	// ノードOSに登録されているユーザ
String pass = "";
String hostname = "hogehost";	// Cassandraノードのアドレス
String JMXURL = "service:jmx:rmi:///jndi/rmi://" + hostname + ":8080/jmxrmi";
Map hm = new HashMap();
hm.put(JMXConnector.CREDENTIALS, new String[]{user, pass});
JMXConnector connector = JMXConnectorFactory.connect(new JMXServiceURL(JMXURL), hm);
MBeanServerConnection connection = connector.getMBeanServerConnection();

ObjectName on = new ObjectName("org.apache.cassandra.service:type=StorageService");
System.out.println(connection.getAttribute(on, "LiveNodes"));
connector.close();

Nagiosで試すことが出来るサンプルは以下から取得してください。

https://github.com/so-net-developer/Cassandra/tree/master/nagios/

Nagiosの設定は以下のようになります。
/etc/nagios/commands.cfg
define command {
        command_name    check_cassandra_node
        command_line    $USER1$/check_cassandra_node.sh $HOSTADDRESS$
        }


/etc/nagios/services.cfg
define service {
        use             generic-service
        host_name       hogehost
        service_description     cassandra-JMX
        contact_groups                  admins
        check_command   check_cassandra_node
        }


この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。

×

この広告は1年以上新しい記事の更新がないブログに表示されております。