Redis Sentinel を VLAN で構成する (Akamai)

# Redis Sentinel on Akamai
Akamai のクラウドコンピューティング・サービスが提供するマーケットプレイスから、Redis Sentinel のクラスタ構成を簡単に作成することができます。HAProxy を配置することで、アプリケーションから常にマスターサーバーに接続して read/write することもできます。詳細については下記の記事を参照ください。

https://qiita.com/110hideki/items/e0ea495ab616b6584da8

上の記事では Private IP を使ってネットワークを構成していますが、本記事は VLAN で構成する方法について記載します。まず、Private IP で構成されるネットワークを上記の記事を参考に作成します。作成されたネットワークは以下のようになります。

![Redis-privateip.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264188/cae0084f-82d1-3f79-2d02-09616f50dd5c.jpeg)


# VLAN
Private IP を使った先程の構成から VLAN 構成に変更したものが以下の図となります。本記事ではこの構成に移行する手順を記載します。

![redis-vlan-client.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264188/eec0eb82-2a68-6182-6e2d-b7ed341c9c11.jpeg)


HAProxy は冗長構成にすべきですが、本記事では解説をシンプルにするため一台構成で進めます。

進め方の大きな流れは下記のようになります。
1. Marketplace から Redis Sentinel をインストールする
2. HAProxy、VLAN のルーター、Redis Client に相当する Linode ノードを構築します
3. Redis Sentinel の各ノード、HAProxy、 ルーター、Redis Client の全てのノードに VLAN の IP アドレスを割り当てます。
4. Redis Sentinel の各ノードと HAProxy から Public IP、Private IP の割当を削除します。
5. Redis Client から HAProxy 経由で常に Redis のマスターサーバーにアクセスできることを確認します。

この大きな流れの中にいくつかチェックポイントを設け、正しく動作しているかを確認します。



# 共通ホスト設定
最初に VLAN の IP を決めます。VLAN については下記の記事を参考にしてください。

https://qiita.com/110hideki/items/aead6f2bc9ff1563641c

全体的に実施することは単純で Private IP を VLAN の IP に変更することです。最初に次のようにマップするテーブルを用意すると楽になります。


| Hostname  | Private IP| Hostname (VLAN) | VLAN IP |
|:-:|:-:|:-:|:-:|
| redis_1  | 192.168.XXX.XX1  | redis-1v  |  10.0.0.11 |
| redis_2  | 192.168.XXX.XX2  | redis-2v  |  10.0.0.12 |
| redis_3  | 192.168.XXX.XX3  | redis-3v  |  10.0.0.13 |
| router  |   | vlan-router |  10.0.0.1 |
| haproxy  |   | vlan-haproxy |  10.0.0.21 |
| Redis Client | | vlan-redisclient | 10.0.0.22|

最終的に Public IP を持つのは、Redis Client と Router となります。要件によって、適切に定義してください。Router は VLAN の中から外に通信するときに必要になります。詳細は以下の記事を参照してください。

https://qiita.com/110hideki/items/851291f0d33acc577a82

## /etc/hosts
```
10.0.0.11 redis-1v #Redis Sentinel 1
10.0.0.12 redis-2v # Redis Sentinel 2
10.0.0.13 redis-3v # Redis Sentinel 3
10.0.0.1 vlan-router
10.0.0.21 vlan-haproxy
10.0.0.22 vlan-redisclient
```

これから、それぞれのホストでの作業を行います。Redis Sentinel の３つのノードでは ``redis.conf``、``sentinel.conf``、``firewalld`` に関する設定を VLAN に変更します。

``redis.conf`` は Redis Replication に関する IP 設定を変更します。既存の Private IP で構成されている記述を VLAN に変更します。

``sentinel.conf`` は Redis Sentinel に関する IP 設定を変更します。既存の Private IP で構成されている記述を VLAN に変更します。

``firewalld`` プロセスが動作している構成に VLAN の IP アドレスを追記します。VLAN を構成した後は、``firewalld`` を動かす必要性は薄れますので、状況によってはプロセスを停止して確認してください。

この他にも、eth デバイスで VLAN を有効にするためにはシステムのネットワーク設定の変更も必須となります。

ルーターとなるホストは IP Forwarding の設定をします。設定は以下の記事を参照します。

