Jabberサーバーをクラスタリングしてみました。
みなさん Jabber をご存じですか? Jabber はオープンな仕様のメッセン ジャーサービスのことで最近だと Google Talk で使用している方が多いと思 います。
KLab では 2001年ぐらいから、誰でも Jabber を使うことが出来る Jabber.JP というサービスを運用してい ます。まだ Jabber を使用したことがない方は是非こちらの「Jabber をはじめよう」 を ご覧になって Jabber を使ってみて下さい。
Jabber.JP は jabberd という実装を使用してい たのですが、冗長構成が難しいという問題がありました。
そこで先日、Jabber.JP では大規 模なメンテナンスを行い ejabberd という実装で分散、冗長化を行ってみました。ejabberd はその名の通り、Erlang で 実装された Jabber サーバーで、Mnesia データーベースを使用してデータの 冗長化を行うことが出来ます。
今回は Jabber.JP で動いている ejabberd やそのクラスタリング方法について紹介したいと思います。
KLab勉強 会#3 に来られた方は見たことがあるかもしれませんが、こんな感じの構 成です。

Jabber クライアントから jabber.jp:5222(5223) へのアクセスは LVS で 分散され、3台のサーバー(ノード)で動作する ejabberd に割り振られます。 ユーザーアカウントのデータなどは Mnesia のデーターベースに保存され、3 台のノードで動作する Mnesia はマルチマスターレプリケーションを行ってい ます。
この様な構成にすることで、3台のサーバーのうち2台が落っこちてもサー ビスを継続出来るようになっています。
それでは具体的な ejabberd でのクラスタリング方法を紹介します。
まず最初の 1台目の ejabberd を普通に起動します。
$ erl -name ejabberd@host1 -s ejabberd
この状態で、mnesia:info/0 を実行し、データーベースの内容を確認する と以下のような出力が得られます。
opt_disc. Directory "/home/jabberd/Mnesia.ejabberd@host1" is used.
use fallback at restart = false
running db nodes = [ejabberd@host1]
stopped db nodes = []
master node tables = []
remote = []
ram_copies = [route,s2s,session]
disc_copies = [acl,
config,
last_activity,
local_config,
motd,
motd_users,
muc_registered,
muc_room,
passwd,
privacy,
roster,
schema,
vcard_search]
disc_only_copies = [disco_publish,offline_msg,vcard]
[{ejabberd@host1,disc_copies}] = [schema,
acl,
config,
local_config,
passwd,
roster,
privacy,
vcard_search,
motd,
motd_users,
muc_room,
muc_registered,
last_activity]
[{ejabberd@host1,disc_only_copies}] = [disco_publish,vcard,offline_msg]
[{ejabberd@host1,ram_copies}] = [route,session,s2s]
running db nodes, stopped db nodes ではレプリケーションを行っている Mnesia ノードのステータスを確認出来ます。今はレプリケーションを行って いないのでひとつしか表示されていません。
続いて remote, ram_copies, disc_copies, disc_only_copies という 4種 類のテーブルタイプ毎に ejabberd で使用するデーターベーステーブルが表示 されています。remote を除く 3種類のテーブルタイプについては過去の記事 「Erlang版 memcached でキャッシュデータの永続化をしてみました。」をご覧くださ い。
このテーブルタイプ情報をみると、ユーザーからのコネクションを管理す る session テーブルは メモリベース、ユーザーアカウントデータを格納する passwd テーブルはディスクベース、という様に使い分けられていることが分 かります
これで、1台目の ejabberd が動作しましたが、この 1台が落っこちるとサー ビスが停止してしまうので 2台目の ejabberd を追加してみます。
2台目の ejabberd を追加するときはいきなり ejabberd を起動するのでは なく、まず Mnesia だけを起動して、レプリケーションの準備を行います。
$ erl -name ejabberd@host2 -mnesia extra_db_nodes "['ejabberd@host1']" -s mnesia
Mnesia を起動し、host2 で mnesia:info/0 を実行すると以下のように出 力されました。
opt_disc. Directory "/home/jabberd/Mnesia.ejabberd@host2" is NOT used.
use fallback at restart = false
running db nodes = ['ejabberd@host1','ejabberd@host2']
stopped db nodes = []
master node tables = []
remote = [acl,
config,
disco_publish,
last_activity,
local_config,
motd,
motd_users,
muc_registered,
muc_room,
offline_msg,
passwd,
privacy,
roster,
route,
s2s,
session,
vcard,
vcard_search]
ram_copies = [schema]
disc_copies = []
disc_only_copies = []
[] = [local_config]
[{'ejabberd@host1',disc_copies}] = [last_activity,
muc_registered,
muc_room,
motd_users,
motd,
vcard_search,
privacy,
roster,
passwd,
config,
acl]
[{'ejabberd@host1',disc_copies},
{'ejabberd@host2',ram_copies}] = [schema]
[{'ejabberd@host1',disc_only_copies}] = [offline_msg,
vcard,
disco_publish]
[{'ejabberd@host1',ram_copies}] = [s2s,session,route]
ejabberd を1台だけ起動したときと異なり、schema を除く全てのテーブル は remote というタイプになっています。これは host2 のノードからデータ ベースにアクセスした場合、間接的に host1 で動作する Mnesia データーベー ステーブルにアクセスすることを意味します。この状態で ejabberd を立ち上 げても正常に動作しますが、データーベースへのアクセスは host1 に集中す ることになり、host1 が落っこちた場合 host2 の ejabberd もサービス不能 に陥ります。
つづいて、host2 にある remote タイプのテーブルをレプリケートしてみ ます。
mnesia:change_table_copy_type(schema, node(), disc_copies). mnesia:add_table_copy(route, node(), ram_copies). mnesia:add_table_copy(s2s, node(), ram_copies). mnesia:add_table_copy(session, node(), ram_copies). mnesia:add_table_copy(acl, node(), disc_copies). mnesia:add_table_copy(config, node(), disc_copies). mnesia:add_table_copy(last_activity, node(), disc_copies). mnesia:add_table_copy(muc_registered, node(), disc_copies). mnesia:add_table_copy(muc_room, node(), disc_copies). mnesia:add_table_copy(motd_users, node(), disc_copies). mnesia:add_table_copy(motd, node(), disc_copies). mnesia:add_table_copy(vcard_search, node(), disc_copies). mnesia:add_table_copy(privacy, node(), disc_copies). mnesia:add_table_copy(roster, node(), disc_copies). mnesia:add_table_copy(passwd, node(), disc_copies). mnesia:add_table_copy(vcard, node(), disc_only_copies). mnesia:add_table_copy(disco_publish, node(), disc_only_copies). mnesia:add_table_copy(offline_msg, node(), disc_only_copies).
この様な設定を行った後、host2 で ejabberd を起動して mnesia:info/0 を確認してみます。
$ erl -name ejabberd@host2 -s ejabberd
opt_disc. Directory "/home/jabberd/Mnesia.ejabberd@host2" is used.
use fallback at restart = false
running db nodes = ['ejabberd@host1','ejabberd@host2']
stopped db nodes = []
master node tables = []
remote = []
ram_copies = [local_config,route,s2s,session]
disc_copies = [acl,
config,
last_activity,
motd,
motd_users,
muc_registered,
muc_room,
passwd,
privacy,
roster,
schema,
vcard_search]
disc_only_copies = [disco_publish,offline_msg,vcard]
[{'ejabberd@host1',disc_copies},
{'ejabberd@host2',disc_copies}] = [muc_registered,
muc_room,
schema,
motd_users,
vcard_search,
acl,
motd,
last_activity,
roster,
passwd,
privacy,
config]
[{'ejabberd@host1',disc_only_copies},
{'ejabberd@host2',disc_only_copies}] = [vcard,
disco_publish,
offline_msg]
[{'ejabberd@host1',ram_copies},
{'ejabberd@host2',ram_copies}] = [route,s2s,session]
[{'ejabberd@host2',ram_copies}] = [local_config]
remote タイプのテーブルはなくなり、host1, host2 の Mnesia で local_config を除く全てのテーブルをレプリケーションすることが出来まし た。これと同じ要領で 3台目の ejabberd を追加することが出来ます。
Erlang の Mnesia はすこし取っつきにくいかもしれませんが、こんなに簡 単にマルチマスターレプリケーションが出来るのは DBMS としてとても魅力的 だと思います。
耐障害性の高い Jabber サーバーを立ててみようかなと考えている方には、 是非 ejabberd をオススメします。
トラックバックURL
この記事へのトラックバック
この記事へのコメント
少し追っかけてみると、ejabberd の内部で使用している XML パーサーの libexpat がエラーを返しているようです。
解決方法としては、libexpat で 32K 以上の要素? を扱えるようにしてやる、あるいは libexpat では無く、他の XML パーサー(libxml, xmerl)を使うようにしてやる必要があるかもしれません。
どちらにせよ、ejabberd の改変が必要なのでちょっと大変かもしれませんね。
個人的にはこのぐらいの制限があった方がサーバーに負荷が掛かり過ぎなくてちょうど良いのではないかと思っています^^;
