grasys blog

Redis 冗長化への道

前書き

こんにちは! grasys2 年目エンジニアの tokura です。

前回記事を書いた時からほぼ 1 年くらい経ってますね。時の流れは早いものです。

さて、今回も直近の業務で触ったものをテーマに書いていきます。

今回のテーマはタイトルの通り「Redis 冗長化」です。Redis(valkey)、sentinel、HAproxy を使います。

構成を初めて見た時に感心したことを思い出しました。同じ気持ちになる人の参考になればと思います。

以下が今回のサーバ構成です。

  • サーバ 0 HAproxy
  • サーバ 1 master & sentinel
  • サーバ 2 replica & sentinel
  • サーバ 3 sentinel & sentinel

全 4 台で OS は Rocky Linux9 です
それではよろしくお願いします

Redis(valkey)server 設定

Redis 冗長化と言っても、最近は Valkey を目にする機会が増え、せっかくなので valkey で進めます。

(違いは名前くらいしかないと思いますが…)

ビルドインストール

homebrew とかパッケージでもできるみたいですが、サーバ 1 にソースをダウンロードしてインストール。

wget -P /usr/local/src https://github.com/valkey-io/valkey/archive/refs/tags/8.0.1.tar.gz

tar -zxvf /usr/local/src/8.0.1.tar.gz
cd /usr/local/src/valkey-8.0.1

make                   # オプションとか必要なら適宜ビルド時に追加
make install

ビルド後は間違えて redis-cli って入力しても使えるようシムリンクが貼られてます

# ll  /usr/local/bin/
total 29568
lrwxrwxrwx 1 root root       16 Nov  5 11:25 redis-benchmark -> valkey-benchmark
lrwxrwxrwx 1 root root       16 Nov  5 11:25 redis-check-aof -> valkey-check-aof
lrwxrwxrwx 1 root root       16 Nov  5 11:25 redis-check-rdb -> valkey-check-rdb
lrwxrwxrwx 1 root root       10 Nov  5 11:25 redis-cli -> valkey-cli
lrwxrwxrwx 1 root root       15 Nov  5 11:25 redis-sentinel -> valkey-sentinel
lrwxrwxrwx 1 root root       13 Nov  5 11:25 redis-server -> valkey-server
-rwxr-xr-x 1 root root  6381928 Nov  5 11:25 valkey-benchmark
lrwxrwxrwx 1 root root       13 Nov  5 11:25 valkey-check-aof -> valkey-server
lrwxrwxrwx 1 root root       13 Nov  5 11:25 valkey-check-rdb -> valkey-server
-rwxr-xr-x 1 root root  7084456 Nov  5 11:25 valkey-cli
lrwxrwxrwx 1 root root       13 Nov  5 11:25 valkey-sentinel -> valkey-server
-rwxr-xr-x 1 root root 16802304 Nov  5 11:25 valkey-server

Service 化

インストールもできたことなので service として redis(valkey)-server を立ち上げます。

/usr/local/src/valkey8.0.1 内にデフォルトファイルがあるので活用。

自サーバ用に編集するために適当にディレクトリ作成して持ってきます。

mkdir /usr/local/etc/valkey
 cp /usr/local/src/valkey8.0.1/utils/systemd-valkey_server.service /usr/local/etc/valkey/valkey.service 
 cp /usr/local/etc/valkey/valkey.conf  /usr/local/etc/valkey/


conf は以下を設定。その他かなり設定項目ありますが、サーバスペックや使い方によって要変更です。

追加で項目が必要そうなら…ドキュメントに頼りましょう

bind 0.0.0.0 
port 6379 
protected-mode no 
daemonize yes


service も簡潔に以下

[Unit]
Wants=network-online.target
After=network-online.target

[Service]
Type=forking
User=root
Group=root
ExecStartPre=/bin/touch /var/log/valkey/valkey.log
ExecStart=/usr/local/bin/valkey-server /usr/local/var/config.d/valkey/valkey.conf


[Install]
WantedBy=multi-user.target


/usr/local/bin/valkey-server /usr/local/var/config.d/valkey/valkey.conf

サービスファイル内の↑の部分の conf について

複数サーバを建てる際に同じ service ファイルで構成できるように↓的なシムリンクを貼っています

/usr/local/var/config.d/valkey/valkey.conf -> /usr/local/etc/valkey/valkey.conf

(運用だとこんな感じで動かしてるプロセスが多くあって非常に勉強になります)

後は service ファイルを systemd へ設定できたら準備完了です。

/etc/systemd/system/valkey.service -> /usr/local/etc/valkey/valkey.service