https://qiita.com/110hideki/items/851291f0d33acc577a82

# Redis Server のプロセス停止
まず、Redis Server と Sentinel のプロセスを止めます。順番は次のようにします。

1. レプリカサーバーの Sentinel を止める。複数台あれば、その順番は問いません。
2. マスターサーバーの Sentinel を止める
3. レプリカサーバーを止める。複数台あれば、その順番は問いません。
4. マスターサーバーを止める

Sentinel を先に止めることによって、意図的にマスターとレプリカの変更をしないようにします。

``redis_1`` がマスターサーバー、``redis_2``と``redis_3`` がレプリカサーバーである場合は次の順番で作業します。

```
redis_2# systemctl stop redis-sentinel
```

```
redis_3# systemctl stop redis-sentinel
```

```
redis_1# systemctl stop redis-sentinel
```

```
redis_2# systemctl stop redis-server
```

```
redis_3# systemctl stop redis-server
```

```
redis_1# systemctl stop redis-server
```

# redis-1v
``redis_1`` がマスターサーバーとして動作していることを想定しています。すでにサーバー群は停止しているので、``redis-1v``、``redis-2v``、``redis-3v`` の作業の順番は問いません。
## /etc/redis/redis.conf

```
#bind 127.0.0.1 ::1 192.168.XXX.XX1
bind 127.0.0.1 ::1 10.0.0.11
```
``bind`` の記述は Redis Server がクライアントから listen するための自分自身の IP アドレスを登録します。Private IP の記述を VLAN の IP に変更します。

## /etc/redis/sentinel.conf

```
#bind 127.0.0.1 ::1 192.168.XXX.XX1
bind 127.0.0.1 ::1 10.0.0.11

#sentinel monitor mymaster 192.168.XXX.XX1 6379 2
sentinel monitor mymaster 10.0.0.11 6379 2

#sentinel known-replica mymaster 192.168.XXX.XX3 6379
sentinel known-replica mymaster 10.0.0.13 6379

#sentinel known-replica mymaster 192.168.XXX.XX2 6379
sentinel known-replica mymaster 10.0.0.12 6379

#sentinel known-sentinel mymaster 192.168.XXX.XX2 26379 f80d00523b7831d8362ad5cf5c60f0975057b10f
sentinel known-sentinel mymaster 10.0.0.12 26379 f80d00523b7831d8362ad5cf5c60f0975057b10f

#sentinel known-sentinel mymaster 192.168.XXX.XX3 26379 83e7fea9eab2635981d02d5ad62142653b309e08
sentinel known-sentinel mymaster 10.0.0.13 26379 83e7fea9eab2635981d02d5ad62142653b309e08
```




# redis-2v
``redis-2v``は一つのレプリカサーバーであることを想定しています。
## /etc/redis/redis.conf
```
#bind 127.0.0.1 ::1 192.168.XXX.XX2
bind 127.0.0.1 ::1 10.0.0.12

#replicaof 192.168.XXX.XX1 6379
replicaof 10.0.0.11 6379
```

## /etc/redis/sentinel.conf

```
#bind 127.0.0.1 ::1 192.168.XXX.XX2
bind 127.0.0.1 ::1 10.0.0.12

#sentinel monitor mymaster 192.168.XXX.XX1 6379 2
sentinel monitor mymaster 10.0.0.11 6379 2

#sentinel known-replica mymaster 192.168.XXX.XX3 6379
sentinel known-replica mymaster 10.0.0.13 6379

#sentinel known-replica mymaster 192.168.XXX.XX2 6379
sentinel known-replica mymaster 10.0.0.12 6379

#sentinel known-sentinel mymaster 192.168.XXX.XX1 26379 662bdc406dfb2ce3fb31d2e7b5ca0eece38910b7
sentinel known-sentinel mymaster 10.0.0.11 26379 662bdc406dfb2ce3fb31d2e7b5ca0eece38910b7

#sentinel known-sentinel mymaster 192.168.XXX.XX3 26379 83e7fea9eab2635981d02d5ad62142653b309e08
sentinel known-sentinel mymaster 10.0.0.13 26379 83e7fea9eab2635981d02d5ad62142653b309e08
```

