コピペコードで快適生活

明日使えるソースを自分のために

Redisへの負荷を分散するためにtwemproxyを使ってシャーディングする

概要

Redis負荷分散のために前段にプロキシ(twemproxy)を配置/運用した内容について記載します。
twemproxyはコンシステントハッシュを使用してシャーディング(キーの分散)を実現します。
なお、twemproxy詳細については https://github.com/twitter/twemproxy を参照してください。

なぜ必要

"書き込みも含めた"Redisへの負荷を分散するため。
読み込みだけの負荷を分散するのであればリードレプリカがあれば大丈夫。

システム構成

Railsアプリ - NLB - ターゲットグループ(blue/green) - EC2(tewmproxy)×N台 - Redis×N台

※ターゲットグループ/EC2はblue系とgreen系を準備して、blue/greenデプロイが可能な構成としています。

構築方法

twemproxyをAmazonLinuxにインストールする

ansible-roleを作成したので、こちらを使ってください。
https://github.com/kinosuke01/ansible-twemproxy

twemproxyの設定概要(ansibleでの設定)
twemproxy_pools:
  redis:
    listen: 127.0.0.1:6379       # listenするアドレス:ポート
    hash: fnv1a_64
    distribution: ketama
    auto_eject_hosts: "true"     # redisが死んだときに切り離す(true)か否(false)か
    redis: "true"                # バックエンドにredisを使う(falseだとmemcached使う)
    server_retry_timeout: 30000  # 下記「redisの切り離しについて」参照
    server_failure_limit: 1      # 下記「redisの切り離しについて」参照
    servers:                     # バックエンドのredisアドレスを配列で記載
      - redis01.example.com:6379:1
      - redis02.example.com:6379:1
ファイルディスクリプタの上限設定(ansibleでの設定)

ファイルディスクリプタのデフォルト上限1024では足りないほどに
twemproxyへの同時アクセス数がある場合は、ansibleで下記変数を設定します。
twemproxyの起動スクリプト内でこの数値で `ulmit -n value` されるようになります。

twemproxy_file_max: 500000
ヘルスチェックについて

twemproxyのmonitoring portである22222ポートを使用する。

運用について

Redisのつけ足しについて

バックエンドのredisを付け足ししたい場合も、blue/greenデプロイすることで無停止で付け足しが可能。
以下、手順を記載します。(前提としてblueがアクティブな状態とします。)

1) Redisを新たに構築。
2) 新たに構築したredisのエンドポイントをgreen系の設定ファイルに追加。twemproxyを再起動。
(ansibleでblue系とgreen系の設定を分けておくと安全)
3) nlbでターゲットグループをblueからgreenに切り替える
4) grenn系がhealthyになったことを確認したら、blue系のEC2にログインしてtwemproxyをstopもしくはrestartする。
(これをしないとblueにも通信が流れ続ける。TCPセッションが切れないため?詳細は不明)

Redisの切り離しについて

server_retry_timeout: 30000 とした場合、
1) キーにヒットするRedisが死んだ。
2) 30000msec以内に同じキーでアクセスする
3) リング状の次のRedisで read or writeが行われる。
4) 次のRedisが使われるようになってから30000msec経つと、またもともとのRedisを参照するようになる。(で死んでいたらまたリング状の次のRedisが使われる)
という動きになる。

Redisクライアント側の対応

・Redisのつけ足しについて/Redisの切り離しについての挙動があるため、ttl期限なしは設定しない。
 Redis増やしたり切り離されたりしたときに、内容が古い消えていないキャッシュにヒットしてしまう可能性があるため。

・すべてのRedisコマンドが使えないので、クライアント側でそれらの使用を控えるか何らかの対策をする必要がある。
 https://github.com/twitter/twemproxy/blob/master/notes/redis.md
 大きな点でいうとkeys,flushdbが使えない。
 keysが使えないのでキー部分一致による削除ができない。