Redis-Twemproxy AgentでSentinelのmaster-change eventを監視する


## はじめに

Redis-Twemproxy Agentを使用して、Sentinelのmaster-change eventを監視する方法を記述します。

RedisのレプリケーションをSentinelにより監視している場合に、マスタのダウンを検知したときは、
Sentinelによりスレーブのマスタへの昇格が行われます。

Redis-Twemproxy AgentにSentinelを監視させ、マスタの変更をtwemproxyへ反映します。

* Redisのレプリケーションマスタがダウン
* Sentinelが、Redisのレプリケーションマスタのダウンを検知し、スレーブをマスタへ昇格させる
* Redis-Twemproxy Agentが、Sentinelのmaster-change evnetを検知し、twemproxyの設定を変更する

## 環境

* CentOS 6.5
* Redis 2.8.16
* twemproxy 0.30.0
* Redis-Twemproxy Agent
* node 0.10.29
* npm 1.3.6

## 構成

|役割    |名称      |ポート|
|:-------|:---------|:-----|
|master  |Redis     |6379  |
|slave   |Redis02   |6380  |
|slave   |Redis03   |6381  |
|sentinel|sentinel  |26379 |
|sentinel|sentinel02|26380 |
|sentinel|sentinel03|26381 |

Redis及びSentinelのインストール及び設定については、[RedisをSentinelで監視する](http://qiita.com/toshiro3/items/931ededeece6b8b1c680)をご参照ください。

## Redis-Twemproxy Agentによる監視設定

+ twemproxyのインストール

```shell
$ sudo yum install rpm-build
$ mkdir -p rmkdir $HOME/rpmbuild/{BUILD,SRPMS,SPECS,SOURCES,RPMS}
$ echo "_topdir $HOME/rpm" > $HOME/.rpmmacros
$ cd ~/rpmbuild/SOURCES
$ curl -L https://github.com/twitter/twemproxy/tarball/master | tar zx
$ mv twitter-twemproxy-b72c722 nutcracker-0.3.0
$ cp nutcracker-0.3.0/scripts/nutcracker.spec ~/rpmbuild/SPECS/
$ tar zcvf nutcracker-0.3.0.tar.gz nutcracker-0.3.0
$ rm -rf nutcracker-0.3.0
$ rpmbuild -ba ~/rpmbuild/SPECS/nutcracker.spec
$ sudo localinstall ~/rpmbuild/RPMS/x86_64/nutcracker-0.3.0-1.x86_64.rpm
```
* twemproxyの設定ファイルを作成します。

```yaml:/etc/nutcracker/nutcracker.yml
alpha:
  listen: 127.0.0.1:22121
  hash: fnv1a_64
  distribution: ketama
  auto_eject_hosts: true
  redis: true
  server_retry_timeout: 2000
  server_failure_limit: 1
  servers:
   - "127.0.0.1:6379:1 mymaster"
```
* 起動スクリプトを修正します。

```shell
$ sudo cp -p /etc/init.d/nutcracker /etc/init.d/nutcracker.org
$ sudo vi /etc/init.d/nutcracker
$ diff /etc/init.d/nutcracker.org /etc/init.d/nutcracker
12c12,14
< OPTIONS="-d -c /etc/nutcracker/nutcracker.yml"
---
> OPTIONS="-d -c /etc/nutcracker/nutcracker.yml \
>   -o /var/log/nutcracker/nutcracker.log \
>   -p /var/run/nutcracker/nutcracker.pid"

$ sudo mkdir /var/log/nutcracker
$ sudo chown nobody:nobody /var/log/nutcracker
$ sudo mkdir /var/run/nutcracker
$ sudo chown nobody:nobody /var/run/nutcracker
```
* twemproxyを起動します。

```shell
$ sudo /etc/init.d/nutcracker start
Starting nutcracker:                                       [  OK  ]
```

* Redis-Twemproxy Agentのインストール

```shell
$ sudo yum install nodejs npm
$ git clone https://github.com/Stono/redis-twemproxy-agent.git
$ cd redis-twemproxy-agent
$ npm install
$ cp lib/cli.js lib/cli.js.org
$ vi lib/cli.js
$ diff lib/cli.js.org lib/cli.js
5c5
<   host:    ['h', 'Redis sentinel hostname', 'string', '172.19.111.20'],
---
>   host:    ['h', 'Redis sentinel hostname', 'string', '127.0.0.1'],
7,9c7,9
<   config:  ['f', 'Path to twemproxy config', 'path', '/etc/twemproxy/conf/22121.yml'],
<   command: ['c', 'Command to restart twemproxy', 'string', '/etc/init.d/twemproxy restart'],
<   log:           ['l', 'The log file location', 'string', '/var/log/twemproxy/twemproxy_sentinel.log']
---
>   config:  ['f', 'Path to twemproxy config', 'path', '/etc/nutcracker/nutcracker.yml'],
>   command: ['c', 'Command to restart twemproxy', 'string', '/etc/init.d/nutcracker restart'],
>   log:           ['l', 'The log file location', 'string', '/var/log/twemproxy_sentinel.log']
```

* Redis-Twemproxy Agentを起動します。

```shell
$ sudo bin/redis_twemproxy_agent &
```
* ログを確認しておきます。

```log:/var/log/twemproxy_sentinel.log
[05:57:27] Loading TwemProxy config
[05:57:27] Redis Sentinel TwemProxy Agent Started on: Fri Sep 19 2014 05:57:27 GMT+0900 (JST)
[05:57:27] Subscribing to sentinel.
[05:57:28] Connection to Redis Sentinel established.
[05:57:28] Getting latest list of masters...
[05:57:28] Master received: mymaster 127.0.0.1:6379
[05:57:28] Updating Master mymaster to 127.0.0.1:6379
[05:57:28] Saving TwemProxy config
[05:57:28] TwemProxy restarted with output:
[05:57:28] Stopping nutcracker: [  OK  ]
Starting nutcracker: [  OK  ]
```

## 動作確認

* twemproxy経由でRedisへデータをsetします。

```shell
$ /usr/local/redis/bin/redis-cli set key value
OK
$ /usr/local/redis/bin/redis-cli get key
"value"
```

* 現在のレプリケーションのマスタを確認します。

```shell
$ /usr/local/redis/bin/redis-cli -p 26379 sentinel get-master-addr-by-name mymaster
1) "127.0.0.1"
2) "6379"
```

* レプリケーションのマスタを停止します。

```shell
$ sudo kill $(cat /var/run/redis.pid)
```
* Sentinelのログを確認します。

```shell
[27985] 19 Sep 06:12:36.929 # +sdown master mymaster 127.0.0.1 6379
[27985] 19 Sep 06:12:37.001 # +odown master mymaster 127.0.0.1 6379 #quorum 3/2
[27985] 19 Sep 06:12:37.028 # +new-epoch 10
[27985] 19 Sep 06:12:37.028 # +try-failover master mymaster 127.0.0.1 6379
[27985] 19 Sep 06:12:37.097 # +vote-for-leader 2041550a40318df885b3b27147b1d6fd86e87495 10
[27985] 19 Sep 06:12:37.112 # 127.0.0.1:26381 voted for b25b2fe16510fc2610e6b4d1adbe3b0765170708 10
[27985] 19 Sep 06:12:37.160 # 127.0.0.1:26380 voted for b25b2fe16510fc2610e6b4d1adbe3b0765170708 10
[27985] 19 Sep 06:12:37.972 # +config-update-from sentinel 127.0.0.1:26381 127.0.0.1 26381 @ mymaster 127.0.0.1 6379
[27985] 19 Sep 06:12:37.972 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6380
[27985] 19 Sep 06:12:37.972 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6380
[27985] 19 Sep 06:12:38.016 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
```

* レプリケーションのマスタを確認します。

```shell
$ /usr/local/redis/bin/redis-cli -p 26379 sentinel get-master-addr-by-name mymaster
1) "127.0.0.1"
2) "6380"

$ /usr/local/redis/bin/redis-cli -p 6380 info replication

# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6381,state=online,offset=74608,lag=0
master_repl_offset:74754
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:74753
```

* Redis-Twemproxy Agentのログも確認しておきます。

```log:/var/log/twemproxy_sentinel.log
[06:12:38] Received switch-master: { details:
   { 'master-name': 'mymaster',
     'old-ip': '127.0.0.1',
     'old-port': '6379',
     'new-ip': '127.0.0.1',
     'new-port': '6380' } }
[06:12:38] Updating Master mymaster to 127.0.0.1:6380
[06:12:38] Saving TwemProxy config
[06:12:38] TwemProxy restarted with output:
[06:12:38] Stopping nutcracker: [  OK  ]
Starting nutcracker: [  OK  ]
```
* twemproxy経由でRedisからデータを取得します。

```shell
$ /usr/local/redis/bin/redis-cli -p 22121 get key
"value"
```
* twemproxy経由でRedisへデータをsetします。

```shell
$ /usr/local/redis/bin/redis-cli -p 22121 set key2 value2
OK

$ /usr/local/redis/bin/redis-cli -p 22121 get key2
"value2"
```

## 参考

* [Redis-Twemproxy Agent](https://github.com/Stono/redis-twemproxy-agent)
* [REDIS-SENTINEL TWEMPROXY AGENT](http://www.jambr.co.uk/Article/redis-twemproxy-agent)
* [Redis Sentinel Documentation](http://redis.io/topics/sentinel)
* [npm](https://github.com/npm/npm)
* [第2回　パッケージ管理npmとアプリケーションの永続化](http://gihyo.jp/dev/serial/01/nodejs/0002)