Redisの冗長化

本稿では、Redis標準のReplicationおよびSentinel機能について解説します。

Redisの可用性を高めるための方法として、以下の3つの機能が標準で用意されています。

  • Replication: マスター・スレーブ型のレプリケーション
  • Sentinel: 死活監視と自動フェイルオーバーを行うサービス
  • Cluster: マルチマスター構成。データを複数サーバに分散できる(シャーディング)

その他の方法としてはPacemaker/Corosync、keepalived、HAProxy等を組み合わせて使うことができます。PacemakerにはRedisのリソースエージェントが同梱されています。

 

Replication

Redis は標準でマスター・スレーブ型のレプリケーションによる冗長構成をとることができます。Redis は非同期レプリケーションを採用しています。マスターは複数のスレーブを持つことができます。スレーブも読み込みクエリを受け付けることができるため、負荷分散目的でも使用することができます。

RedisのReplication構成

なお、 Replication 機能自体はマスター障害時の自動フェイルオーバー機能などは持っていないため、自動フェイルオーバーを行うには後述する Sentinel 、もしくは Pacemaker/Corosync、keepalived などの他のクラスタ管理ソフトウェアを使用する必要があります。

レプリケーションの設定

以下の構成でレプリケーションの設定を行います。

名前 ホスト名 IPアドレス ポート
Master server1 192.168.1.1 6379
Slave server2 192.168.1.2 6379

他ホストからの接続を受け付けるよう、マスターおよびスレーブの redis.conf を修正します。

# vi /etc/redis.conf
bind 127.0.0.1 -> bind 127.0.0.1 192.168.1.1 192.168.1.2

Redis をスレーブとして動作させるには、 redis.conf に slaveof <マスターのIPアドレス> <ポート番号> を追加し、 Redis を再起動します。

slaveof 192.168.1.1 6379

もしくは redis-cli で SLAVEOF コマンドを発行してもよいです。

127.0.0.1:6379> slaveof 192.168.1.1 6379

スレーブ側でredis-cli info replicationコマンドを実行し、スレーブとして動作していることを確認します。

server2# redis-cli info replication
# Replication
role:slave
master_host:192.168.1.1
master_port:6379
master_link_status:up
...

マスター側でも実行し、スレーブが認識されていることを確認します。

server1# redis-cli info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.1.2,port=6379,state=online,offset=294,lag=1
...

マスターでキーを登録し、スレーブに反映されていることを確認します。

Master> set repl-test 10000
OK

Slave> get repl-test
"10000"

Master> incr repl-test
(integer) 10001

Slave> get repl-test
"10001"

デフォルトでスレーブは read-only モードで動作します。参照は可能ですが書き込むことはできません。

Slave> incr repl-test
(error) READONLY You can't write against a read only replica.

Sentinel

Sentinel はマスターとスレーブの死活監視を行い、障害が発生した際に自動フェイルオーバーを行う機能です。
マスター・スレーブ構成の2台以上の Redis サーバに対し、 Sentinel 3台以上で管理を行います。Sentinel は Redis サーバと同居させても、別のノード(クライアント等)で動作させても構いません。Sentinel は多数決によりフェイルオーバーを行うかどうかを決定します。

RedisのSentinel構成

なお、 Sentinel 2台という構成はとることができません。これは片方の Sentinel がダウンした場合に残りの Sentinel が過半数(quorum)を獲得することができなくなるためです。

Sentinel の設定

前述の Replication 構成に Sentinel を追加します。
Sentinel は3つあればどこで動かしてもよいですが、今回はシンプルにするため1台のSlaveマシン上でポート番号を変えて3つのSentinelを動かします。実際に運用する場合は3台の別々のマシンで動作させてください。

名前 ホスト名 IPアドレス ポート
Master server1 192.168.1.1 6379
Slave server2 192.168.1.2 6379
Sentinel 1 server2 192.168.1.2 26379
Sentinel 2 server2 192.168.1.2 26380
Sentinel 3 server2 192.168.1.2 26381

Sentinel 用の設定ファイルを3台分コピーします。

