分散Key/Valueストア、Kaiを使ってみよう!

第4回Kaiの詳細(2)―Kaiの設定をチューニングする

今回は、Kaiの設定とチューニングについて詳しく説明します。

なお、本連載が対象とする Kaiのバージョンは0.4、ErlangのバージョンはR13Bです。

データ管理の単位 = バケット

ここでは、Kai設定ファイル内の number_of_bucketsについて説明します。

number_of_bucketsは、ノード間での負荷の偏りや、データ配置の計算コストを調整するために使用します。ノード数が数十程度であれば、デフォルト設定のまま変更する必要はありません。

Kaiでは、担当ノードの決定やデータの再配置を行なう際、効率を高めるため、データをバケットという単位でまとめて扱います。バケットとは、32ビットのハッシュ空間をnumber_of_bucketsに指定された値で均等に分割したものです。

図1 ハッシュ空間とパケットの関係
図1 ハッシュ空間とパケットの関係

バケット数(number_of_buckets)が少ないと、ノードごとの担当データ量に偏りが生じるため、上手に負荷分散されません。極端な例ですが、バケットが一つであるならば、全てのデータが特定のノードに集中してしまいます。

一方で、バケット数が多いと、データを操作するときの処理量が大きくなります。大雑把な見積もりではありますが、ノード数が数十程度であれば、バケット数は、1,000~100,000の範囲が適当だと思われます。

なお、デフォルトでは1,024に設定してあります。もし、設定を変更するのであれば、以下の点に注意してください。

  1. なるべく、クラスタ内の総仮想ノード数よりも大きな値にする(仮想ノードについては後述します)
  2. クラスタを構成する全てのノードで、必ず、同じ値を設定する

(1) は、負荷分散のため、(2) は、データを操作するときの計算結果を、クラスタを構成する全ノードで一致させるために必要な条件となります。バケット数は、なるべく、将来の仮想ノードの増加を考慮して決めてください。

仮想ノードによるデータ配置の重み付け

ここでは、Kai設定ファイル内のnumber_of_virtual_nodesについて説明します。

number_of_virtual_nodesは、ノードの性能差に合わせて負荷を調節するために使用します。

本連載の第1回で、Kaiは、Consistent Hashingによりデータ割り当てを決定する際、リング状のハッシュ空間にノードを配置すると説明しました。

性能の高いノードには、他のノードより多くのデータを割り当てられると、より適切に負荷分散を行うことができます。Kaiでは、ノードをハッシュリングに配置する際、性能に合わせて擬似的なノードを多く配置することができます。このときの配置数をnumber_of_virtual_nodesによって設定します。

例えば、標準的な構成のノードには128を設定し、2倍のメモリを搭載したノードには256を設定します。なお、詳細は省略しますが、少なくとも100以上の数値を設定するようにしてください。

Consistent Hashingについてのより詳しい説明は、こちらのページもご参照ください。

プロセス数とソケット数の設定

ここでは、Kai設定ファイル内のmemcache_max_processes、rpc_max_processes、max_connectionsについて説明します。

これらの設定値は、クライアントからの最大同時接続数を制限し、ネットワークのトラフィックを調節するために使用します。もし、設定を変更するのであれば、なるべく、以下の条件を守るようにしてください。

  • memcache_max_processes = rpc_max_processes / N
  • rpc_max_processes = クラスタを構成する全ノード数 * memcache_max_processes
  • max_connections = memcache_max_processes * N + α

Kaiは、デフォルトの設定ファイルを変更せずに起動すると、約70個のErlangプロセスを起動します。その内の約40個が、TCP/IP接続を待ち受ける為に待機しているプロセスです。待機中のプロセス数は、設定ファイルの memcache_max_processes、rpc_max_processes で設定を行う事ができます。

それぞれの意味は、以下の通りです。関連項目である、max_connections も共に説明します。

設定名説明
memcache_max_processesmemcachedクライアントからの同時接続数を設定します。
大きい値を設定すると同時接続数は増えますが、ノード間通信が増えるため、クラスタ全体の負荷が上がります。
また、rpc_max_processes、max_connectionsに大きな値を設定する必要があります。
rpc_max_processesクラスタを構成する他のノードからの同時接続数を設定します。
max_connections他のノードへの同時接続数を設定します。
ノード間通信では、Kai の性能を引き出すために、一つのノードに対して複数接続を行う事もあります。
memcache_max_processes、rpc_max_processesとは異なり、ここで設定した値はプロセス数とは関係ありません。

