share facebook facebook2 twitter menu hatena pocket slack

2013.02.14 THU

Cassandraってなんじゃ?(EC2でクラスタリング:シングルリージョン編)

三浦 悟

WRITTEN BY三浦 悟

Cassandraってなんじゃ?
Cassandraってなんじゃ?(API編)

上記過去記事にてec2へcassandraを導入し、ローカルからThrift APIでアクセスするところまで行いました。
そして今回ですが、クラスタリングになります。
cassandraはread/writeを分散できるクラスタリングの機能をサポートしており、負荷分散や冗長化がしやすいため、
この機会に勉強したいと思います。

構成は以下の通りです。

VPCのprivateサブネットに3台でクラスタリングして、publicサブネットからAPIでアクセスしてみます。
ここでは、natインスタンスを踏み台にしてcassandraの各ノードにsshで接続して作業します。

cassandraは、新しくノード(EC2インスタンス)が追加された際に、クラスタ上のどれか1台に繋がれば、
自動的にすべてのノードに新ノードの情報が伝わるようになっています。
そのため、新規ノードが立ち上がった時に接続するためのseedといわれるノードが1つ以上必要となります。
ここではそのノードをseed (10.0.1.10)とします。

○cassandraインスタンスの準備

cassandraは複数立ち上げますが、設定ファイルに自分のIP等を記載する必要があるため、
インスタンスごとに設定しなくともよいように設定を自動化したAMIを作成します。

ベースとなるインスタンスへ、以下のようにインストールします。
過去記事から変更された箇所があるため、最初から記載します。

・javaのインストール

javaは過去記事「Cassandraってなんじゃ?(API編)」同様、ブラウザでOracleのサイトからダウンロードを始め
一旦キャンセルし、通信上のURLをコピーして使用します。
またcassandraではjdk7ではなくjdk6が推奨されているため、今回はjdk6をインストールします。


# cd /usr/local/src
# curl -o jdk-6u39-linux-x64-rpm.bin -L http://download.oracle.com/otn-pub/java/jdk/6u39-b04/jdk-6u39-linux-x64-rpm.bin?AuthParam=1360419913_e2b3080676cab457471a1ee88b4dc0c5
# chmod a+x jdk-6u39-linux-x86-rpm.bin
# ./jdk-6u39-linux-x86-rpm.bin

・cassandraのインストール


# cd /usr/local/src
# curl -OL http://ftp.tsukuba.wide.ad.jp/software/apache/cassandra/1.2.1/apache-cassandra-1.2.1-bin.tar.gz
# tar xzvf apache-cassandra-1.2.1-bin.tar.gz
# mv apache-cassandra-1.2.1 /usr/local/
# cd /usr/local
# ln -s apache-cassandra-1.2.1 cassandra

・cassandra.yaml

クラスタリングの設定では、cassandra.yamlの以下の部分を変更します。


seeds: 10.0.1.10

本来はseedも動的に取得すべきですが、ここではseedが10.0.1.10を1つだけの固定にします。


rpc_address: 0.0.0.0

thriftプロトコルを受け付けるIPになります。
ここでは自分のprivate IPを指しますが、0.0.0.0でも動作します。


endpoint_snitch: Ec2Snitch

ノードの置かれているネットワークトポロジの情報をcassandraが判断するための方式です。
通常のデータセンターではデータセンターやラックという単位で区分けされますが、Ec2Snitchを使用すると、
それがリージョンやゾーンとして区分けされます。


listen_address: 自分のprivate IP

ノード間の通信に使用するときの自分のアドレスです。
ここでは正しくIPを指定する必要があるようです。


auto_bootstrap: 自動でクラスタ参加するかどうか

自分がseedのときはfalse、非seedのときはtrueを設定します。

このうち、listen_addressはノードによって変わるため、起動前に動的に書き換えられるようにする必要があります。
こちらの方法は後述します。

・/etc/hosts

例えば、10.0.1.10はhostnameとしてip-10-0-1-10等と振られますが、hostsファイルに記載がないため
cassandraの起動時にエラーが発生します。
そのため、起動時にhostsに自動登録する必要があります。
cloud-initや起動スクリプト内で行う対処法がありますが、ここではcassandraの起動スクリプト内で実行してみます。

・/etc/init.d/cassandra (簡易版)

ここでは最もシンプルな起動スクリプトを使います。
必要であればさらに高機能のものでもよいです。
ただ、上述のcassandra.yamlと/etc/hostsへの自動登録をstart時に行うようにしておきます。


# chkconfig: 345 95 1
# description: cassandra
# processname: cassandra

#!/bin/sh

CASS_BIN=/usr/local/cassandra/bin/cassandra
CASS_PID=/var/run/cassandra.pid

case "$1" in
start)
# hosts1行目(127.0.0.1)への自動登録

