Redisでレプリケーションを設定する

# TL;DR

* Redisは、データのレプリケーションが可能
* 永続化を有効にしていない場合、マスターの自動再起動設定を行わないこと
* マスターのフェイルオーバーを行いたい場合は、Redis ClusterやRedis Sentinelを検討すること


# Redisのレプリケーション

文字通り、Redisにマスター、レプリカの役割を持たせることができます。

ドキュメントは、こちらです。

[Replication](https://redis.io/topics/replication)

いくつか、ポイントが書かれています。

まずは、動作について。

* マスター・レプリカ間が接続できている場合、マスターからレプリカへデータの変更（書き込み、有効期限切れなど）の更新コマンドを送信する
* ネットワーク的に、マスターとレプリカが分断されても、復旧して接続できればレプリカは部分的な同期を試みる
  * 部分的な同期が不可能な場合、レプリカは完全な再同期が必要となり、マスターにデータのスナップショットの作成と送信、以後の継続的な更新イベントの送信を要求する

マスター障害時のフェイルオーバーなど、いわゆるHA的なことはできません。こちらが必要な場合は、Redis ClusterまたはRedis Sentinelを検討することになります。

その他、レプリケーションの特徴や注意点はこんな感じです。

* 非同期で動作する
* マスターに対して、レプリカは複数存在することができる
* レプリケーションは、マスター、レプリカそれぞれでノンブロッキングで動作する
    * レプリケーション中に、マスターは継続してクエリを処理可能
    * レプリカ側で、大きなデータセットをロードする必要が出てきた場合は、ブロックが発生するパターンがある
* マスターが再起動した時に、空のデータセットで処理を受け付けられるようになった場合、空のデータセットがレプリカへ同期され、レプリカ側も空になることになるので注意

とりあえず、こんなところまで調べてみたので、実際に動かしてみましょう。


# 環境

今回の確認環境。

```shell
$ cat /etc/redhat-release 
CentOS Linux release 7.6.1810 (Core)


$ uname -a
Linux localhost.localdomain 3.10.0-957.12.2.el7.x86_64 #1 SMP Tue May 14 21:24:32 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux


$ redis-server -v
Redis server v=5.0.5 sha=00000000:0 malloc=jemalloc-5.1.0 bits=64 build=619d60bfb0a92c36
```

サーバーは2台用意し、以下とします。

* マスター … `192.168.33.10`
* レプリカ … `192.168.33.11`

# CentOSへRedisをインストールする

Remi Repositoryから、可能な限り新しいRedisをインストールします。

```shell
$ sudo yum install epel-release
$ sudo yum install https://rpms.remirepo.net/enterprise/remi-release-7.rpm
$ sudo yum --enablerepo=remi install redis
```


# マスターの設定

`/etc/redis.conf`を修正。

今回は、まずはバインドするIPアドレスのみ修正します。

```shell
bind 0.0.0.0
```

ちなみに、Remi RepositoryからインストールしたRedis 5.0.5のデフォルト設定はこんな感じでした。

```shell
$ sudo grep -v '#' /etc/redis.conf | grep -v '^$'
bind 127.0.0.1
protected-mode yes
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize no
supervised no
pidfile /var/run/redis_6379.pid
loglevel notice
logfile /var/log/redis/redis.log
databases 16
always-show-logo yes
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /var/lib/redis
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
replica-priority 100
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
```

Redis起動。

```shell
$ sudo systemctl start redis
```

接続。

```shell
$ redis-cli -h 192.168.33.10
```

データを入れて、確認。

```shell
192.168.33.10:6379> set key1 value1
OK

192.168.33.10:6379> set key2 value2
OK

192.168.33.10:6379> keys *
1) "key1"
2) "key2"
```


# レプリカを設定する

レプリカ側では、`/etc/redis.conf`に`bind`以外に`replicaof`を指定します。

```shell
replicaof 192.168.33.10 6379
```

`replicaof`に指定するのは、マスターのIPアドレス（またはホスト名）とポートです。

レプリカ側のRedis起動。

```shell
$ sudo systemctl start redis
```

接続。

```shell
$ redis-cli -h 192.168.33.11
```

すでにデータが入っています。

```shell
192.168.33.11:6379> get key1
"value1"
192.168.33.11:6379> get key2
"value2"
```

Remi RepositoryからインストールしたRedisでは、デフォルト設定で`replica-read-only`が`yes`になっているのでレプリカ側で書き込みを行うことができません。

```shell
192.168.33.11:6379> set key3 value3
(error) READONLY You can't write against a read only replica.
```

レプリケーションの情報を見るには、`info replication`で。

マスター側で実行した場合。

```shell
$ redis-cli -h 192.168.33.10
192.168.33.10:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.33.11,port=6379,state=online,offset=135,lag=1
master_replid:0bff47e7ae6b3513e56f6f7f1b571fdc8b668418
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:135
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:135
```

レプリカ側で実行した場合。

```shell
$ redis-cli -h 192.168.33.11 
192.168.33.11:6379> info replication
# Replication
role:slave
master_host:192.168.33.10
master_port:6379
master_link_status:up
master_last_io_seconds_ago:3
master_sync_in_progress:0
slave_repl_offset:135
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:0bff47e7ae6b3513e56f6f7f1b571fdc8b668418
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:135
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:135
```


# 永続化をオフにして、マスターを再起動してみる

試しに、設定ファイルをこれくらいシンプルにしてRDBファイルは1度削除。

マスター側。

```shell
bind 0.0.0.0
pidfile /var/run/redis_6379.pid
loglevel notice
logfile /var/log/redis/redis.log
dbfilename dump.rdb
dir /var/lib/redis
```

レプリカ側。

```shell
bind 0.0.0.0
pidfile /var/run/redis_6379.pid
loglevel notice
logfile /var/log/redis/redis.log
dbfilename dump.rdb
dir /var/lib/redis
replica-read-only yes
replicaof 192.168.33.10 6379
```

マスターにデータを入れます。

```shell
$ redis-cli -h 192.168.33.10
192.168.33.10:6379> keys *
(empty list or set)
192.168.33.10:6379> set key1 value1
OK
192.168.33.10:6379> set key2 value2
OK
```

レプリカを確認します。

```shell
$ redis-cli -h 192.168.33.11
192.168.33.11:6379> keys *
1) "key2"
2) "key1"
```

マスターを再起動します。

```shell
$ sudo systemctl stop redis
$ sudo systemctl start redis
```

マスターを確認します。永続化していないので、データがなくなりました。

```shell
$ redis-cli -h 192.168.33.10
192.168.33.10:6379> keys *
(empty list or set)
```

レプリカを確認します。

```shell
$ redis-cli -h 192.168.33.11
192.168.33.11:6379> keys *
(empty list or set)
```

こちらも、キレイになくなりましたね。

ところで、設定にRDBファイルを定義しているのは、

```shell
dbfilename dump.rdb
dir /var/lib/redis
```

データのフル転送が必要な時に、一時的にダンプファイルを作成する必要があるからみたいです。この設定を外すと、完全な同期が必要になった際にスナップショットが作れなくてエラーになるみたいです。

```shell
Opening the temp file needed for MASTER <-> REPLICA synchronization: Permission denied
```

データの永続化自体は、元のファイルにあった以下を削除したので、定期的な保存を行わなくなっています。

```shell
save 900 1
save 300 10
save 60 10000
```

[Redis 5.0 Configuration](https://raw.githubusercontent.com/antirez/redis/5.0/redis.conf)

この場合は、Redisを再起動してもデータはなくなりません。

永続化設定と、再起動設定には気をつけましょうね、と。