Flareを使う(マルチテナント化編)
当ブログをご覧の皆様こんにちは。さくらインターネット研究所の大久保です。 今回はFlareを例に分散KVSをマルチテナント化する方法について考えてみたいと思います。
マルチテナント化の必要性
過去の記事では、1つのユーザが分散KVSのサーバファームを独占して使用する前提で設定を行ってきましたが、分散KVSを利用するユーザが複数居た場合はどのようにしたらよいでしょうか?
下図のように単純にユーザごとにサーバファームを用意してしまうという方法が考えられますが、ユーザ毎に一定台数のサーバが必要であり、コストが掛かってしまいます。

そこで、1つのサーバファームに複数のユーザを収容する、いわゆるマルチテナント構成を実装する方法を考えてみます。

マルチテナント構成を実現するメリットとしては、
- ユーザごとに物理サーバを用意する必要がなく、オンデマンドでリソースの確保が可能。
- 収容効率をアップでき、サーバの台数を削減できる。(特に、利用率の低いユーザが複数居た場合、集約効果が見込める)
- ユーザ毎に別個のサーバトポロジを管理する必要がなく、全ユーザ共通のトポロジで設計、拡張ができる。
という点があげられます。
マルチテナント方式
マルチテナントを実装するにあたっては、各ユーザのデータ空間を分離する必要があります。具体的には以下の2点を満たさなければなりません。
- あるユーザのデータが別のユーザのデータと混ざることなく分離できる
- あるユーザが別のユーザのデータにアクセスできないようにする
検討した結果、これらを満たす方法として以下の3つの方式を考えました。
- 各ユーザ毎に仮想サーバを立ち上げ、VLANでネットワークを分割する 仮想化技術を用いた方式で、各ユーザ毎に物理サーバを用意する代わりに仮想サーバを用意します。 インフラレベルで論理的に分離できるため、隔離度は高くセキュアです。ただ、新規ユーザを収容するためにVLAN設定、VMの作成、IPアドレスの割り当てが必要であり、VMの動作にもオーバヘッドが発生します。
- ポート番号を分けて複数のデーモンを立ち上げる 上記1とは異なり、OSやネットワークは共有しますが、ポート番号を分けて複数のデーモンを立ち上げることにより複数の分散KVSの面を重ねる方法です。 新規ユーザを収容するためにはポート番号の予約、プロセスの起動を行うだけでよく、IPアドレス割り当てやVLAN作成は必要ありません。比較的軽量ですが、ポート番号を間違えて起動すると正常に動作しないばかりでなく、別のユーザのデータが混ざってしまう可能性もあるため、慎重な操作が必要です。
- キーにPrefixを付与して名前空間を分離する プロキシサーバからストレージサーバにデータを格納する際、ユーザリクエストに含まれるキーに特定の文字列(Prefix)を付加します。複数のユーザのデータが1つのKVSの面に格納されますが、名前空間で分離されます。 各ストレージサーバにて起動するデーモンは1個でよいので2.よりもさらに軽量ですが、キーの操作処理のためソフトウェアの改変が必要です。
今回はソフトウェアの修正が必要なく、比較的軽量な「2.ポート番号を分けて複数のデーモンを立ち上げる」方式を検討することにしました。
設定方法
下図のように、Flareを用いてユーザAとユーザBの2面を一つのサーバファームに重ねる構成で説明します。