# redis-3v
``redis-3v``はもう一つのレプリカサーバーであることを想定しています。
## /etc/redis/redis.conf
```
#bind 127.0.0.1 ::1 192.168.XXX.XX3
bind 127.0.0.1 ::1 10.0.0.13
```

```
# Generated by CONFIG REWRITE
#replicaof 192.168.XXX.XX1 6379
replicaof 10.0.0.11 6379
```

## /etc/redis/sentinel.conf

```
#bind 127.0.0.1 ::1 192.168.XXX.XX3
bind 127.0.0.1 ::1 10.0.0.13

#sentinel monitor mymaster 192.168.XXX.XX1 6379 2
sentinel monitor mymaster 10.0.0.11 6379 2

#sentinel known-replica mymaster 192.168.XXX.XX3 6379
sentinel known-replica mymaster 10.0.0.13 6379

#sentinel known-replica mymaster 192.168.XXX.XX2 6379
sentinel known-replica mymaster 10.0.0.12 6379

#sentinel known-sentinel mymaster 192.168.XXX.XX1 26379 662bdc406dfb2ce3fb31d2e7b5ca0eece38910b7
sentinel known-sentinel mymaster 10.0.0.11 26379 662bdc406dfb2ce3fb31d2e7b5ca0eece38910b7

#sentinel known-sentinel mymaster 192.168.XXX.XX2 26379 f80d00523b7831d8362ad5cf5c60f0975057b10f
sentinel known-sentinel mymaster 10.0.0.12 26379 f80d00523b7831d8362ad5cf5c60f0975057b10f
```

# Firewalld
## /etc/firewalld/zones/internal.xml
``firewalld`` の設定です。マーケットプレイスからプロビジョニングしたときは、Redis サーバーの Private IP 以外からはアクセスできないように設定されています。以下の行を追加します。
```
  <source address="10.0.0.11"/>
  <source address="10.0.0.12"/>
  <source address="10.0.0.13"/>
  <source address="10.0.0.21"/>
  <source address="10.0.0.1"/>
  <source address="10.0.0.22"/>
```
``source address`` 以外の設定はそのまま残しておいてください。最終的には Private IP を削除します。

設定を反映します。
```
firewall-cmd --reload
```
設定を確認します。
```
firewall-cmd --get-active-zone
```

# Redis-server の再起動
まず、Redis Server と Sentinel のプロセスを起動します。順番は次のようにします。

1. マスターサーバーを起動する
2. レプリカサーバーを起動する。複数台あれば、その順番は問いません。
3. マスターサーバーの Sentinel を起動する
4. レプリカサーバーの Sentinel を起動する。複数台あれば、その順番は問いません。

``redis_1`` がマスターサーバー、``redis_2``と``redis_3`` がレプリカサーバーである場合は次の順番で作業します。
## redis-1v
```
redis_1:/etc/redis# systemctl start redis-server
```

```
redis_1:/etc/redis# redis-cli --tls --cacert /etc/redis/tls/ca.crt ping
PONG
redis_1:/etc/redis# redis-cli --tls --cacert /etc/redis/tls/ca.crt role
1) "master"
2) (integer) 258540423
3) (empty array)
```

## redis-2v

```
redis_2:/etc/redis# systemctl start redis-server
```

```
redis_2:/etc/redis# redis-cli --tls --cacert /etc/redis/tls/ca.crt ping
PONG
redis_2:/etc/redis# redis-cli --tls --cacert /etc/redis/tls/ca.crt role
1) "slave"
2) "10.0.0.11"
3) (integer) 6379
4) "connected"
5) (integer) 258540493
```

## redis-3v

```
redis_3:/etc/redis# systemctl start redis-server
```

```
redis_3:/etc/redis# redis-cli --tls --cacert /etc/redis/tls/ca.crt ping
PONG
redis_3:/etc/redis# redis-cli --tls --cacert /etc/redis/tls/ca.crt role
1) "slave"
2) "10.0.0.11"
3) (integer) 6379
4) "connected"
5) (integer) 258540619
```

# HAproxy ホストからの確認

