目次
前書き
こんにちは! 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 分けてredis同様にリンクをここに貼った方が管理が楽になります。
以下 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 では、いろんな技術を見て触れる機会が多いのでエンジニアとしてとても充実した毎日を過ごしています。
また機会があれば何卒よろしくお願いいたします!!