# cd /etc/
# cp redis-sentinel.conf redis-sentinel-26379.conf
# cp redis-sentinel.conf redis-sentinel-26380.conf
# cp redis-sentinel.conf redis-sentinel-26381.conf

各設定ファイルについて、最低限以下の設定を行います。

protected-mode no
port <Sentinelのポート番号>
daemonize yes
pidfile /var/run/redis-sentinel-<Sentinelのポート番号>.pid
logfile /var/log/redis/sentinel-<Sentinelのポート番号>.log
sentinel monitor mymaster 192.168.1.1 6379 2

Sentinel を起動します。

# redis-sentinel /etc/redis-sentinel-26379.conf
# redis-sentinel /etc/redis-sentinel-26380.conf
# redis-sentinel /etc/redis-sentinel-26381.conf

ログファイル /var/log/redis/sentinel-<Sentinelのポート番号>.log に Master および他の Sentinel の情報が出力されていることを確認します。

2103:X 27 Jun 2019 15:56:54.718 # Sentinel ID is 5d34898e3b273c93a45aa80d6bcce659cb7c3b04
2103:X 27 Jun 2019 15:56:54.718 # +monitor master mymaster 192.168.1.1 6379 quorum 2
2073:X 27 Jun 2019 15:56:54.719 * +slave slave 192.168.1.2:6379 192.168.1.2 6379 @ mymaster 192.168.1.1 6379
2103:X 27 Jun 2019 15:57:18.722 * +sentinel sentinel 3132314ff9ab580fb8ed83e34a24d724e104d4ac 192.168.1.2 26380 @ mymaster 192.168.1.1 6379
2103:X 27 Jun 2019 15:57:37.034 * +sentinel sentinel bf5642a065425c23dcaff16cb749c7556379c317 192.168.1.2 26381 @ mymaster 192.168.1.1 6379

Master、Slave、Sentinelのプロセスが動作していることを確認します。

Master# ps auxww | grep redis
redis 1219 0.1 0.2 159584 4460 ? Ssl 11:14 0:31 /usr/bin/redis-server 0.0.0.0:6379

Slave# ps auxww | grep redis
redis 2052 0.2 1.6 167204 16428 ? Ssl 15:44 0:03 /usr/bin/redis-server 0.0.0.0:6379
root 2103 0.3 0.7 153892 7808 ? Ssl 15:56 0:01 redis-sentinel *:26379 [sentinel]
root 2111 0.3 0.7 153892 7816 ? Ssl 15:57 0:01 redis-sentinel *:26380 [sentinel]
root 2119 0.3 0.7 153892 7776 ? Ssl 15:57 0:01 redis-sentinel *:26381 [sentinel]

フェイルオーバー

Masterのredis-serverプロセスに障害を発生させます。

Master# killall redis-server
# ps auxww | grep redis

Sentinelのログを確認し、フェイルオーバーが行われたことを確認します。

Sentinelがノードの障害を検知するとSDOWN (Subjectively Down)状態に移行します。複数のSentinelがSDOWN状態となり、quorumの数を満たすとODOWN (Objectively Down)状態に移行し、フェイルオーバーが開始されます。フェイルオーバーを実行するSentinelは投票により決定されます。今回は 26381 番ポートの Sentinel がリーダーとして選出され、フェイルオーバーを実行しました。

