まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:cluster

IBM CloudantApache CouchDB をベースとしたマネージド NoSQL DB サービスです。IBM Cloud のライトアカウントを利用することで無料枠内で利用することも可能です。

そんな便利な IBM Cloudant ですが、IBM Cloud では(特に無料枠で使った場合)では、どのようなクラスタリング構成で運用されているのか気になりました。もともと NoSQL DB はスケーリングに優れていて、大規模運用向きと言われています。ではこの(特に無料のライトプランで提供されている)IBM Cloudant はどのような運用構成で提供されているのでしょう? 理論上は1サーバーノードで(クラスタリングなしで)提供することも可能だし、無料プランということを加味して、クラスタリング無しだったとしてもまあそうだよね・・とも考えられます。一方で無料プランと有償プランで(わざわざ)差をつけて運用しているのか?という疑問もあります。このあたりをそっと調べてみました。


まず、調べる方法は CouchDB REST API の GET /db を使うことにしました。この REST API を実行すると指定したデータベースの情報を得ることができ、その中には(クラスタリング構成になっていれば)クラスターに関する情報が含まれていることになっています。この方法で自分がローカルで構成した単一構成の CouchDB と、IBM Cloud のライトプランで契約した IBM Cloudant の2つのデータベースに対して実行し、その結果を比較してみることにします。

まず前者の単一構成 CouchDB のデータベースに対してこのコマンドを curl で実行しました(実行結果は青字):
$ curl 'http://localhost:5984/ccdb'

