share facebook facebook2 twitter menu hatena pocket slack

2013.03.11 MON

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

三浦 悟

WRITTEN BY三浦 悟

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

そこで今回は、ノードの追加と削除を行います。

○トークンの算出

ノードを追加するケースをリングの負荷状態によって大きく分けてみると、下記2通りかと思います。

  1. 保存するデータのハッシュやアクセスに偏りが大きく、特定ノードの容量または処理の負荷が高いため
    負荷集中を分散したい。
  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スクリプトを作ります。

 
#!/usr/bin/python
import sys
argvs = sys.argv

tokenc = int(argvs[1])

def tokens(num_nodes):
for i in range(num_nodes):
print ((2**64 / num_nodes) * i) - 2**63

tokens(tokenc)

引数にノード数を入れて実行すると、ノードリングに均等に配置された場合のハッシュトークンのリストが
出力されます。

 
$ ./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

 
sed -i "1s/ip-.*//g" /etc/hosts
sed -i "1s/$/ $(hostname)/g" /etc/hosts
sed -i "s/^listen_address:.*$/listen_address: $(curl -s http://169.254.169.254/latest/meta-data/local-ipv4)/" /usr/local/cassandra/conf/cassandra.yaml
sed -i "s/^initial_token:.*$/initial_token: $1/" /usr/local/cassandra/conf/cassandra.yaml
sed -i "s/^auto_bootstrap:.*$/auto_bootstrap: $2/" /usr/local/cassandra/conf/cassandra.yaml
sed -i "s/^ - seeds:.*/ - seeds: "$3"/" /usr/local/cassandra/conf/cassandra.yaml

/etc/init.d/cassandra start

ここまで設定したら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)などのポートを
準備できているか、セキュリティグループやnetstatで確認します。
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

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

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

三浦 悟

三浦 悟

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