Tags

By year

  1. 2014 (28)

ElastiCache for memcached の分散処理をどうするか悩む

AWS ElastiCache RDS memcached twemproxy

Posted on Apr 26


はじめに

ちょいウンチク(というか個人的なメモ)。

  • ElastiCachememcahcedRedis に対応したキャッシュサービス
  • Redis の場合にはレプリケーションが可能
  • memcached もクラスタ内にノードを複数持てるが…しかしキャッシュの同期等は出来ない

以下の図のような事がデフォルトでは出来ないことになる…。

ということで memcached を使う場合、可用性を考慮して複数のノードで分散処理を考えた場合には何か別な方法を考えにゃあかんことになる。



参考

ググると以下のように既に同じ疑問にぶち当たって試行錯誤されている方の事例がヒットする。

どうやら twemproxy というツールを使うことで分散処理を実現出来そうだ。

また、RDSMySQL 5.6memcached プラグインを使い RDSMulti AZ を組み合わせることで可用性の向上が期待出来そう。

ということで、上記の参考にならい twemproxy の設定と簡単なベンチマークを取ってみたい。既に上記の参考等で同様のベンチマークを取られていたりするが手を動かしたい性分なので自分でもやってみる。



設定


もろもろ割愛

ElastiCachememcached 設定自体は割愛するが構築する ElastiCachememcached は以下のような構成。

  • cache.t1.micro
  • memcached 1.4.14
  • 2 nodes

以下、今回のキモになりそうな部分だけ手順等を記載。


twemproxy(nutcracker)

twemproxytwitter でも利用されていたのされるツールだが、現在は nutcracker という名前のようだ。インストールはコンパイルが必要となるので注意。今回利用した環境は EC2 インスタンス(t1.micro からの Amazon Linux)。

wget https://twemproxy.googlecode.com/files/nutcracker-0.3.0.tar.gz
tar zxvf utcracker-0.3.0.tar.gz
cd utcracker-0.3.0
./configure
make
sudo make install

ビルドにあたっては開発ツールが必要になるので適宜インストールしておくのをお忘れなく…

以下は一応ヘルプ表示。

This is nutcracker-0.3.0

Usage: nutcracker [-?hVdDt] [-v verbosity level] [-o output file]
                  [-c conf file] [-s stats port] [-a stats addr]
                  [-i stats interval] [-p pid file] [-m mbuf size]

Options:
  -h, --help             : this help
  -V, --version          : show version and exit
  -t, --test-conf        : test configuration for syntax errors and exit
  -d, --daemonize        : run as a daemon
  -D, --describe-stats   : print stats description and exit
  -v, --verbosity=N      : set logging level (default: 5, min: 0, max: 11)
  -o, --output=S         : set logging file (default: stderr)
  -c, --conf-file=S      : set configuration file (default: conf/nutcracker.yml)
  -s, --stats-port=N     : set stats monitoring port (default: 22222)
  -a, --stats-addr=S     : set stats monitoring ip (default: 0.0.0.0)
  -i, --stats-interval=N : set stats aggregation interval in msec (default: 30000 msec)
  -p, --pid-file=S       : set pid file (default: off)
  -m, --mbuf-size=N      : set size of mbuf chunk in bytes (default: 16384 bytes)

設定は YAML 形式で書き、ファイル名を nutcracker.yml として以下のように設定した。

hoge:
  listen: 127.0.0.1:11211
  hash: fnv1a_64
  distribution: ketama
  timeout: 100
  auto_eject_hosts: true
  server_retry_timeout: 2000
  server_failure_limit: 1
  servers:
   - 192.168.xxx.1:11211:1
   - 192.168.xxx.2:11211:1

