2013年3月9日土曜日

Cassandraってなんじゃ?(EC2で起動時自動ノード追加とノードの削除)

Cassandraを使っているときに、容量が足らない、負荷が高いなどの理由でノードを増やしたい場合があります。また、負荷が長期的に落ち着き、予算を抑えるためにノードを減らすこともあります。

今回はノードの追加と削除をやってみます。



トークンの算出



ノードを追加するケースはリングの負荷状態によって、大きく分けて
  1. 保存するデータのハッシュやアクセスに偏りが大きく、特定ノードの容量または処理の負荷が高いので負荷集中を分散したい。
  2. リング全体の容量、または処理の負荷が高いので、全体的に数を増やして均一に分散したい。

の2通りかと思います。

1の場合、負荷の高いノードの近くにノードを追加し、ノードの均一化はせずに特定のトークンの範囲にノードを集中させたほうがよさそうです。
2の場合、ノードを追加したら、全ノードが均等にリング上に再配置したほうが全体としての負荷は抑えられそうです。

今回は1の場合を例にとってみます。


まず、トークンを計算するためのツールをつくっておくと便利です。
引数にノードの数を与えるとそれがリング上に均等に配置されるようなトークンのリストを返すツールです。

計算式はPartitionerによってことなり、
#RandomPartitionerの場合
 
def tokens(nodes):
  for x in xrange(nodes):
    print 2 ** 127 / nodes * x
 
#Murmur3Partitionerの場合
 
def tokens(num_nodes):
  for i in range(num_nodes):
    print ((2**64 / num_nodes) * i) - 2**63

のようになります。

今回はMurmur3Partitionerを使用しているので、
以下のようなpythonスクリプトを作ります。


引数にノード数を入れて実行すると、ノードリングに均等に配置された場合のハッシュトークンのリストが出力されます。
$ ./tokens.py 4
-9223372036854775808
-4611686018427387904
0
4611686018427387904
 
$ ./tokens.py 8
-9223372036854775808
-6917529027641081856
-4611686018427387904
-2305843009213693952
0
2305843009213693952
4611686018427387904
6917529027641081856


既存のノードが4つだったとして、トークンが以下のように配置されているとします。
  • A:0
  • B:4611686018427387904
  • C:-9223372036854775808
  • D:-4611686018427387904




この場合Aの両脇にノードを2つ追加しておくとよさそうです。
また、追加ノードのトークンは、
  • B、DもCに比べて負荷が高めであればA-B, A-Cのそれぞれ中間
  • BDの負荷はそれほどでもなければもっとAより

に配置する形になります。
前者であれば、./tokens 8、後者であれば./tokens 12 などでAの位置の両脇のトークンを使用します。
たとえば後者のパターンで追加してみます。

$ ./tokens 12
-9223372036854775808
-7686143364045646507
-6148914691236517206
-4611686018427387905
-3074457345618258604
-1537228672809129303
-2
1537228672809129299
3074457345618258600
4611686018427387901
6148914691236517202
7686143364045646503

-2の部分がAのノード位置です。
その両脇の
  • -1537228672809129303
  • 1537228672809129299

が今回の追加ノードとなります。




以前の記事でちょっと触れましたが、自動でリングに追加されるようなAMIを作ります。



AMIの作成



AMIの作成をします。cassandraをインストールしただけでまだ一度も起動していないインスタンスをベースに作業を行うとスムーズです。

以下の設定ファイルにはIPなどノード固有の情報(赤字の部分)が含まれます。

/usr/local/cassandra/conf/cassandra.yaml
cluster_name: 'クラスタ名'
initial_token: トークン
seed_provider:
- class_name: org.apache.cassandra.locator.SimpleSeedProvider
parameters:
- seeds: "シードIP"

listen_address: 自分のIP
endpoint_snitch: Ec2Snitch
auto_bootstrap: シードならfalse,非シードならtrue

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

この固有の項目を書き換えるようなシェルスクリプトを書きます。