{"db_name":"ccdb","update_seq":"6-

g1AAAAEzeJzLYWBg4MhgTmHgzcvPy09JdcjLz8gvLskBCjMlMiTJ____PyuRAYeCJAUgmWSPX40DSE08WA0TLjUJIDX1eM3JYwGSDA1ACqhsPiF1C

yDq9uO2E6LuAETdfULmPYCoA_khCwCKxmL8","sizes":{"file":58598,"external":615,"active":1730},"purge_seq":0,"other":

{"data_size":615},"doc_del_count":2,"doc_count":2,"disk_size":58598,"disk_format_version":6,"data_size":1730,"com

pact_running":false,"instance_start_time":"0"}

指定したデータベース(上例では ccdb)の現在の状態が表示されています。各項目の意味は上述の GET /db API のリンク先で説明されているのでそちらを参照いただきたいのですが、この実行結果にはクラスタリング情報が含まれていません。実際クラスタリング構成ではなく単一構成で動いているので、この実行結果もその運用状態を正しく表しています。

次に同じコマンドを IBM Cloudant のライトプランで作成したデータベースに対して実行しました。その結果がこちらです:
{"status":true,"info":{"update_seq":"20-

g1AAAAQneJzLYWBgEMhgTmHQTElKzi9KdUhJMtFLytVNTtYtLdYtzi8tydA1NNBLzskvTUnMK9HLSy3JAWphSmRI4v___39WIgNIsxZcs6EhMbqTB

IBkkjzYAGZU24nTrwDSr49NvzlR-g1A-u0RHiDR90kOIP3-

CPtJDoAAkAHx2BxAnP4EkP58bPqJC4ACkP56sgMgjwVIMjQAKaAR_VmJTOQEAsSQCRBD5pMXEBAzFkDMWE9eYEDM2AAxYz_UM2QFyAGIGeezEhnJD

5ALEEPuUxIgDyBmvCcve0DM-AAxA5TEswCIaVtl","db_name":"statedb","sizes":

{"file":11774513,"external":10511286,"active":10543509},"purge_seq":0,"other":

{"data_size":10511286},"doc_del_count":7,"doc_count":2,"disk_size":11774513,"disk_format_version":6,"data_size":1

0543509,"compact_running":false,"cluster":{"q":16,"n":3,"w":2,"r":2},"instance_start_time":"0"}}

↑特に赤字部分に注目してほしいのですが、先程の実行結果には存在しなかったクラスタリングに関する情報が含まれています。そしてこの結果を見ると、このデータベースは
 ・シャード数: 16(!)
 ・1つのドキュメントの分散数: 3
 ・書き込みコマンドを実行した場合、2つ以上に書き込めたら書き込み成功とする
 ・読み取りコマンドを実行した場合、2つ以上から結果が返ってきたら読み取り成功とする

という条件でクラスタリングが構成されていることがわかります。無料のライトプランでも結構な好条件でクラスタリングされていたんですね、へぇ~。


 

2台の CentOS サーバーで MySQL クラスタ環境を構築する機会があったので、その作業メモです。


前提条件として、2台の CentOS 6.5(x64_86) をそれぞれ A, B とします。それぞれの IP アドレスを AAA.AAA.AAA.AAA / BBB.BBB.BBB.BBB とします。これらにそれぞれ MySQL のデータノードと SQL ノードを構築します。A, B には MySQL 関連モジュールの導入は一切行っていないものとします。
なおサーバー A は管理ノードを兼ねることにします。以下の手順は2台構成ですが、3台目以降を追加する場合は B と同じ手順を行うことになります。

【手順1 MySQL Cluster をインストール(A, B)】
A/B 両方のサーバーに MySQL Cluster をインストールします。CentOS 6.x では MySQL Cluster は標準の yum リポジトリには含まれていないようなので、公式サイトからソースをダウンロードしてインストールします:
Download MySQL Cluster

上記サイトから最新版のインストールセットをダウンロードします。今回の前提環境(CentOS 6.5 64bit)であれば Platform に "Linux Generic" を選択して 64bit 版のモジュールファイルをダウンロードします(ダウンロード時に Oracle のアカウントが必要になります):
2014052801

ちなみに 2014/05/28 現在、この方法で取得すると MySQL Cluster 7.3.5(ファイル名 mysql-cluster-gpl-7.3.5-linux-glibc2.5-x86_64.tar.gz)がダウンロードできます。以下、このモジュールを使っている前提で記載を続けます。バージョン差異などでファイル名が異なる場合は適宜読み替えてください。

ダウンロードしたモジュールファイルを A, B 両方にコピーします(/tmp にコピーしたと仮定します)。そして以下の導入作業を A, B 双方で行います。

サーバーに root でログインし、まず mysql 用のユーザーとグループを作成します:
# groupadd mysql
# useradd -g mysql mysql

コピーした MySQL Cluster インストールモジュールを /usr/local/ 以下に展開し、mysql という名称でアクセスできるようにシンボリックリンクを張ります:
# cd /usr/local
# tar xzvf /tmp/mysql-cluster-gpl-7.3.5-linux-glibc2.5-x86_64.tar.gz
# ln -s mysql-cluster-gpl-7.3.5-linux-glibc2.5-x86_64 mysql

ファイルオーナーとファイルグループを変更します:
# chown -R root.mysql mysql-cluster-gpl-7.3.5-linux-glibc2.5-x86_64
# chown -R mysql.mysql mysql/data/

/usr/local/mysql/bin に PATH を通します:
(例)こういう結果になるよう ~/.bash_profile を編集する
# cat ~/.bash_profile
  :
  :
PATH=$PATH:$HOME/bin:/usr/local/mysql/bin
  :
  :

MySQL の管理用データベースを初期化して、デーモン起動用スクリプトをコピーして用意します:
# cd /usr/local/mysql
# ./scripts/mysql_install_db --user=mysql
# cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysql
# chown root.root /etc/init.d/mysql
# chmod u+x /etc/init.d/mysql

これでモジュールの導入は完了です、意外と簡単。この処理は全てのサーバー(今回の例であれば A と B)に対して行います。


管理ノードではないサーバー(B) のみ、/var/lib/mysql-cluster/ というフォルダを作成しておきます。中身は不要ですが MySQL が利用するのでファイルオーナーとグループオーナーを変更しておきます:
# mkdir /var/lib/mysql-cluster
# chown -R mysql.mysql /var/lib/mysql-cluster

管理ノードサーバー(A) に関しては、この作業は次の手順の中に含まれているので不要です。


【手順2 MySQL Cluster 管理ノードの設定(Aのみ)】
次に管理ノードの設定を行います。この作業はサーバー A に対してのみ行います。

今回は設定ファイルを /var/lib/mysql-cluster/ 以下に作成します。場所は任意で構いませんが、変更する場合は適宜読み替えてください。
# mkdir /var/lib/mysql-cluster
# chown -R mysql.mysql /var/lib/mysql-cluster
# vi /var/lib/mysql-cluster/config.ini
  :
  :
[NDB_MGMD]
hostname=AAA.AAA.AAA.AAA  # 管理サーバー(サーバーA)のIPアドレス

[NDBD default]
NoOfReplicas=2  # クラスタ環境の冗長化数(台数)
datadir=/var/lib/mysql-cluster

[NDBD]
hostname=AAA.AAA.AAA.AAA  # 1台目のデータノードの IP アドレス
datadir=/var/lib/mysql-cluster

[NDBD]
hostname=BBB.BBB.BBB.BBB  # 2台目のデータノードの IP アドレス
datadir=/var/lib/mysql-cluster

[MYSQLD]
hostname=AAA.AAA.AAA.AAA  # 1台目のSQLノードの IP アドレス

[MYSQLD]
hostname=BBB.BBB.BBB.BBB  # 2台目のSQLノードの IP アドレス


【手順3 MySQL Cluster SQL ノードの設定(A, B)】
次にSQLノードの設定を行います。この作業はサーバー A, B 両方に対して行います。

具体的には /etc/my.cnf をクラスタ環境向けに編集します:
# vi /etc/my.cnf
  :
  :
[mysqld]
ndbcluster
character-set-server=utf8

[mysql]
default-character-set=utf8

[mysql-cluster]
ndb_connectstring=AAA.AAA.AAA.AAA

[ndb_mgmd]
config_file=/var/lib/mysql-cluster/config.ini


【手順4 MySQL Cluster の起動(A, B)】
ここまでの作業で MySQL Cluster が起動できるようになっています。ただ MySQL Cluster を起動する際の注意点として、起動する時の順序は必ず以下の順番で行う必要があります。また停止は逆の順序で行う必要があります:
(1) 管理ノード起動(A)
(2) データノード起動(A, B)
(3) SQL ノード起動(A, B)

具体的には以下の様なオペレーションで MySQL Cluster を起動します:
管理ノード起動(A)
# cd /var/lib/mysql-cluster # ndb_mgmd
(起動メッセージが表示される。管理ノードがSQLノードを兼ねている場合は警告が表示されるが無視)

データノード起動(A, B)
# ndbd

SQLノード起動(A, B) # /etc/init.d/mysql start

この段階でサーバー A, B 双方で MySQL サーバーが起動して、それらはクラスタリング構成になっています。そのことを管理ノード A から ndb_mgm を実行後のプロンプトから show コマンドで確認してみます:
# ndb_mgm
-- NDB Cluster -- Management Client --
ndb_mgm> show
Connected to Management Server at: AAA.AAA.AAA.AAA:1186 管理ノード
Cluster Configuration
 ---------------------
[ndbd(NDB)]     2 node(s)
id=2    @AAA.AAA.AAA.AAA  (mysql-5.6.17 ndb-7.3.5, Nodegroup: 0, *) データノード(マスター)
id=3    @BBB.BBB.BBB.BBB  (mysql-5.6.17 ndb-7.3.5, Nodegroup: 0) データノード

[ndb_mgmd(MGM)] 1 node(s)
id=1    @AAA.AAA.AAA.AAA  (mysql-5.6.17 ndb-7.3.5) 管理ノード

[mysqld(API)]   2 node(s)
id=4    @AAA.AAA.AAA.AAA  (mysql-5.6.17 ndb-7.3.5) SQLノード
id=5    @BBB.BBB.BBB.BBB  (mysql-5.6.17 ndb-7.3.5) SQLノード

ndb_mgm>

こんな感じになっていれば正しくクラスタリングできています。


【手順5 MySQL ユーザーやデータベースの設定(A, B)】
MySQL Cluster では対象のデータベースの同期が取られますが、同期が取られないデータもあります。それらは個別に生成/設定する必要があります:
root ユーザーパスワード設定(A, B)
# mysql -u root
mysql> set password for root@localhost=PASSWORD('XXXXXXXX');

データベース作成(A) これは B に同期される
mysql> create database testdb default character set utf8;

データベースユーザー作成(A, B)
mysql> grant all privileges on testdb.* to test_user@localhost identified by 'test_pass'; 

そして改めて A で作成したデータベース testdb が B に同期されていることを確認します:
test_user ユーザーで testdb にログイン(B)
# mysql -u test_user -ptest_pass testdb
mysql> Bでは作成していないデータベースにログインできた!

※B での mysql コマンドでソケットファイルのエラーが表示される場合は
# mysql -u test_user -ptest_pass testdb --socket=/tmp/mysql.sock
といった感じで、ソケットファイルのパスを指定して実行する必要があります。


【手順6 データの同期を確認(A, B)】
最後に A で作成したテーブルやデータが B に同期されることを確認します。まずはクラスタ対応のテーブルを作成します:
(サーバー A で作業)
# mysql -u test_user -ptest_pass testdb
mysql> create table testtbl(n int) engine=ndbcluster; mysql> show table status *************************** 1. row *************************** Name: testtbl Engine: ndbcluster Version: 10 : :
mysql>

このテーブルに適当にデータを入れておきます:
(サーバー A で作業)
mysql> insert into testtbl values (123); mysql> select * from testtbl; +------+ | n | +------+ | 123 | +------+

このテーブルとデータがサーバー B にも同期されていることを確認します:
(サーバー B で作業)
# mysql -u test_user -ptest_pass testdb (--socket=/tmp/mysql.sock) mysql> select * from testtbl; +------+ | n | +------+ | 123 | +------+

無事、データの同期もできていることが確認できました。





このページのトップヘ