sed -i '1s/ip-.*//g' /etc/hosts
sed -i "1s/$/ $(hostname)/g" /etc/hosts
# cassandra.yamlへのlisten_addressの自動登録
sed -i '/^listen_address:/d' /usr/local/cassandra/conf/cassandra.yaml
echo "listen_address: `curl http://169.254.169.254/latest/meta-data/local-ipv4`" >> /usr/local/cassandra/conf/cassandra.yaml

$CASS_BIN -p $CASS_PID
echo "Running Cassandra"
;;
stop)
kill `cat $CASS_PID`
rm -f $CASS_PID
echo "Stopped Cassandra"
;;
*)
echo "Usage: $0 {start|stop}"
exit 1esac
exit 0

cassandra.yamlのauto_bootstrapについては、UserData -> cloud-init等で自動設定もできますが、
今回はseedと非seed用でtrue, falseに固定し、それぞれの状態でseed用と非seed用のAMIを作成しておきます。

○セキュリティグループ

セキュリティグループを設定します。
cassandraは以下のポートを利用します。

  • 7000 : ノード間の接続
  • 7199 : nodetool等、ツールが使用するJMX
  • 9160 : Thrift API

これらと、作業用のsshポート等を開放します。

・http

  • 80 0.0.0.0/0

・ssh

  • 22 10.0.0.0/16

・nat

  • 22 10.0.0.0/16
  • 22 作業者のIP

・cassandra

  • 7000 10.0.0.0/16
  • 7199 10.0.0.0/16
  • 9160 10.0.0.0/16

それぞれのインスタンスには以下を割り当てます。

  • app:http
  • nat:ssh

○起動と確認

ここまで完了したら、cassandraのAMIを起動します。
まず、seed用のAMIから起動します。
subnetはprivate用の10.0.1.0/24を指定し、privateIPに10.0.1.10を指定します。
セキュリティグループは以下を割り当てます。

  • seed, cluster*:ssh, cassandra

次に、cluster用のAMIから同様に2台起動します。
private IPは特に指定しません。

sshで10.0.1.10に入ります。
そこで過去記事同様cassandra-cliでeyspaceやcolumn familyを作成します。


# /usr/local/cassandra/bin/cassanra-cli
[default@unknown] create keyspace Hogebook;
[default@unknown] use Hogebook;
[default@Hogebook] create column family User with comparator = UTF8Type and
default_validation_class=UTF8Type and key_validation_class=UTF8Type and column_metadata =[
{column_name: email, validation_class: UTF8Type},
{column_name: gender, validation_class: UTF8Type, index_type: KEYS}];
[default@Hogebook] set User['memorycraft']['email'] = 'memorycraft@gmail.com';
UnavailableException

エラーが発生しました。
cassandraは他ノードへのデータのレプリカ数と、設定された一貫性保証レベルによって
書き込みの際にエラーになったりするようです。
こちらについては別の機会に触れたいと思います。

ここでは、以下のようにして3台すべてにデータレプリケーションされるように設定しておきます。


[default@Hogebook] update keyspace Hogebook with placement_strategy = 'org.apache.cassandra.locator.NetworkTopologyStrategy' and strategy_options = {ap-northeast:3};
[default@Hogebook] set User['memorycraft']['email'] = 'memorycraft@gmail.com';
[default@Hogebook] set User['memorycraft']['gender'] = 'male';
[default@Hogebook] set User['memorycraftgirl']['gender'] = 'female';
[default@Hogebook] set User['memorycraftgirl']['email'] = 'memorycraft+girl@gmail.com';
[default@Hogebook] get User where gender = 'male';
[default@Hogebook] get User where gender = 'female';

そして、クラスタの状態を見てみます。
クラスタの管理はcassandraのインストールディレクトリに入っているnodetoolを利用します。
ringコマンドは、クラスタの状態をみることのできるコマンドです。


# /usr/local/cassandra/bin/nodetool ring
Datacenter: ap-northeast

==========
Replicas: 3

Address Rack Status State Load Owns Token
4159756940621079776
10.0.1.227 1a Up Normal 70.45 KB 100.00% 8650976588742297378
10.0.1.176 1a Up Normal 80.79 KB 100.00% -7564491331177403445
10.0.1.10 1a Up Normal 90.42 KB 100.00% 4159756940621079776

Ownsが100%になっているので、3台にすべてデータがレプリケーションされている状態です。

また、非seedであるclusterインスタンスをみてみます。


# cat /etc/hosts
127.0.0.1 localhost.localdomain localhost ip-10-0-1-176

# cat /usr/local/cassandra/conf/cassandra.yaml

....
auto_bootstrap: true
listen_address: 10.0.1.176

設定ファイルの自動登録も正しく行っているようです。
これなら何台でも同じAMIから起動できます。

○APIアクセス

また、appインスタンスにCassandraってなんじゃ?(API編)ん記事と同じように、phpcassaを
インストールします。
そして、以下のようなスクリプトで10.0.1.10に対してデータを連続投入してみます。


~/app/test.php
---
require(dirname(__FILE__).'/lib/autoload.php');