```
# redis-cli  -h redis-1v --tls --cacert redis-nj_ca.crt role
1) "master"
2) (integer) 258541333
3) 1) 1) "10.0.0.12"
      2) "6379"
      3) "258541319"
   2) 1) "10.0.0.13"
      2) "6379"
      3) "258541319"
```
```
# redis-cli  -h redis-2v --tls --cacert redis-nj_ca.crt role
1) "slave"
2) "10.0.0.11"
3) (integer) 6379
4) "connected"
5) (integer) 258541333
```
```
# redis-cli  -h redis-3v --tls --cacert redis-nj_ca.crt role
1) "slave"
2) "10.0.0.11"
3) (integer) 6379
4) "connected"
5) (integer) 258541347
```

# Redis-sentinel の起動

```
redis_1# systemctl start redis-sentinel
```

```
redis_2# systemctl start redis-sentinel
```

```
redis_3# systemctl start redis-sentinel
```

# HAproxy ホストからの接続

```
haproxy# redis-cli -h vlan-haproxy --tls --cacert /etc/haproxy/tls/redis-nj_ca.crt ping
PONG
haproxy# redis-cli -h vlan-haproxy --tls --cacert /etc/haproxy/tls/redis-nj_ca.crt role
1) "master"
2) (integer) 259155340
3) 1) 1) "10.0.0.12"
      2) "6379"
      3) "259155340"
   2) 1) "10.0.0.13"
      2) "6379"
      3) "259155207"
```

# Client からの接続

```
haproxy:~# ip -4 a show dev eth1
4: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    inet 10.0.0.21/24 brd 10.0.0.255 scope global eth1
       valid_lft forever preferred_lft forever
```
```
redisclient:~# redis-cli -h vlan-haproxy --tls --cacert redis-nj_ca.crt ping
PONG
redisclient:~# redis-cli -h vlan-haproxy --tls --cacert redis-nj_ca.crt role
1) "master"
2) (integer) 275843158
3) 1) 1) "10.0.0.12"
      2) "6379"
      3) "275843158"
   2) 1) "10.0.0.13"
      2) "6379"
      3) "275843144"
```

# Redis の Public IP を削除し、VLAN Only にする

## redis-1v
### before
![redis-1v-network-before.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264188/93f49f2a-c164-2b7f-e092-362b3b484303.jpeg)
### after
![redis-1v-network-after.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264188/fcdcc764-cbab-141f-39b7-561dff5316da.jpeg)