# systemctl status valkey.service
● valkey.service - Valkey data structure server
     Loaded: loaded (/etc/systemd/system/valkey.service; linked; preset: disabled)
     Active: active (running) since Fri 2024-11-08 06:56:33 UTC; 3min 24s ago
       Docs: https://github.com/valkey-io/valkey-doc
    Process: 149437 ExecStartPre=/bin/touch /var/log/valkey/valkey.log (code=exited, status=0/SUCCESS)
    Process: 149439 ExecStart=/usr/local/bin/valkey-server /usr/local/var/config.d/valkey/valkey.conf (code=exited, status=0/SUCCESS)
   Main PID: 149441 (valkey-server)
      Tasks: 6 (limit: 23069)
     Memory: 8.9M
        CPU: 920ms
     CGroup: /system.slice/valkey.service
             └─149441 "/usr/local/bin/valkey-server 0.0.0.0:6379"

Nov 08 06:56:33 tokura-test-1 systemd[1]: Starting Valkey data structure server...
Nov 08 06:56:33 tokura-test-1 systemd[1]: Started Valkey data structure server.

Replication

1 のサーバが無事起動したら次は replication させます。

サーバ 2 に、ほぼサーバ 1 と同様に設定して conf で以下のように設定して以下のようなシムリンクを貼ります。

replicaof <サーバー1の外部IP>  6379

/usr/local/var/config.d/valkey/valkey.conf -> /usr/local/etc/valkey/valkey-replica.conf

サーバ 2 起動前にサーバ 1 の Redis に適当に key を入れておくと replication の確認にもなります。

2 も同様に起動して両サーバの valkey-cli で様子を見ます。

#  redis-cli info replication
# Replication
role:master
connected_slaves:1
slave0:ip=<サーバー2の外部IP>,port=6379
# valkey-cli info replication
# Replication
role:slave
master_host:<サーバー1の外部IP>
master_port:6379

ここまでは Redis の設定です。

次は Sentinel を入れていきます。

Sentinel

Sentinel はレプリケーションしている Redis マスターに障害が発生して通信ができなくなった場合に「マスターがダウンした」と検知し、フェイルオーバーを実行する機能を持ちます

詳しい仕組みや設定については公式を確認してください

Sentinel の構成と 3 台目の必要性

valkey-server をインストールすると、valkey-sentinel も設定されているため、この Sentinel を利用してマスター監視とフェイルオーバー設定ができます。

ただし、Sentinel は 2 台構成ではフェイルオーバーに支障が出る場合があります。

フェイルオーバーを実行するには、Sentinel のグループ内で過半数の同意が必要なため、2 台だけで構成していると以下のようなリスクがあります

  • Sentinel が 2 台だけの構成で、1 台に入ってるマスターRedis と Sentinel がダウンした場合、残った 1 台の Sentinel ではフェイルオーバーを実行できない。

この問題を避けるために、3 台目の Sentinel をサーバ 3 に追加します。3 台構成にすることで、1 台のマスター Redis と Sentinel がダウンしても、残りの 2 台の Sentinel で過半数が確保でき、正しくフェイルオーバーが実行され、レプリカが新しいマスターに昇格されます。

conf と service

conf について server 設定時と同様に sentinel ディレクトリでも作成して conf ファイルを作成します。

mkdir /usr/local/etc/sentinel
cp /usr/local/src/valkey8.0.1/sentinel.conf /usr/local/etc/sentinel/

service ファイルも同様です。valkey-server から↓のように書き換えるくらいで動きます。

ExecStartPre=/bin/touch /var/log/valkey/sentinel.log
ExecStart=/usr/local/bin/valkey-sentinel /usr/local/var/config.d/valkey/sentinel.conf

conf に関して今回は以下のように設定。

protected-mode no
port 26379
daemonize yes
pidfile "/var/run/valkey-sentinel.pid"
loglevel notice
logfile "/var/log/valkey/sentinel.log"
dir "/tmp"

sentinel monitor mymaster <サーバー1の外部IP> 6379 2

sentinel failover-timeout mymaster 10000

acllog-max-len 128

sentinel resolve-hostnames no
sentinel announce-hostnames no

sentinel で見るホストを hostname で取れるオプションもあるのでサーバ構成に応じて使ってください…

server設定の時はシムリンクで設定していたが、今回は /usr/local/var/config.d/valkey/sentinel.conf へ直接コピーする。理由は後述。

conf ファイルなどが設定できたら起動を確認。

# systemctl status sentinel.service
● sentinel.service - Valkey data structure server
     Loaded: loaded (/etc/systemd/system/sentinel.service; linked; preset: disabled)
     Active: active (running) since Fri 2024-11-08 07:50:47 UTC; 7s ago

この時 conf を見ると以下のような追記がされている

# Generated by CONFIG REWRITE
latency-tracking-info-percentiles 50 99 99.9
user default on nopass sanitize-payload ~* &* +@all
sentinel myid 4a81bfa9cffc81e0bb3122db27f21381f4b64b06
sentinel config-epoch mymaster 2
sentinel leader-epoch mymaster 2
sentinel current-epoch 2

