高速多重化共有ブロックデバイス V-FIELD

V-FIELDという単語は散々出てきているわけですが、ここでセールスポイントをまとめてみる。


V-FIELDは、大まかに言うと、ディスク(ブロックデバイス)をネットワークで読み取り専用で共有するもの。このとき、データをV-FIELDネットワークに参加しているすべてのノードに「重複させつつ分散させる」ことで、冗長化しつつ負荷分散する。ノードの動的な追加や離脱に対応している。

 


日本語で説明するよりC言語シェルスクリプトで説明した方が早そうな業界なので、突然具体的な使い方をば。

 
まず、1台目のノードを起動する。仮にIPアドレスを192.168.1.1とする。

192.168.1.1$ vfield -file viver.squash

'-file'引数に共有したいディスクイメージを指定する。


続いて2台目。

192.168.1.2$ vfield -bs 192.168.1.1 -store 128

'-bs'引数(boot strap)に、V-FIELDネットワークに参加しているノードの内の1台のIPアドレスを指定する。2台目を起動する時点では1台目のノードしかいないので、まずは192.168.1.1。


'-store'引数は、共有されているディスクイメージ(ここではviver.squash)のうちの、何MBをメモリ上に保持するかを指定する。起動時に指定された容量分だけ他のノードからダウンロードしてくる。
すべてのノードはクライアントでもサーバーであり、メモリ上に保持したディスクイメージを提供する。



この調子でどんどんノードを追加していく。
'-bs'引数にはV-FIELDネットワークに参加しているノードならどのノードを指定しても良いので、2台目のIPアドレスを指定してもいい。

192.168.1.3$ vfield -bs 192.168.1.2 -store 128

もちろん1台目でもいい。

192.168.1.4$ vfield -bs 192.168.1.1 -store 144


ここで、1台目と2台目で128MBずつ、3台目で144MB分メモリ上に保持したので、合計400MB分がメモリに載っている。たとえば共有しているディスクイメージ(ここではviver.squash)が300MBだった場合、各ノードのメモリ上に載っているデータだけでディスクイメージを完成できるので、1台目のノードを落としても(落ちてしまっても)良い。
さらにノードを増やしていくと、多重化度が上がっていく。



このように高い冗長性があるほか、データをダウンロードしてくるときに、複数のノードから並列してダウンロードすることで、高速にダウンロードする。また、ダウンロードを要求するノードを均等に(均等にと言うか、ラウンドロビンで)分散させることで、負荷を分散させる。ノードを追加すると多重化度が上がり、負荷が分散される。


実装面では、マルチスレッドに対応しており、カーネルから複数の要求を一度に受け取って、同時に処理する。
また、コネクションプールを実装しており、3-Way Handshakeのオーバーヘッドが無い。(コネクションプールがあると無いとでは、性能が10倍くらい違ったりする)




実際に性能を測ってみると、以下のようになった。

ノードの数 NBD V-FIELD
1対1 79.3MB/s 63.6MB/s
30台 3.8MB/s 8.6MB/s
50台 2.3MB/s 7.7MB/s

ギガビットのネットワークで、使用したマシンはXeon 3.6GHz ×2way、メモリ1GB、NIC Broadcom BCM5704sのブレード。(やたら性能が良いマシンで、もうちょっと安物のマシンでも良いかなと思うものの、環境が無い…)
NBDというのは、クライアント・サーバー型のディスクイメージ共有ソフトウェア。実はlinux 2.2の時代からLinuxカーネルに含まれている古参。
ディスクイメージのサイズは540MB。ディスクIOの影響を無視するためにtmpfs上に置いた。V-FIELDの-sotore引数は、すべてのノードで32MBとした。


まずは1対1で読み込み速度を測ってみた。するとNBDの方が若干速い…というのは、このデータがV-FIELDが並列処理に対応する前のものだからで、今は同じくらい。(正確なデータはまだ取っていない…)


次に全ノードで一斉にシーケンシャルリードを行って、すべてのデータ(540MB)を読み込み終わるまでの時間を測定し、平均の読み込み速度を計算した。
ノードの数が30台だと、V-FIELDの方が2倍くらい速い。50台になると3倍以上速い。(このときはダウンロード要求をラウンドロビンで分散させる機能を実装していないので、今はもう少し速いかもしれない)

V-FIELDはノード50台で平均7.7MB/s出るということは、50台×7.7MB/s×8≒3080Mbpsということで、1Gbpsを超える。NBDは50台×2.3MB/s×8≒920Mbpsで、1Gbpsを超えられない。ここに決定的な差がある。
今回はシーケンスリードであったため、データが3重化されているときは、ある瞬間でダウンロード要求を受け付けることができるノードは3台しか存在しない。(ちなみに50台の実験時は、各ノードが32MBずつ保持していて、ディスクイメージが540MBなので、50×32MB÷540MB≒3といことで、3重化されていた)
しかし各ノードがそれぞれランダムリードすれば、すべてのノードがダウンロード要求を受け付けることになるので、もっと速くなるはず。(…実際に試さないとダメですね)





V-FIELDの引数には、-bsや-storeの他に、'-join_boost'というものがある。(次のバージョンのVIVERから搭載予定。V-FIELDの開発リポジトリにはコミット済み)
VIVERでは、1台目がCDやUSBメモリから起動した後、他のマシンをネットワークブートするときに、多数のマシンを同時にブートすることが予想される。そのままでは、V-FIELDが起動時に-storeで指定された分をダウンロードするトラフィックがとんでもない量になり、ブート処理が止まってしまう。
'-join_boost'はそれを防ぐためのオプションで、起動時に-storeで指定された分をダウンロードするときに、ゆっくりダウンロードしたり、速くダウンロードしたりする。速度は0から9まで指定でき、9はフルスピード。デフォルトは7。


VIVERは、-join_boostパラメータをマシンによってランダムに変えている。あるノードはフルスピードでダウンロードし、すぐにサーバーになる。またあるノードはゆっくりダウンロードし、他の準備ができたノードにダウンロード要求を分散させる。






V-FIELDのポイントは、ノードを動的に追加できてスケールアウトする点と、各ノードが保持するデータの容量をそれぞれのノードが独自に決められる点。後でデータを見つけることを考えると、各ノードが保持するデータ容量は、サーバーから「何MB保持せよ!」と指定する仕組みにしそうになるが、それでは各ノードの都合に合わせて容量を決められない。V-FIELDでは、そもそもメモリをあまり搭載していないマシンや、あまりメモリを使いたくない(他のアプリケーションにメモリを使いたい)マシンは、少ししか保持しないでも良い。

そしてもう1つ、V-FIELDはLANで使うようにチューニングしてある。検索アルゴリズムにはDHTは使っておらず、むしろ単純なアルゴリズムを使っていて、非常に小さい遅延でデータをダウンロードを開始できるようになっている。



カーネルモードでの実装が嫌だったので、すべてユーザーランドで動くデーモンになっている。カーネルとのインターフェースには、NBDのサーバー機能を実装して、カーネル内のNBDクライアントと通信する。
…が、これがどうもそこそこ性能に影響しているような気がしてきた。カーネルとユーザーランドの間でメモリのコピーが多いからか。ということで、V-FIELDに関して次の目標は、カーネルモードで動かすこと…どうやってやれば良いのか…。