### /etc/systemd/network/05-eth0.network
Linode の [Network Helper](https://www.linode.com/docs/products/compute/compute-instances/guides/network-helper/) は様々な OS のネットワーク設定を自動で最適化してくれる便利な機能ですが、VLAN を利用するときは無効化することが推奨されています。Netowork Helper を無効化し、ネットワークの設定は以下のようにします。
```

[Match]
Name=eth0

[Network]
DHCP=no

Domains=members.linode.com
IPv6PrivacyExtensions=false

Address=10.0.0.11/24
Gateway=10.0.0.1
```

### ルーティング設定
ping が動作しない場合、ルーティングを確認してください。
```
redis_1:/etc/systemd/network# ip r
default via 10.0.0.1 dev eth0 proto static
10.0.0.0/24 dev eth0 proto kernel scope link src 10.0.0.11
```

ping も動作します。
```
redis_1:/etc/systemd/network# ping linode.com
PING linode.com (69.164.200.202) 56(84) bytes of data.
64 bytes from www-loadbal2.linode.com (69.164.200.202): icmp_seq=1 ttl=48 time=50.0 ms
64 bytes from www-loadbal2.linode.com (69.164.200.202): icmp_seq=2 ttl=48 time=46.8 ms
```

DNS に問題があるようでしたら、```/etc/systemd/resolved.conf``` の ``DNS`` を確認してください。

## redis-2v
Cloud Console 上の Configuration > Network 設定を確認してください。``redis-1v`` 同様、``05-eth0.network`` や ``ip r`` も確認してください。
### before
![redis-2v-netowrk-before.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264188/f2214538-ad3b-b26d-47a1-cb2b50fba025.jpeg)
### after
![redis-2v-netowrk-after.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264188/638f8dbe-fb09-9073-c442-69732f6135f6.jpeg)

### /etc/systemd/network/05-eth0.network
```
[Match]
Name=eth0

[Network]
DHCP=no

Domains=members.linode.com
IPv6PrivacyExtensions=false

Address=10.0.0.12/24
Gateway=10.0.0.1
```

## redis-3v
### after
![redis-3v-netowrk-after.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264188/a9c2ff4a-3b3d-a0ca-a15d-1c9eb9654ad7.jpeg)



## Client からの接続

```
redisclient:~# redis-cli -h vlan-haproxy --tls --cacert redis-nj_ca.crt
vlan-haproxy:6379> role
1) "master"
2) (integer) 286550827
3) 1) 1) "10.0.0.12"
      2) "6379"
      3) "286550827"
   2) 1) "10.0.0.13"
      2) "6379"
      3) "286550827"
```

# HAProxy の Public IP を削除し、VLAN Only にする
## Before
![vlan-haproxy-network-before.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264188/39da4910-5087-6695-de2b-3dcedd6a3470.jpeg)

## After
![vlan-haproxy-network-after.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264188/58dd3440-2aea-c650-51d8-c2b78d0c9621.jpeg)


## Client からの接続

```
redisclient:~# redis-cli -h vlan-haproxy --tls --cacert redis-nj_ca.crt ping
PONG
redisclient:~# redis-cli -h vlan-haproxy --tls --cacert redis-nj_ca.crt role
1) "master"
2) (integer) 286608397
3) 1) 1) "10.0.0.12"
      2) "6379"
      3) "286608264"
   2) 1) "10.0.0.13"
      2) "6379"
      3) "286608264"
```

## Redis マスターサーバーを計画停止する
Redis Client から HAProxy 経由で Redis マスターサーバーに接続します。
```
redisclient:~# redis-cli -h vlan-haproxy --tls --cacert redis-nj_ca.crt
vlan-haproxy:6379> role
1) "master"
2) (integer) 286765792
3) 1) 1) "10.0.0.12"
      2) "6379"
      3) "286765792"
   2) 1) "10.0.0.13"
      2) "6379"
      3) "286765792"
vlan-haproxy:6379> set scott tiger
OK
vlan-haproxy:6379> get scott
"tiger"
```

マスターサーバーが動作している ``redis_1`` の ``redis-server`` を停止します。
```
redis_1:~# systemctl stop redis-server
```
Redis クライアント(``redisclient``)から HAProxy(``vlan-haproxy``)に接続している ``redis-cli`` でコマンドをいくつか打ちます。一瞬接続エラーがでましたが、その後、正常に動作しています。Redis Server が切り替わるまでは接続エラーになるはずですが、切り替わった後は HAProxy 経由で正常に接続し、書き込みもできています。
```
vlan-haproxy:6379> role
Could not connect to Redis at vlan-haproxy:6379: SSL_connect failed: unexpected eof while reading
not connected> role
1) "master"
2) (integer) 286782638
3) 1) 1) "10.0.0.13"
      2) "6379"
      3) "286782505"
vlan-haproxy:6379> role
1) "master"
2) (integer) 286783170
3) 1) 1) "10.0.0.13"
      2) "6379"
      3) "286783037"
vlan-haproxy:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=10.0.0.13,port=6379,state=online,offset=286787720,lag=0
master_failover_state:no-failover
master_replid:d73bba0408b89d4481305c1a90cf2ee662079b32
master_replid2:7c0b274d9fa87ba0c2328927591112715b687d09
master_repl_offset:286787720
second_repl_offset:286781538
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:286550660
repl_backlog_histlen:237061
vlan-haproxy:6379> get scott
"tiger"
vlan-haproxy:6379> set scott lion
OK
vlan-haproxy:6379> get scott
"lion"
```

Redis マスターサーバーが計画停止したときにレプリカサーバーがマスターに昇格しています。Redis クライアントからは HAProxy を経由して、常にマスターサーバーに接続できており、マスターサーバーがどこで動いているかを気にする必要がありません。

![vlan-redis-master-change.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264188/89416604-79a1-0f07-f7f4-87df36292cf9.jpeg)


# まとめ
最終的に以下のような構成を取ることができました。Redis Server には他のネットワークからは接続することができません。Redis を必要とするアプリケーションは同じ Region で同一の VLAN ネットワーク上である必要があります。
![redis-vlan.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264188/2e8e1325-55c5-8c97-55d5-a14ed4d20086.jpeg)