Sentinel の conf を Redis のようにシムリンクではなくコピーしたのは、Sentinel 起動後に sentinel の conf に対してサーバ固有の追記が行われるためでした。

FO 確認

Sentinel の起動が確認できたらフェイルオーバーを確認します。

サーバ 3 で tail -f /var/log/valkey/sentinel.log でログでも見ながら

Redis マスターのサーバ 1 で Redis を止めて、再度起動してみます。

すると sentinel.log で +sdown やら +odown ,+switch-master やらと FO が発生してそうなログが流れています。

replica だったサーバ 2 で確認してみると

 redis-cli info replication
# Replication
role:master

たしかに昇格しています。

ここでサーバ 3 の sentinel を止めて、sentinel を 2 台にした上でサーバ 2 の redis を止めてみます

# redis-cli info replication
# Replication
role:slave
master_host:<サーバー2の外部IP>
master_port:6379
master_link_status:down

昇格できません。もう一度サーバ 3 の Sentinel を動かすとあっさり昇格してくれました。sentinel サーバが 3 台ないと FO してくれないことが確認できました。

このままでも master が落ちたら勝手に切り替わって冗長じゃないか?と思ってたんですが、さらに上がありました。

HAproxy の設定

HAproxy を使用することでより冗長な構成になります。

HAproxy は負荷分散およびプロキシサーバに用いられる OSS です。詳細は公式を参照ください。

ダウンロード

今回自分はパッケージマネージャで入れましたが、インストールしたい希望のバージョンある場合はソースから

# dnf info haproxy
Last metadata expiration check: 2:35:00 ago on Fri 08 Nov 2024 05:46:34 AM UTC.
Available Packages
Name         : haproxy
Version      : 2.4.22

# dnf install haproxy -y

conf と service

インストール完了したら service から conf の場所を確認します

systemctl status haproxy
○ haproxy.service - HAProxy Load Balancer
Loaded: loaded (/usr/lib/systemd/system/haproxy.service; disabled; preset: disabled)
[Unit]
Description=HAProxy Load Balancer
After=network-online.target
Wants=network-online.target

[Service]
EnvironmentFile=-/etc/sysconfig/haproxy
Environment="CONFIG=/etc/haproxy/haproxy.cfg" "PIDFILE=/run/haproxy.pid" "CFGDIR=/etc/haproxy/conf.d"
ExecStartPre=/usr/sbin/haproxy -f $CONFIG -f $CFGDIR -c -q $OPTIONS
ExecStart=/usr/sbin/haproxy -Ws -f $CONFIG -f $CFGDIR -p $PIDFILE $OPTIONS
ExecReload=/usr/sbin/haproxy -f $CONFIG -f $CFGDIR -c -q $OPTIONS
ExecReload=/bin/kill -USR2 $MAINPID
KillMode=mixed
SuccessExitStatus=143
Type=notify

[Install]
WantedBy=multi-user.target

/etc/haproxy/haproxy.cfg

↑cfg を設定します。今回サーバ 0 のみですが HAproxy を多く設定するサーバがある場合は conf 分けた方が楽です。

以下 cfg デフォルトから書き加えたところ

frontend redis_frontend
    bind *:6379
    mode tcp
    default_backend redis_backend

backend redis_backend
    mode tcp
    option tcp-check
    tcp-check connect
    tcp-check send PING\r\n
    tcp-check expect string +PONG
    tcp-check send info\ replication\r\n
    tcp-check expect string role:master
    server test-1 <サーバー1の外部IP>:6379 check inter 1s
    server test-2 <サーバー2の外部IP>:6379 check inter 1s

listen stats
    bind :9000
    maxconn 16
    mode http
    stats enable
    stats uri /stats
    stats refresh 5s

書き換えて HAproxy 再起動します

すると HAproxy が動いているサーバでは redis-cli -p 6379 で IP を気にせず常に Redis の master を参照できるようになります。

可用性と冗長性が確保された環境で、安心して Redis を活用できるようになりました。

ちなみに最後の listen 部分を設定すると http://<instance-Ip>:9000/stats で proxy 状態が目視で確認できます。以下 FO した後の様子で、test1 が赤く DOWN に、test2 が緑で UP になっているのが見えます。

あとがき

以上が自分が歩んだ Redis の冗長化への道でした。

こうした方がモダンで良いよ!効率的でしょ!!などのアイデアありましたら、ぜひご教示ください!!!

大変勉強になります。

現在の目標としては、構築の時はより冗長かつ安定でいて運用が楽な構築を、運用の時は効率よく安全に、を目標として日々邁進中です。

grasys では、いろんな技術に見て触れる機会が多いのでエンジニアとしてとても充実した毎日を過ごしています。

また機会があれば何卒よろしくお願いいたします!!


採用情報
お問い合わせ