各種設定についてはざっくり下記の通り。

  • listennutcrackerListen する IPPort を指定する
  • hash はハッシュ関数の種類を指定する
  • distribution はキー配布の種類を指定する
  • auto_eject_hosts はノードが停止した場合に停止したノードを切り離すかを指定する
  • server_retry_timeoutauto_eject_hosttrue の場合にノード(サーバー)への再接続の時間を指定(デフォルトは 30000 ミリ秒)
  • server_failure_limitauto_eject_hosttrue の場合にノード(サーバー)への接続失敗の上限(デフォルトは 2
  • servers には memcached のノードを指定する(追記:ノードは IP アドレスで指定する必要がある)

設定後に以下のようにして nutcracker を起動する。

nutcracker -d -c nutcracker.yml

-d はデーモンモード、-c で設定ファイルを指定する。起動すると 11211 番ポートと 22222 番ポートが Listen する。22222 番ポートは nutcracker のステータスが確認することが出来るので…起動したことは下記のようにして確認。

curl -s localhost:22222 | jq .

以下のような結果が JSON で返ってくる。

{
  "hoge": {
    "192.168.xxx.53": {
      "out_queue_bytes": 0,
      "out_queue": 0,
      "in_queue_bytes": 0,
      "in_queue": 0,
      "response_bytes": 16,
      "server_eof": 0,
      "server_err": 0,
      "server_timedout": 0,
      "server_connections": 1,
      "server_ejected_at": 0,
      "requests": 2,
      "request_bytes": 42,
      "responses": 2
    },
    "192.168.xxx.164": {
      "out_queue_bytes": 0,
      "out_queue": 0,
      "in_queue_bytes": 0,
      "in_queue": 0,
      "response_bytes": 0,
      "server_eof": 0,
      "server_err": 0,
      "server_timedout": 0,
      "server_connections": 0,
      "server_ejected_at": 0,
      "requests": 0,
      "request_bytes": 0,
      "responses": 0
    },
    "fragments": 0,
    "forward_error": 0,
    "server_ejects": 0,
    "client_connections": 2,
    "client_err": 0,
    "client_eof": 0
  },
  "timestamp": 1398523199,
  "uptime": 213,
  "version": "0.3.0",
  "source": "ip-192-168-xxx-138",
  "service": "nutcracker"
}

おお、動いているようだ。尚、構成下記のようなイメージとなる。


memslap の導入

memcached のベンチマークには memslap というツールを使う。memslaplibmemcached に同梱されている。

sudo yum install libmemcached.x86_64

簡単な使い方は下記の通り。

memslap -s 127.0.0.1:11211


ベンチマーク

いきなりで恐縮だが、とりあえず以下のコマンドにてベンチマークを取ってみた。

memslap -s ${memcached_host}

${memcached_host} には nutcracker.ymllisten で指定されたホストと ElastiCache のノード一台を指定してベンチマークを取得した。


twemproxy 経由

以下の通り 3 回テスト。

  1. 5.108 sec
  2. 4.885 sec
  3. 6.067 sec

ElastiCache for memcached 1 node

以下の通り 3 回テスト。

  1. 4.139 sec
  2. 5.472 sec
  3. 3.923 sec

細かいパラメータの指定は行っていないが、上記の通り twemproxy 経由は若干オーバーヘッドがあるようだ。



twemproxy の動きを見てみる


今更だけど

twemproxy って何してんだろうってことで…debug モードを有効にしてコンパイルしてみる。

CFLAGS="-ggdb3 -O0" ./configure --enable-debug=full
make
sudo make install

デバッグモードを最高にしてログを /tmp/debug.log に出力して起動する場合には下記のように実行する。

nutcracker -d -c nutcracker.yml -v 11 -o /tmp/debug.log

ログ見てみる

以下のように setget のリクエストを投げた場合…

echo -e "set test 0 0 3\r\n123\r" | nc localhost 11211

からの…

echo "get test" | nc -C localhost 11211 

を実行すると以下のように結果は表示される。

VALUE test 0 3
123
END

ログは以下の gist のようなログが出力される。

結構な量のログが流れる中で set した際に…

nc_server.c:644 key 'test' on dist 0 maps to server 'xxx.xxx.x.164:11211:1'

上記のようなログが記録される。また、get の場合にも…

nc_server.c:644 key 'test' on dist 0 maps to server 'xxx.xxx.x.164:11211:1'

上記のようなログが流れる。twemproxy は該当の key がどのノードに記録されているかを記憶しているのかな…。ということは、該当の key がストアされているノードが停止した場合にはどうなるのかしらということでノードを停止してみたところ…

$ echo "get test" | nc -C localhost 11211
SERVER_ERROR Connection timed out

上記のようにエラーとなった。また、key がストアされていないノードへの問い合わせが発生した場合には下記のような出力となった。

$ echo "get test" | nc -C localhost 11211
END

自分なりの理解

  • twemproxy は何らかのアルゴリズムで複数のノードの中から選択して一台のノードにデータを set している
  • そして…キーがどのノードに set されたかを管理している
  • get のリクエストがあった場合にはデータが記録されているノードから get してクライアントに返している
  • あくまでも twemproxy はキャッシュクラスタへのリクエストをルーティングしているだけでノード間のデータ同期等の面倒までは見ていない(当たり前と言えば、当たり前だが…)


最後に

  • twemproxy を利用すれば ElastiCache for memcached クラスタでデータの分散処理は行える
  • 但し、データの可用性、冗長性は期待できないので注意する
  • 可用性、冗長性の考慮が必要であれば ElastiCache for Redis 又は RDS for MySQL 5.6 + memcached Plugin + Multi AZ を選択する必要があると思う
  • RDS for MySQL 5.6 + memcached Plugin の構成については別途で簡単なベンチマークをとってみたい



2014 かっぱのほげふが