ノードにmemcachedクライアントが1つ接続すると、N個分(Nは、Quorumの設定値⁠⁠、クラスタ内の他のノードへ接続が発生します。さらに、定期的に自動で行なわれるノード間通信や、Erlang VMのコンソールから手動で行なうノード間通信を考慮すると、余分に幾つか接続数を確保しておく必要があります。よって、⁠max_connections = memcache_max_processes * N + α」となります。

また、ノード間通信で接続される側のノードを考慮すると、クラスタを構成する他のノードにmemcachedクライアントが1つ接続すると、自ノードへ1つ接続が発生する可能性があります。よって、⁠rpc_max_processes = クラスタを構成する全ノード数 * memcache_max_processes」となります。

データの取得・保存の際は、N個のノード全てがレスポンスを返す必要はないため、rpc_max_processesに少々低い値を設定しても、それほどレスポンス速度が低下する事はありません。しかし、Kai は、ノード間通信の転送を行なわないので、rpc_max_processes に大きな値を設定したとしても、ネットワーク・トラフィックが増える事はありません。

当たり前の事ですが、クラスタを構成するノードの memcache_max_processes の総数が、クラスタ全体の memcached クライアント同時接続数となります。クラスタにノードを追加する際は、ノード間通信によるネットワーク・トラフィックを軽減するために、ノードごとの memcache_max_processes を減らせないか検討してください。

なお、これらの設定値は、最適な値を見つける事が難しいため、検証環境をご用意いただき、実際に負荷を掛けながら希望するパフォーマンスに達するまでチューニングし続けてください。

例として、ストレージにetsを選んだ際に、以下の設定値で良いパフォーマンスが出たとの報告がありますので参考にしてください。

設定名設定値
memcache_max_processesrpc_max_processes / N
rcp_max_processes120
max_connections256

これらの設定値に大きな値を設定すると、EMFILEエラーが発生する事があります。

Erlang VMは、OSからは一つのプロセスとして扱われているため、Kaiが大量にファイル・ディスクリプタを消費すると、OSからは一つのプロセスが大量にファイル・ディスクリプタを消費していると見なされます。

よって、これらの設定値に大きな値を指定する際は、limitコマンドやsysctlコマンド、OSの設定ファイルを編集するなどして、一つのプロセスが扱えるファイル・ディスクリプタの数を増やす必要があります。

設定方法は、各プラットフォームごとに異なります。以下に例を挙げておきますが、詳しくは、ご利用のプラットフォームのマニュアルなどをご参照ください。

プラットフォーム設定方法
Linux"/etc/security/limits.conf"を編集し、Kai実行ユーザのnofileに大きな値を設定してください。
必要があれば、"/etc/sysctl.conf"を編集し、fs.file-maxに大きな値を設定してください。
*BSD"/etc/login.conf"を編集し、Kai実行ユーザのログインクラスのopenfilesに大きな値を設定してください。
必要があれば、"/etc/sysctl.conf"を編集し、kern.maxfilesとkern.maxfilesperprocに大きな値を設定してください。

また、スループットが高くなる事が予想される場合は、ソケットバッファのサイズを増やす事で、Kaiの性能を引き出す事ができます。

こちらも、設定方法が各プラットフォームごとに異なります。以下に例を挙げておきますが、詳しくは、ご利用のプラットフォームのマニュアルなどをご参照ください。

プラットフォーム設定方法
Linux"/etc/sysctl.conf"を編集し、net.core.rmem_max、net.core.wmem_max、net.core.optmem_maxに大きな値を設定してください。
*BSD"/etc/sysctl.conf"を編集し、net.inet.tcp.sendspace、net.inet.tcp.recvspace、kern.ipc.maxsockbufに大きな値を設定してください。

前述で、ネットワーク・トラフィックに関してのみ説明しましたが、プロセスを大量にSpawnする事で発生する負荷には触れませんでした。

ご存知の方も多いかとは思いますが、Erlangにはプロセスを大量にSpawnしても負荷が低いという利点があります。このため(だけではありませんが⁠⁠、Erlangで作られたサーバは、プログラムの設計やチューニング次第で、C10K問題を比較的簡単に回避する事が可能です。

Kaiでプロセス数を調節しているのは、プロセス負荷を下げる事が目的ではなく、ネットワークトラフィックを下げる事が目的です。

なお、ErlangプロセスやC10K問題のより詳しい内容については以下をご参照ください。

ノードが格納できるデータのサイズ

ここでは、Kai設定ファイル内のnumber_of_tablesについて説明します。

number_of_tables は、データをディクスに保存する際のサイズを調節するために使用します。より具体的には、ノードがディスクに格納できるデータのサイズは「number_of_tables * 2G Byte」となります。

第2回では、データをディスクに保存する方法と、store、dets_dirについて説明しました。

その際、データの保存先にディスクを指定するのは、念には念を入れたい場合や、何らかの事情でクラスタ全体を停止したい場合と説明しましたが、もう一つだけ理由があります。

その理由を説明する前に、以下の表をご覧ください。

 32bit64bit
ets3G Byte3G Byte 以上(メモリサイズによる)
dets2G Byte2G Byte

この表は、Erlangのストレージets、detsのテーブル一つが保存できるデータの最大サイズを示したものです。

etsテーブルは、32bitアーキテクチャであれば3G Byte まで、64bitアーキテクチャであれば3G Byte以上のデータを保存できます。Kaiでは、ストレージとしてメモリを選択すると etsテーブルを使うので、3G Byte以上のデータを保存するならば、64bitアーキテクチャ上に環境を構築する必要があります。

detsテーブルは、アーキテクチャとは無関係に、常に 2G Byteまでデータを保存できます。Kaiでは、ストレージとしてディスクを選択すると、容量の制限を回避するため、複数のdetsテーブルを束ねて使います。detsテーブルの数は、number_of_tablesに設定します。例えば、number_of_tables = 256であるならば、そのノードの最大ストレージサイズは512G Byteとなります。

このため、メモリに収まらないサイズのデータを保存するために、ストレージとしてディスクを使用するという選択肢が増えます。ただし、detsテーブルの最大サイズは2G Byteであるため、一つのデータが2G Byteを超える場合、そのデータは保存できない点に注意してください。また、当たり前の事ですが、メモリよりディスクは低速である点に注意してください。

なお、ets、detsテーブル共に保存するデータの件数に制限はありません。

その他の設定値

ここでは、Kai設定ファイル内のまだ説明を行なっていない設定について説明します。

設定名説明
logfileログを残すファイルのパスを設定します。未設定の場合は、Erlang VM のコンソールにログが出力されます。
初期は "kai.log" がコメントアウトされているので、使用する際は、コメントを解除し、フルパスに書き換えてください。
hostnameノード情報に利用されるホスト名を設定します。名前解決できるホスト名か、IP アドレスを設定してください。
IPアドレスを設定する場合は、以下のような形式で設定してください。
{hostname, {10, 0, 0, 1}} 
% .(ドット) ではなく , (カンマ) 区切りです
rpc_portノード間通信を待ち受けるTCP/IPのポート番号を設定します。
memcache_portmemcachedクライアントからの接続を待ち受けるTCP/IPのポート番号を設定します。

hostnameに設定された値は、名前解決された後にノードの情報として利用されます。より具体的には、IPアドレスとポート番号の組み合わせがノードの一意情報として利用されています。

このため、ノードは、必ずクラスタを構成する他のノードと、IPアドレスかポート番号が異なっている必要があります。

本連載の第2回第3回では、Kaiを簡単に試していただけるように、一台の物理マシン上で複数のノードを起動していたので、ノードごとに異なる値をrpc_portに設定していました。

しかし、複数の物理マシンごとにノードを起動する場合は、hostnameを初期値の"localhost"から、その物理マシンのホスト名 (もしくは、ループバックアドレス以外のIPアドレス) に変更してください。

蛇足ですが、hostnameはListenするIPアドレスを設定する項目ではありません。

将来的には、NICの負荷を分散するため、memcached、ノード間通信インターフェース、それぞれにListenするIPアドレスを設定できるようにする予定です。

次回の予告

本連載の第2回から今回まで、Kaiを導入するために必要となる情報を一通り説明してきました。Kaiとは「AmazonのインフラであるDynamoを手軽に導入できる現実的な実装」である事が、お分かりいただけたでしょうか?

次回は、Kaiの導入事例の紹介と運用のノウハウを説明します。どうぞ、ご期待ください。

おすすめ記事

記事・ニュース一覧