2119:X 27 Jun 2019 16:08:25.571 # +sdown master mymaster 192.168.1.1 6379
2119:X 27 Jun 2019 16:08:25.662 # +odown master mymaster 192.168.1.1 6379 #quorum 2/2
2119:X 27 Jun 2019 16:08:25.662 # +new-epoch 1
2119:X 27 Jun 2019 16:08:25.663 # +try-failover master mymaster 192.168.1.1 6379
2119:X 27 Jun 2019 16:08:25.666 # +vote-for-leader bf5642a065425c23dcaff16cb749c7556379c317 1
2119:X 27 Jun 2019 16:08:25.670 # 5d34898e3b273c93a45aa80d6bcce659cb7c3b04 voted for bf5642a065425c23dcaff16cb749c7556379c317 1
2119:X 27 Jun 2019 16:08:25.671 # 3132314ff9ab580fb8ed83e34a24d724e104d4ac voted for bf5642a065425c23dcaff16cb749c7556379c317 1
2119:X 27 Jun 2019 16:08:25.733 # +elected-leader master mymaster 192.168.1.1 6379
2119:X 27 Jun 2019 16:08:25.733 # +failover-state-select-slave master mymaster 192.168.1.1 6379
2119:X 27 Jun 2019 16:08:25.800 # +selected-slave slave 192.168.1.2:6379 192.168.1.2 6379 @ mymaster 192.168.1.1 6379
2119:X 27 Jun 2019 16:08:25.800 * +failover-state-send-slaveof-noone slave 192.168.1.2:6379 192.168.1.2 6379 @ mymaster 192.168.1.1 6379
2119:X 27 Jun 2019 16:08:25.867 * +failover-state-wait-promotion slave 192.168.1.2:6379 192.168.1.2 6379 @ mymaster 192.168.1.1 6379
2119:X 27 Jun 2019 16:08:25.874 # +promoted-slave slave 192.168.1.2:6379 192.168.1.2 6379 @ mymaster 192.168.1.1 6379
2119:X 27 Jun 2019 16:08:25.874 # +failover-state-reconf-slaves master mymaster 192.168.1.1 6379
2119:X 27 Jun 2019 16:08:25.939 # +failover-end master mymaster 192.168.1.1 6379
2119:X 27 Jun 2019 16:08:25.939 # +switch-master mymaster 192.168.1.1 6379 192.168.1.2 6379
2119:X 27 Jun 2019 16:08:25.939 * +slave slave 192.168.1.1:6379 192.168.1.1 6379 @ mymaster 192.168.1.2 6379
2119:X 27 Jun 2019 16:08:55.994 # +sdown slave 192.168.1.1:6379 192.168.1.1 6379 @ mymaster 192.168.1.2 6379

Slaveとして動作していたRedisがマスターに昇格していることを確認します。

# redis-cli info replication
# Replication
role:master
connected_slaves:0

元Masterを再起動すると、自動的に設定がSlaveに変更され、再同期が行われます。

# systemctl start redis
# ps auxww | grep redis
redis 4926 0.5 0.2 156512 4156 ? Ssl 16:37 0:00 /usr/bin/redis-server 0.0.0.0:6379

# less /var/log/redis/redis.log

4926:S 27 Jun 2019 16:37:26.415 * Before turning into a replica, using my master parameters to synthesize a cached master: I may be able to synchronize with the new master with just a partial transfer.
4926:S 27 Jun 2019 16:37:26.416 * REPLICAOF 192.168.1.2:6379 enabled (user request from 'id=3 addr=192.168.1.2:46079 fd=8 name=sentinel-5d34898e-cmd age=10 idle=0 flags=x db=0 sub=0 psub=0 multi=3 qbuf=155 qbuf-free=32613 obl=36 oll=0 omem=0 events=r cmd=exec')
4926:S 27 Jun 2019 16:37:26.417 # CONFIG REWRITE executed with success.
4926:S 27 Jun 2019 16:37:27.110 * Connecting to MASTER 192.168.1.2:6379
4926:S 27 Jun 2019 16:37:27.110 * MASTER <-> REPLICA sync started
4926:S 27 Jun 2019 16:37:27.110 * Non blocking connect for SYNC fired the event.
4926:S 27 Jun 2019 16:37:27.111 * Master replied to PING, replication can continue...
4926:S 27 Jun 2019 16:37:27.111 * Trying a partial resynchronization (request 428e1658f7baea92e543dce299ee9c4769e37729:1).
4926:S 27 Jun 2019 16:37:27.140 * Full resync from master: e006bbaf56f0298b1f022d037290ba9a65a75850:517729
4926:S 27 Jun 2019 16:37:27.141 * Discarding previously cached master state.
4926:S 27 Jun 2019 16:37:27.187 * MASTER <-> REPLICA sync: receiving 14785 bytes from master
4926:S 27 Jun 2019 16:37:27.187 * MASTER <-> REPLICA sync: Flushing old data
4926:S 27 Jun 2019 16:37:27.188 * MASTER <-> REPLICA sync: Loading DB in memory
4926:S 27 Jun 2019 16:37:27.188 * MASTER <-> REPLICA sync: Finished with success

