コピペコードで快適生活

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

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が使えないのでキー部分一致による削除ができない。

SublimeTextで設定したことメモ

随時追加

¥(円)マークで\バックスラッシュを入力

Sublime Text 2 -> Preferences -> Keybindings - User

でキーマップの設定画面を開き、下記の行を追加する。

{ "keys": ["\u00a5"], "command": "insert", "args": {"characters": "\\"}}

参考にさせていただきました。
Sublime Text2 ¥(円)マークで\バックスラッシュを入力 – 黒川仁の文具堂ブログ三昧

ファイルディスクリプタの上限変更

確認

$ cat /proc/sys/fs/file-nr
576	0	1636820

file-nr内のパラメータはそれぞれ、

  • オープンされているファイル数
  • 空きファイル管理データの数
  • システム中のオープンファイル管理データの最大数

を示します。

一時的な設定

ログインプロセスとその子プロセスでのみ有効。

# 確認
$ ulimit -n
1024

# 変更
$ ulimit -n 2048

恒久的な設定

システム全体
# 確認
cat /proc/sys/fs/file-nr

$ sudo vi /etc/sysctl.conf
# fs.file-max = 1232457
# を追記する

# 設定を反映
sudo /sbin/sysctl -p

# 確認
cat /proc/sys/fs/file-nr
ユーザごと

nodoc

daemonプロセス

limits.confに設定した値は、ログインしないdameonプロセスには効かないので、
起動スクリプト(/etc/init.d/*)に

ulimit -n 123456

を追記して対応する。

当該プロセスのファイルディスクリプタ上限の確認は下記でわかる。

cat /proc/#{pid}/limits
Limit                     Soft Limit           Hard Limit           Units
# --- 略 ---
Max open files            500000               500000               files
# --- 略 ---

該当プロセスが開いているファイル数はこれで確認できる

ls /proc/#{pid}/fd | wc -l

削除済のファイルを掴んでいるのもわかる

ls -la /proc/#{pid}/fd
# 出てきたリストに (deleted) と表示されていれば、削除済ファイルを掴んでいる

参考にさせていただきました。
@IT:/procによるLinuxチューニング [後編](2/4)

http://mikio.github.io/article/2013/03/02_.html

デーモンごとにulimitで設定した値を確認する - foxtrot0304's diary

AmazonLinuxでタイムゾーン設定メモ

どうするんだっけとなったときのメモ。
Linux インスタンスの時刻の設定 - Amazon Elastic Compute Cloud

sudo vi /etc/sysconfig/clock # 下記編集する
# ----------
# ZONE="UTC" を消して↓とする
ZONE="Asia/Tokyo" 
# ----------

# シンボリックリンクはる
sudo ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

# リブートする
sudo reboot

pyenvとpyenv-virtualenvでPython環境を作る

Pythonインストール
# cloneする
git clone https://github.com/yyuu/pyenv.git ~/.pyenv

# 設定追加
vim  ~/.bash_profile
===
export PYENV_ROOT=$HOME/.pyenv
export PATH=$PYENV_ROOT/bin:$PATH
eval "$(pyenv init -)"
===
# 上記設定の再読み込み
exec $SHELL -l

# バージョン指定してインストール
pyenv install 2.7

# インストール済のバージョン確認
pyenv versions

# デフォルト設定
pyenv global 2.7
prenv global system # で戻る

# カレントディレクトリでの設定
pyenv local 2.7 
pyenv-virtualenvの設定(gemsetみたいなやつ)
git clone https://github.com/yyuu/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv

vim ~/.bash_profile
===
eval "$(pyenv virtualenv-init -)"
===
# 上記設定の再読み込み
exec $SHELL -l

# virtualenv作る
pyenv virtualenv 2.7 2.7_ansible

# 確認(versionと同じように扱える)
pyenv versions
pyenv uninstall 2.7-ansible
pyenv global 2.7_ansible

# あとはお好きにpip install
pip install boto3
pip install botocore
pip install boto
pip install ansible