Flareを使う(マルチテナント化編)

  • ohkubo
at
    Tags:
  • KVS

当ブログをご覧の皆様こんにちは。さくらインターネット研究所の大久保です。 今回はFlareを例に分散KVSをマルチテナント化する方法について考えてみたいと思います。

マルチテナント化の必要性

過去の記事では、1つのユーザが分散KVSのサーバファームを独占して使用する前提で設定を行ってきましたが、分散KVSを利用するユーザが複数居た場合はどのようにしたらよいでしょうか?

下図のように単純にユーザごとにサーバファームを用意してしまうという方法が考えられますが、ユーザ毎に一定台数のサーバが必要であり、コストが掛かってしまいます。

Image

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

Image

マルチテナント構成を実現するメリットとしては、

  • ユーザごとに物理サーバを用意する必要がなく、オンデマンドでリソースの確保が可能。
  • 収容効率をアップでき、サーバの台数を削減できる。(特に、利用率の低いユーザが複数居た場合、集約効果が見込める)
  • ユーザ毎に別個のサーバトポロジを管理する必要がなく、全ユーザ共通のトポロジで設計、拡張ができる。

という点があげられます。

マルチテナント方式

マルチテナントを実装するにあたっては、各ユーザのデータ空間を分離する必要があります。具体的には以下の2点を満たさなければなりません。

  • あるユーザのデータが別のユーザのデータと混ざることなく分離できる
  • あるユーザが別のユーザのデータにアクセスできないようにする

検討した結果、これらを満たす方法として以下の3つの方式を考えました。

  1. 各ユーザ毎に仮想サーバを立ち上げ、VLANでネットワークを分割する 仮想化技術を用いた方式で、各ユーザ毎に物理サーバを用意する代わりに仮想サーバを用意します。 インフラレベルで論理的に分離できるため、隔離度は高くセキュアです。ただ、新規ユーザを収容するためにVLAN設定、VMの作成、IPアドレスの割り当てが必要であり、VMの動作にもオーバヘッドが発生します。
    Image
  2. ポート番号を分けて複数のデーモンを立ち上げる 上記1とは異なり、OSやネットワークは共有しますが、ポート番号を分けて複数のデーモンを立ち上げることにより複数の分散KVSの面を重ねる方法です。 新規ユーザを収容するためにはポート番号の予約、プロセスの起動を行うだけでよく、IPアドレス割り当てやVLAN作成は必要ありません。比較的軽量ですが、ポート番号を間違えて起動すると正常に動作しないばかりでなく、別のユーザのデータが混ざってしまう可能性もあるため、慎重な操作が必要です。
    Image
  3. キーにPrefixを付与して名前空間を分離する プロキシサーバからストレージサーバにデータを格納する際、ユーザリクエストに含まれるキーに特定の文字列(Prefix)を付加します。複数のユーザのデータが1つのKVSの面に格納されますが、名前空間で分離されます。 各ストレージサーバにて起動するデーモンは1個でよいので2.よりもさらに軽量ですが、キーの操作処理のためソフトウェアの改変が必要です。
    Image

今回はソフトウェアの修正が必要なく、比較的軽量な「2.ポート番号を分けて複数のデーモンを立ち上げる」方式を検討することにしました。

設定方法

下図のように、Flareを用いてユーザAとユーザBの2面を一つのサーバファームに重ねる構成で説明します。

Image
  1. インデックスサーバにて ユーザ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
  2. ストレージサーバ(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ユーザを想定したテストを行いましたが、サーバのリソースが許す限りユーザの追加が可能です。ユーザやアクセス数が増えた場合は、ストレージサーバ全体の負荷状況を見ながらパーティションを分割しサーバを追加していけば良いでしょう。