元Masterがスレーブとして動作していることを確認します。

# redis-cli info replication
# Replication
role:slave
master_host:192.168.1.2
master_port:6379
master_link_status:up

現Slave(元Master)の redis.conf も自動的に書き換えられています。

# cat /etc/redis.conf
...
# Generated by CONFIG REWRITE
replicaof 192.168.1.2 6379

仮想IPの利用

Sentinelは自動でフェイルオーバーを行ってくれますが、標準では仮想IPを付与するような機能はありませんので、クライアントは複数のサーバのIPアドレスと、どちらがMasterであるかを把握している必要があります。
常にMasterに固定の仮想IPを割り振りたい場合は以下の方法があります。

フェイルオーバー時のスクリプトの呼び出し

Sentinelに以下の設定を行うとフェイルオーバー時にスクリプトを呼び出すことができます。スクリプトは各Sentinelサーバ上で実行されますので、SentinelをMasterサーバおよびSlaveサーバと同居させておく必要があります。

sentinel client-reconfig-script mymaster /var/lib/redis/redis-vip.sh

スクリプトに渡される引数は以下になります。今回はフェイルオーバー後のMasterのIPアドレス <to-ip> のみ使用します。

<master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>

MasterおよびSlaveに /var/lib/redis/redis-vip.sh として以下のスクリプトを配置します。自ホストのIP等は各サーバに合わせます。それ以外のSentinelサーバではclient-reconfig-scriptの設定を無効にしておくか、ダミーのスクリプトを配置しておきます。

#!/bin/sh

MASTER_IP=${6}
MY_IP='<自ホストのIP>'
VIP='<仮想IP>'
NETMASK='<仮想IPのネットマスク>'
INTERFACE='<仮想IPを付与するNIC>'

if [ ${MASTER_IP} = ${MY_IP} ]; then
        /sbin/ip addr add ${VIP}/${NETMASK} dev ${INTERFACE}
        /sbin/arping -q -c 3 -A ${VIP} -I ${INTERFACE}
        exit 0
else
        /sbin/ip addr del ${VIP}/${NETMASK} dev ${INTERFACE}
        exit 0
fi
exit 1

なお、Redis起動時には上記スクリプトは実行されないので、初回のみ手動で実行してMasterに仮想IPを付与する必要があります。

server1# /var/lib/redis/redis-vip.sh mymaster 0 0 0 0 <MasterのIPアドレス> 6379

server1# ip -4 a show dev ens192
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    inet 192.168.1.1/24 brd 192.168.1.255 scope global dynamic ens192
       valid_lft 9311sec preferred_lft 9311sec
    inet <仮想IP> scope global secondary ens192
       valid_lft forever preferred_lft forever

server2# ip -4 a show dev ens192
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    inet 192.168.1.2/24 brd 192.168.1.255 scope global dynamic ens192
       valid_lft 11155sec preferred_lft 11155sec

Master (server1)のredis-serverプロセスを停止し、フェイルオーバーを発生させてみます。

server1# killall redis-server

元Slaveのserver2がMasterに昇格し、仮想IPが移動していることを確認します。

server1# ip -4 a show dev ens192
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    inet 192.168.1.1/24 brd 192.168.1.255 scope global dynamic ens192
       valid_lft 9311sec preferred_lft 9311sec

server2# redis-cli info replication
# Replication
role:master
connected_slaves:0

server2# ip -4 a show dev ens192
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    inet 192.168.1.2/24 brd 192.168.1.255 scope global dynamic ens192
       valid_lft 11155sec preferred_lft 11155sec
    inet <仮想IP> scope global secondary ens192
       valid_lft forever preferred_lft forever

おわりに

今回は、Redisの標準機能を用いてRedisの冗長化を行う方法について解説しました。Redisは標準機能のみで非常に柔軟に分散・冗長構成をとることができ、ニーズにあわせた様々な構成が考えられます。

次回はRedis Cluster機能について紹介します。