前回までは、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は以前の記事と同じように、ブラウザで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です。ここでは自分のprivateIPを指しますが、0.0.0.0でも動きます。
endpoint_snitch: Ec2Snitch
ノードの置かれているネットワークトポロジの情報をcassandraが判断するための方式です。
通常のデータセンターではデータセンターやラックという単位で区分けされますが、Ec2Snitchを使用すると、
それがリージョンやゾーンとして区分けされます。
listen_address: 自分のprivateIP
ノード間の通信に使用するときの自分のアドレスです。
ここでは正しく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 1 esac 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台起動します。privateIPは特に指定しません。
sshで10.0.1.10に入ります。
そこでcassandra-cliで前回と同じようにkeyspaceや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インスタンスに前回の記事と同じように、phpcassaをインストールします。
そして、以下のようなスクリプトで10.0.1.10に対してデータを連続投入してみます。
~/app/test.php --- <?php require(dirname(__FILE__).'/lib/autoload.php'); use phpcassa\ColumnFamily; use phpcassa\ColumnSlice; use phpcassa\Connection\ConnectionPool; 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).
取得も成功しました。
とりあえず基本的なクラスタリングの設定ができたようです。
次回は、もう少し詳しく見てみたいと思います。