このスクリプトは後でUserDataから呼び出され、第1引数にトークン、第2引数にauto_bootstrapのブーリアン文字列、第3引数にシードIPが渡されます。

/etc/cloud/cassandra.sh


ここまで設定したらno rebootでAMIを作成します。




ノードの自動追加



このAMIをつかって新規ノードを追加します。
クラスタも何もない状態で、一番最初のノードを立ち上げる場合は以下のようにUserDataを指定します。

#!/bin/sh
/etc/cloud/cassandra.sh "0" false 10.0.1.10


そして、プライベートIPアドレスを10.0.1.10に固定して起動します。



また、2個め以降(ここでは例として6個目)の追加ノードは以下のようにUserDataを指定します。
ここで、第1引数のトークンは冒頭で記した計算ツールで算出しておきます。

#!/bin/sh
/etc/cloud/cassandra.sh "1537228672809129299" true 10.0.1.10,10.0.1.146,10.0.1.176,10.0.1.232,10.0.1.134


特にプライベートIPを指定する必要はありません。



これにより、立ち上がったインスタンスでは、hostsファイルとcassandra.yamlが自動的に設定され、cassandraが起動し、リングに自動的に追加されます。

起動前は、

# /usr/local/cassandra/bin/nodetool ring

Datacenter: ap-northeast
==========
Replicas: 3

Address         Rack        Status State   Load            Owns                Token                                     
                                                                               -4611686018427387905                       
10.0.1.146      1a          Up     Normal  75.43 KB        66.67%              -1537228672809129303                       
10.0.1.10       1a          Up     Normal  61.65 KB        50.00%              0                                         
10.0.1.176      1a          Up     Normal  58.33 KB        58.33%              -9223372036854775808                       
10.0.1.232      1a          Up     Normal  58.45 KB        50.00%              4611686018427387901                       
10.0.1.134      1a          Up     Normal  58.48 KB        75.00%              -4611686018427387905

だったのが、起動後


# /usr/local/cassandra/bin/nodetool ring

Datacenter: ap-northeast
==========
Replicas: 3

Address         Rack        Status State   Load            Owns                Token                                       
                                                                               -4611686018427387905                        
10.0.1.213      1a          Up     Normal  49.55 KB        33.33%              1537228672809129299                         
10.0.1.146      1a          Up     Normal  63.7 KB         66.67%              -1537228672809129303                        
10.0.1.10       1a          Up     Normal  66.59 KB        50.00%              0                                           
10.0.1.176      1a          Up     Normal  63.28 KB        50.00%              -9223372036854775808                        
10.0.1.232      1a          Up     Normal  63.4 KB         33.33%              4611686018427387901                         
10.0.1.134      1a          Up     Normal  63.43 KB        66.67%              -4611686018427387905 

↑のようにノード追加されていることが分かります。



ノードの取り外し



逆に問題があるノードを取り外すときは、そのノードに入って、

# /usr/local/cassandra/bin/nodetool decommission

とすれば外れます。
再び接続させるには、cassandraを追加します。

#/etc/init.d/cassandra stop
#/etc/init.d/cassandra start



接続できない場合


たまに、リングに接続できなかったりした場合は、まず7000(ノード間), 9160(Thrift), 7199(JMX)などのポートを準備できているかセキュリティグループやnsetstatで確認します。netstatでlistenできていない場合は、cassandraを再起動させたりログを調べたりして原因を探していく流れになります。




# netstat -tupln | grep -i listen | grep java
tcp        0      0 0.0.0.0:9160                0.0.0.0:*                   LISTEN      3967/java         
tcp        0      0 0.0.0.0:38250               0.0.0.0:*                   LISTEN      3967/java         
tcp        0      0 0.0.0.0:53170               0.0.0.0:*                   LISTEN      3967/java         
tcp        0      0 10.0.1.213:7000             0.0.0.0:*                   LISTEN      3967/java         
tcp        0      0 0.0.0.0:7199                0.0.0.0:*                   LISTEN      3967/java



これで、ノードを簡単に増やせるようになりました。
以上です。