use phpcassaColumnFamily;
use phpcassaColumnSlice;
use phpcassaConnectionConnectionPool;

try{
$servers = array('10.0.1.10:9160');
$pool = new ConnectionPool('Hogebook', $servers);
$user = new ColumnFamily($pool, 'User');

//データの挿入
while(1){
$id = md5(uniqid(rand(),1));
echo $id."n";
$user->insert($id,
array(
'email' => uniqid().'@gmail.com',
'gender' => 'female',
)
);
sleep(1);
}
//$pool->close();
}
catch(Exception $e){
echo 'ERROR : ' . print_r($e, true);
}
?>
----

本来コネクションプールは閉じないと行けませんが、ここでは無限ループさせてみます。


$ php test.php
5b3e62154f94a2a669e6b77298f22272
131b1d417165fea802c1a24afbfc2e7e
b3a731d17a301c1fa4f82d89500a1f68
86d568f544470595ab72483657b04352
ab4a15d2346440cc0e089235f68032ae
553ce1a57ad18a108ccba94224c98c4c
c2cf1319c7e099d16f84ecc2802f7b43
61c1d722b1d5d6442706fd5c4e5c0077
7067cfc415def987c5f5ffa12db9879e
a21f80887c9c2d163218045cf8ba321c
d71602cdd32f2cbd0a054aebacaa3764
f4f752c1dd8edde587d6d4a77ae30ec2
12901e29685ab6c3581a4944207c2e55
394f9c09e93dd33b1d378a9b299d1573
7d547f9682592945ddc0ddf1218e1c7e
f6fa6f5c8a80130abee396c438b6a8ac
fec78b484fa4f3975e60c86c082fe9fe
b629d87134a38d487e1750b3fa631d4c
72f9e4965084f39f158853761f0c62c5
e216af506ddae295346d94292e62fb7d
1631160e0977fdd6c4264555006c84ef
aa7f18a38ff739ce4a290d1f109e77f5
709a39189cef0cf82375ea124ef1d97c
0819cf50c7ed9f22b34c5a685f349f4f
5961358ecb3b4bb15ed2e6853357900d
1982683673d01a968a7f90dac8f6fe2c
3a4515cad2ee0ae803230aa7a5da7169
1d331dbd45cbee8038bb1c550039ae31
5d24a90282342d2786ce2dc25b398737
22194a119a48dd492cc42efc6f150b5a
e9040f305bccee5a5ae9fafa47e81820
f5dd73dfb1eda2de50a6ed03ce2e6de0
db86daa2bcb97bc269f846f3a78bb606
0c8a8793d8e71f29021917d7e0441519
bbf590273829d2efb99adba810ef7f13
a41e7ed89d14174f8b896bf887b7cfdc
bad960ca18793b57ca92974dce8bf248
6d4abcef26f8a9138034208df4bbc227
9fc5abb9d63e42a86c297835e9dad6bd
d1dbe39f50628cce099e2dcc3035392b

任意の個所で終了して、seedインスタンス(10.0.1.10)でnodetoolを見てみます。
cfstatsコマンドではデータの統計情報を確認できます。


# /usr/local/cassandra/bin/nodetool cfstats
.....
----------------
Keyspace: Hogebook
Read Count: 18
Read Latency: 0.16872222222222222 ms.
Write Count: 338
Write Latency: 0.5308639053254437 ms.
Pending Tasks: 0
Column Family: User
SSTable count: 0
Space used (live): 0
Space used (total): 0
Number of Keys (estimate): 0
Memtable Columns Count: 667
Memtable Data Size: 346140
Memtable Switch Count: 0
Read Count: 18
Read Latency: 0.169 ms.
Write Count: 338
Write Latency: 0.531 ms.
Pending Tasks: 0
Bloom Filter False Positives: 0
Bloom Filter False Ratio: 0.00000
Bloom Filter Space Used: 0
Compacted row minimum size: 0
Compacted row maximum size: 0
Compacted row mean size: 0

データの投入は成功しているようです。

またclusterインスタンス(10.0.1.176)で上記のPHPで出力されたキーを任意に選んで取得してみます。


# /usr/local/cassandra/bin/cassandra-cli

[default@unknown] use Hogebook;
[default@Hogebook] get User['d1dbe39f50628cce099e2dcc3035392b'];

=> (column=email, value=5119c2f9030a6@gmail.com, timestamp=1360642809012461)
=> (column=gender, value=female, timestamp=1360642809012461)
Returned 2 results.
Elapsed time: 19 msec(s).

取得も成功しました。

以上で基本的なクラスタリングの設定ができたようです。
次回は、もう少し詳しく見てみたいと思います。

こちらの記事はなかの人(memorycraft)監修のもと掲載しています。
元記事は、こちら

三浦 悟

三浦 悟

高円寺在住のなんじゃ系男子 またの名をmemorycraftといいます。 炭水化物大好き 日々の「なんじゃ?」を記事にしてます。