- インデックスサーバにて ユーザAとユーザB用のディレクトリを作成し、ユーザAは12120番ポート、ユーザBは12130番ポートを使用するように設定ファイル(flarei.conf)を作成し、デーモンを起動します。
$ pwd
/home/admin
$ mkdir user-a
$ mkdir user-b
$ cat > user-a/flarei.conf
data-dir = /home/admin/user-a
log-facility = local0
server-name = 192.168.12.151
server-port = 12120
monitor-threshold=3
monitor-interval=1
monitor-read-timeout=1000
$ cat > user-b/flarei.conf
data-dir = /home/admin/user-b
log-facility = local0
server-name = 192.168.12.151
server-port = 12130
monitor-threshold=3
monitor-interval=1
monitor-read-timeout=1000
$ /usr/bin/flarei -f /home/admin/user-a/flarei.conf --daemonize
$ /usr/bin/flarei -f /home/admin/user-b/flarei.conf --daemonize - ストレージサーバ(Master,Slave)、およびプロキシサーバにて インデックスサーバと同様に2つのディレクトリを作成し、ユーザAは12121番ポート、ユーザBは12131番ポートを使用するように設定ファイル(flared.conf)を作成し、デーモンを起動します。なお、flared.confのserver-nameは各サーバのIPアドレスに変更します。
$ pwd
/home/admin
$ mkdir user-a
$ mkdir user-b
$ cat > user-a/flared.conf
data-dir = /home/admin/user-a
log-facility = local0
storage-bucket-size = 16777216
storage-large = true
index-server-name = 192.168.12.151
index-server-port = 12120
server-name = 192.168.12.152
server-port = 12121
$ cat > user-b/flared.conf
data-dir = /home/admin/user-b
log-facility = local0
storage-bucket-size = 16777216
storage-large = true
index-server-name = 192.168.12.151
index-server-port = 12130
server-name = 192.168.12.152
server-port = 12131
$ /usr/bin/flared -f /home/admin/user-a/flared.conf --daemonize
$ /usr/bin/flared -f /home/admin/user-b/flared.conf --daemonize
デーモンの起動が終わったら、続いてインデックスサーバにて役割設定を行います。
- まずはユーザAのインデックスサーバにて設定します。
$ telnet localhost 12120
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
stats nodes
STAT 192.168.12.152:12121:role proxy
STAT 192.168.12.152:12121:state active
STAT 192.168.12.152:12121:partition -1
STAT 192.168.12.152:12121:balance 0
STAT 192.168.12.152:12121:thread_type 16
STAT 192.168.12.153:12121:role proxy
STAT 192.168.12.153:12121:state active
STAT 192.168.12.153:12121:partition -1
STAT 192.168.12.153:12121:balance 0
STAT 192.168.12.153:12121:thread_type 17
STAT 192.168.12.154:12121:role proxy
STAT 192.168.12.154:12121:state active
STAT 192.168.12.154:12121:partition -1
STAT 192.168.12.154:12121:balance 0
STAT 192.168.12.154:12121:thread_type 18
END
node role 192.168.12.152 12121 master 1 0
OK
node role 192.168.12.153 12121 slave 1 0
OK
node role 192.168.12.153 12121 slave 1 0
OK
stats nodes
STAT 192.168.12.152:12121:role master
STAT 192.168.12.152:12121:state active
STAT 192.168.12.152:12121:partition 0
STAT 192.168.12.152:12121:balance 1
STAT 192.168.12.152:12121:thread_type 16
STAT 192.168.12.153:12121:role slave
STAT 192.168.12.153:12121:state active
STAT 192.168.12.153:12121:partition 0
STAT 192.168.12.153:12121:balance 1
STAT 192.168.12.153:12121:thread_type 17
STAT 192.168.12.154:12121:role proxy
STAT 192.168.12.154:12121:state active
STAT 192.168.12.154:12121:partition -1
STAT 192.168.12.154:12121:balance 0
STAT 192.168.12.154:12121:thread_type 18
END
quit
Connection closed by foreign host. - つづいてユーザBのインデックスサーバにて設定します。
$ telnet localhost 12130
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
stats nodes
STAT 192.168.12.152:12131:role proxy
STAT 192.168.12.152:12131:state active
STAT 192.168.12.152:12131:partition -1
STAT 192.168.12.152:12131:balance 0
STAT 192.168.12.152:12131:thread_type 16
STAT 192.168.12.153:12131:role proxy
STAT 192.168.12.153:12131:state active
STAT 192.168.12.153:12131:partition -1
STAT 192.168.12.153:12131:balance 0
STAT 192.168.12.153:12131:thread_type 17
STAT 192.168.12.154:12131:role proxy
STAT 192.168.12.154:12131:state active
STAT 192.168.12.154:12131:partition -1
STAT 192.168.12.154:12131:balance 0
STAT 192.168.12.154:12131:thread_type 18
END
node role 192.168.12.152 12131 master 1 0
OK
node role 192.168.12.153 12131 slave 1 0
OK
node role 192.168.12.153 12131 slave 1 0
OK
stats nodes
STAT 192.168.12.152:12131:role master
STAT 192.168.12.152:12131:state active
STAT 192.168.12.152:12131:partition 0
STAT 192.168.12.152:12131:balance 1
STAT 192.168.12.152:12131:thread_type 16
STAT 192.168.12.153:12131:role slave
STAT 192.168.12.153:12131:state active
STAT 192.168.12.153:12131:partition 0
STAT 192.168.12.153:12131:balance 1
STAT 192.168.12.153:12131:thread_type 17
STAT 192.168.12.154:12131:role proxy
STAT 192.168.12.154:12131:state active
STAT 192.168.12.154:12131:partition -1
STAT 192.168.12.154:12131:balance 0
STAT 192.168.12.154:12131:thread_type 18
END
quit
Connection closed by foreign host.
これで2面の分散KVSが完成しました。
動作確認
実際にプロキシサーバからデータを投入して、データが分離されるか確認します。
- ユーザAの面に対して
$ telnet localhost 12121
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
get test
END
set test 0 0 5
abcde
STORED
get test
VALUE test 0 5
abcde
END
quit
Connection closed by foreign host. - ユーザBの面に対して
$ telnet localhost 12131
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
get test ← ユーザAの面に投入したデータは見えない
END
set test 0 0 5
12345
STORED
get test
VALUE test 0 5
12345
END
quit
Connection closed by foreign host. - ユーザAの面に対して
$ telnet localhost 12121
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
get test
VALUE test 0 5
abcde ← ユーザBの面に投入したデータで上書きされることはない
END
quit
Connection closed by foreign host.
上記のとおり、ユーザAの面とユーザBの面できちんとデータが分離できていることが確認できます。また、下記のとおりデータ数を確認すると各面で1つのデータが正しくレプリケーションされていることがわかります。
$ bin/flare-status.pl 12120
server:port role state balance partition items
--------------------------------------------------------------------
192.168.12.152:12121 master active 1 0 1
192.168.12.153:12121 slave active 1 0 1
192.168.12.154:12121 proxy active 0 -1 0
--------------------------------------------------------------------
sum 2
$ bin/flare-status.pl 12130
server:port role state balance partition items
--------------------------------------------------------------------
192.168.12.152:12131 master active 1 0 1
192.168.12.153:12131 slave active 1 0 1
192.168.12.154:12131 proxy active 0 -1 0
--------------------------------------------------------------------
sum 2
また、今回は説明を省きますがプロキシサーバにてユーザからのアクセス制限を実施する必要があります。
ユーザからのアクセスは、Memcachedプロトコルが用いられますが、Memcachedプロトコルには認証の機構がありません。そのため、ユーザAのポート番号にはユーザAのIPアドレスからのみ、ユーザBのポート番号にはユーザBのIPアドレスからのみ接続できるように、iptablesを使ったパケットフィルタ等でアクセス制限を実施する方式が現実的かと思います。
まとめ
上記のとおり、1つのサーバファーム上で複数のデーモンをポート番号を分けて起動することにより、マルチテナント構成を実装できることがわかりました。今回は2ユーザを想定したテストを行いましたが、サーバのリソースが許す限りユーザの追加が可能です。ユーザやアクセス数が増えた場合は、ストレージサーバ全体の負荷状況を見ながらパーティションを分割しサーバを追加していけば良いでしょう。