2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Redis Sentinel 構成の前に HAProxy を配置し常にマスターサーバーに接続できるようにする

Last updated at Posted at 2023-07-14

Redis Sentinel はマスターサーバーの障害を検知し、動的にレプリカをマスターに昇格することできる高可用性ソリューションです。しかし、アプリケーションからの視点では一つ気になることがあります。アプリケーションからどうやってマスターサーバーの IP を知るかです。Redis Sentinel にはクライアントの構築についてドキュメントが公開されています。

この方法を使えば、Sentinel と通信することで、動的にマスターサーバーの IP を知ることができます。しかし、実際にはアプリケーションの仕様を変更したくないケースは多いです。本記事では HAProxy を利用して、アプリケーションの仕様変更を不要とし、常時マスターサーバーに接続できるようにする方法について説明します。

最終的には下記の構成を構築できます。
redis-haproxy-final.jpg.jpg

今回紹介する例では、HAProxy が SPOF になります。HAProxyをクラスタ化し IP Sharing 機能を使うことで SPOF を排除できます。IP Sharing については以下の記事を参考にしてください。

Redis Sentinel

Redis Sentinel の環境を用意します。今回は下記の記事で紹介した環境で行います。

アプリケーション (Redis Client) から見える Redis Sentinel

HAProxy を導入しないパターンを説明します。

最初にクライアントはマスターサーバーのIPを静的に保持して接続できています。
redis-sentinel-1-first.jpg

マスターサーバーがダウンし、クライアントがマスターサーバーに接続できないケースを考えます。
redis-sentinel-2-serverdown.jpg

3 台のマスター・レプリカ構成を取っていた場合、レプリカのどちらがマスターになるかを知る必要があります。そのためには誰かが Sentinel か Redis に問い合わせする必要があります。
redis-sentinel-3-masterchange.jpg

本問題を解決するために本記事では HAProxy を使う例を紹介します。

HAProxy を動作するインスタンスの準備

Linode を作成します。

  • Tokyo Region
  • Ubuntu 22.04 LTS
  • Linode 2GB Plan
  • Private IP をオンにする (後からでも追加可能)
  • Linode Label: haproxy-redis-tokyo
  • Tags: Redis Sentinel と同じ

設定した Private IP をメモします。後で利用します。

localhost:~# ip -4 a show dev eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    inet 172.104.79.130/24 brd 172.104.79.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet {PUBLIC_IP_ADDRESS}/17 brd 192.168.255.255 scope global eth0
       valid_lft forever preferred_lft forever

インスタンスがセキュアになるように設定します。

Redis Server に接続できるように /etc/hosts に IP アドレスを登録します。

# BEGIN redis servers
# Redis
192.168.198.87 redissentinel-tokyo1 redis_1
192.168.198.103 redissentinel-tokyo2 redis_2
192.168.198.112 redissentinel-tokyo3 redis_3
# END redis servers

Redis Server の設定

HAProxy がアクセスできるように firewalld の設定をします。

  <source address="{PUBLIC_IP_ADDRESS}"/>

最終的には全ての Redis Server が動作している Linux で設定する必要がありますが、まずは、マスターのサーバー環境で実施します。

systemctl restart firewalld

HAProxy のインストール

www.linode.com のドキュメントを参照して、HAProxy をインストールします。

apt update && apt upgrade
sudo apt-get install haproxy

インストールに成功したらバージョンを確認します。

haproxy-redis-tokyo:/etc/haproxy# haproxy -v
HAProxy version 2.4.22-0ubuntu0.22.04.1 2023/03/22 - https://haproxy.org/
Status: long-term supported branch - will stop receiving fixes around Q2 2026.
Known bugs: http://www.haproxy.org/bugs/bugs-2.4.22.html
Running on: Linux 5.15.0-73-generic #80-Ubuntu SMP Mon May 15 15:18:26 UTC 2023 x86_64

HAProxy の Config

/etc/haproxy/ にコンフィグファイルがあります。

haproxy-redis-tokyo:~# cd /etc/haproxy/
haproxy-redis-tokyo:/etc/haproxy# ls
errors  haproxy.cfg

haproxy.cfg を確認します。このファイルに追記することになります。

haproxy-redis-tokyo:/etc/haproxy# cat haproxy.cfg
global
    log /dev/log	local0
    log /dev/log	local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

	# Default SSL material locations
	ca-base /etc/ssl/certs
	crt-base /etc/ssl/private

	# See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate
        ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
        ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
        ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets

defaults
	log	global
	mode	http
	option	httplog
	option	dontlognull
        timeout connect 5000
        timeout client  50000
        timeout server  50000
	errorfile 400 /etc/haproxy/errors/400.http
	errorfile 403 /etc/haproxy/errors/403.http
	errorfile 408 /etc/haproxy/errors/408.http
	errorfile 500 /etc/haproxy/errors/500.http
	errorfile 502 /etc/haproxy/errors/502.http
	errorfile 503 /etc/haproxy/errors/503.http
	errorfile 504 /etc/haproxy/errors/504.http

Redis 用の設定

以下の設定を追記します。この例ではマスターとして動作している redis_2 を登録する例です。

# Specifies TCP timeout on connect for use by the frontend ft_redis
# Set the max time to wait for a connection attempt to a server to succeed
# The server and client side expected to acknowledge or send data.
defaults REDIS
    mode tcp
    timeout connect 3s
    timeout server 6s
    timeout client 6s

# Specifies listening socket for accepting client connections using the default
# REDIS TCP timeout and backend bk_redis TCP health check.
frontend ft_redis
    bind *:6379 name redis
    default_backend bk_redis

# Specifies the backend Redis proxy server TCP health settings
# Ensure it only forward incoming connections to reach a master.
backend bk_redis
    server redis_6379 redis_2:6379 check inter 1s

Stats ページのための設定

この設定を入れると HAProxy がフォワードする Redis サーバーの状態を表示できます。後ほど使用する例を紹介します。

#
listen stats
    mode http
    bind {HAproxy の Public IP}:7000
    stats uri /
    stats auth {ユーザー名}:{パスワード}
    stats refresh 10s
    stats show-legends

stats auth には Stats にアクセスするためのクレデンシャルとなります。

使わないときはコメントアウトしてオフにしましょう。

redis-cli

設定を確認するために redis-cli を入手します。

apt install redis

エラーがでました。

haproxy-redis-tokyo:/etc/haproxy# apt install redis
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
E: Unable to locate package redis-cli

以下のコマンドの実行で解決できました。

add-apt-repository universe

再度インストールを試みます。

apt install redis

redis-server も同時にインストールされ、プロセスが起動します。不要なので停止してください。手順は後ほど説明します。

インストールされたら、バージョンを確認します。

haproxy-redis-tokyo:~# redis-cli -v
redis-cli 6.0.16

redis-cli は TLS 接続する必要があるので、Redis Server の /etc/redis/tls/ca.crt をローカルのファイルにコピーします。

haproxy-redis-tokyo:/etc/haproxy# pwd
/etc/haproxy
haproxy-redis-tokyo:/etc/haproxy# ls -l tls
total 4
-r--r----- 1 haproxy haproxy 2029 Jul  1 02:49 redissentinel-tokyo_ca.crt

REdISCLI_AUTH も環境変数に設定します。

export REDISCLI_AUTH=`Redis に接続するためのパスワード`

現在マスターが redis_2 のホストとして登録しているので、-h で指定します。証明書は事前にコピーしたファイルを指定します。ここでは redis-cli の動作確認をするだけで、HAProxy 経由での接続確認ではありません。

haproxy-redis-tokyo:~# redis-cli -h redis_2 --tls --cacert /etc/haproxy/tls/redissentinel-tokyo_ca.crt
redis_2:6379> ping
PONG

PING PONG ができれば、認証された接続に成功しています。

書き込みができるかも確認します。

haproxy-redis-tokyo:~# redis-cli -h redis_2 --tls --cacert /etc/haproxy/tls/redissentinel-tokyo_ca.crt

redis_2:6379> get scott
"tiger"
redis_2:6379> set scott lion
OK
redis_2:6379> get scott
"lion"

scott のキーの値を変更できました。これは、クライアントがマスターサーバーに接続できているからです。

HAProxy 上で Redis-server の停止

redis-cli を使うために Redis をインストールしましたが、Redis サーバーが動いていると後ほど問題が起きる可能性がありますので、停止します。

systemctl stop redis-server
systemctl disable redis-server

実際に発生した問題は、HAProxy の Bind 設定との衝突です。今回は実施しませんが、HAProxy を冗長化したときに、redis-server が 6379 ポートを Listen していると、次のような bind 設定がエラーとなって HAProxy が起動できません。

bind *:6379

そのため、特定の IP を指定する必要がありました。例えば次のように設定する必要がありました。

bind {Server の IP address}:6379

このように設定することで問題は解消されるのですが、別の記事で紹介する keepalive を使った VIP の設定では、バックアップとして動作するサーバー側ではその VIP が動いていないので、HAProxy 起動時に VIP でバインドすることができません。よって keepalived でフェールオーバーしても、VIP にバインドされていないので HAProxy につなぐことができませんでした。これも HAProxy で redis-server を停止することで bind *:6379 のように設定も楽になり問題も解消されます。

HAProxy に監視の機能を入れる

external-check

HAProxy の external-check は外部のコマンドを起動してステータス監視をするときに便利な機能です。この機能を利用し、スクリプトを登録します。スクリプトの中では、以下のコマンドを起動し、どのサーバーがマスターなのかを常に監視します。

tcp-check を使う方法もありますが、TLS で接続する必要があるため、external-check を使います。

haproxy-redis-tokyo:~# /usr/bin/redis-cli --no-auth-warning -a $password -h redis_1 -p 6379 --tls --cacert /etc/haproxy/tls/redissentinel-tokyo_ca.crt INFO REPLICATION | egrep role
role:slave

haproxy-redis-tokyo:~# /usr/bin/redis-cli --no-auth-warning -a $password -h redis_2 -p 6379 --tls --cacert /etc/haproxy/tls/redissentinel-tokyo_ca.crt INFO REPLICATION | egrep role
role:master

exernal_check で指定するスクリプトを配置します。

haproxy-redis-tokyo:/etc/haproxy# ls -l bin
total 4
-rwxr-x--- 1 haproxy haproxy 459 Jul  1 12:52 check_redis_master.sh

external-check で起動されるスクリプトです。

#!/bin/bash
password='YOUR_REDIS_SERVER_PASSWORD'
ip_adr=$(echo $@ | awk '{print$3}')
command='INFO REPLICATION'
redis_server_role=$(/usr/bin/redis-cli --no-auth-warning -a $password -h $ip_adr -p 6379 --tls --cacert /etc/haproxy/tls/redissentinel-tokyo_ca.crt $command | grep role | awk -F ':' '{print $2}' | tr -d '\r')

if [[ $redis_server_role == 'master' ]]; then
        exit 0
else
        exit 1
fi

haproxy.cfg

haproxy.cfg の global を次のように変更します。insecure-fork-wantedinsecure-setuid-wantedexternal-check を追記します。

global
    insecure-fork-wanted
    insecure-setuid-wanted
	external-check
	log /dev/log	local0
	log /dev/log	local1 notice
#	chroot /var/lib/haproxy
	stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
	stats timeout 30s
	user haproxy
	group haproxy
	daemon

backend の設定を次のように external-check を使うように設定します。

# Specifies the backend Redis proxy server TCP health settings
# Ensure it only forward incoming connections to reach a master.
backend bk_redis
    option external-check
    external-check path "/usr/bin:/bin:/usr/local/bin"
    external-check command /etc/haproxy/bin/check_redis_master.sh
    server redis_6379 redis_2:6379 check inter 10s

HAProxy を再起動します。

systemctl restart haproxy

/var/log/haproxy.log には redis_2 に対してステータスを確認しているログが表示されています。

haproxy-redis-tokyo:/etc/haproxy# tail -f /var/log/haproxy.log
Jul  1 04:22:33 haproxy-redis-tokyo haproxy[15891]: XX.XX.XX.XX:64282 [01/Jul/2023:04:22:33.071] stats stats/<STATS> 0/0/0/0/0 200 17812 - - LR-- 2/2/0/0/0 0/0 "GET / HTTP/1.1"
Jul  1 04:22:43 haproxy-redis-tokyo haproxy[15891]: XX.XX.XX.XX:64282 [01/Jul/2023:04:22:43.143] stats stats/<STATS> 0/0/0/0/0 200 17812 - - LR-- 2/2/0/0/0 0/0 "GET / HTTP/1.1"
Jul  1 04:22:53 haproxy-redis-tokyo haproxy[15891]: XX.XX.XX.XX:64282 [01/Jul/2023:04:22:53.255] stats stats/<STATS> 0/0/0/0/0 200 17812 - - LR-- 2/2/0/0/0 0/0 "GET / HTTP/1.1"
Jul  1 04:23:03 haproxy-redis-tokyo haproxy[15891]: XX.XX.XX.XX:64282 [01/Jul/2023:04:23:03.337] stats stats/<STATS> 0/0/0/0/0 200 17810 - - LR-- 2/2/0/0/0 0/0 "GET / HTTP/1.1"
Jul  1 04:23:13 haproxy-redis-tokyo haproxy[15891]: XX.XX.XX.XX:64282 [01/Jul/2023:04:23:13.435] stats stats/<STATS> 0/0/0/0/0 200 17812 - - LR-- 1/1/0/0/0 0/0 "GET / HTTP/1.1"
Jul  1 04:23:23 haproxy-redis-tokyo haproxy[15891]: XX.XX.XX.XX:64282 [01/Jul/2023:04:23:23.539] stats stats/<STATS> 0/0/0/0/0 200 17812 - - LR-- 1/1/0/0/0 0/0 "GET / HTTP/1.1"
Jul  1 04:23:33 haproxy-redis-tokyo haproxy[15891]: XX.XX.XX.XX:64282 [01/Jul/2023:04:23:33.626] stats stats/<STATS> 0/0/0/0/0 200 17812 - - LR-- 2/2/0/0/0 0/0 "GET / HTTP/1.1"
Jul  1 04:23:43 haproxy-redis-tokyo haproxy[15891]: XX.XX.XX.XX:64282 [01/Jul/2023:04:23:43.698] stats stats/<STATS> 0/0/0/0/0 200 17812 - - LR-- 2/2/0/0/0 0/0 "GET / HTTP/1.1"
Jul  1 04:23:53 haproxy-redis-tokyo haproxy[15891]: XX.XX.XX.XX:64282 [01/Jul/2023:04:23:53.802] stats stats/<STATS> 0/0/0/0/0 200 17812 - - LR-- 2/2/0/0/0 0/0 "GET / HTTP/1.1"
Jul  1 04:24:03 haproxy-redis-tokyo haproxy[15891]: XX.XX.XX.XX:64282 [01/Jul/2023:04:24:03.879] stats stats/<STATS> 0/0/0/0/0 200 17810 - - LR-- 2/2/0/0/0 0/0 "GET / HTTP/1.1"
Jul  1 04:24:13 haproxy-redis-tokyo haproxy[15891]: XX.XX.XX.XX:64282 [01/Jul/2023:04:24:13.954] stats stats/<STATS> 0/0/0/0/0 200 17812 - - LR-- 1/1/0/0/0 0/0 "GET / HTTP/1.1"

redis-cli も動作しています。

haproxy-redis-tokyo:~# redis-cli -h redis_2 --tls --cacert /etc/haproxy/tls/redissentinel-tokyo_ca.crt
redis_2:6379> ping
PONG
redis_2:6379> get scott
"lion"
redis_2:6379> set dixson
(error) ERR wrong number of arguments for 'set' command
redis_2:6379> set scott dixson
OK
redis_2:6379> get scott
"dixson"
redis_2:6379> quit

Stats

http://{HAProxy サーバーの PublicIP}:7000/ にアクセスします。次のようなページが表示されます。まずは、表示されたことを確認してください。haproxy.cfgauth で指定したユーザー名とパスワードを入力してください。

haproxy-status.jpg

次に haproxy.cfg に全ての Redis サーバーを登録します。次のように書き換えます。

変更前
server redis_6379 redis_2:6379 check inter 10s
変更後
server redis_6379_1 redis_1:6379 check inter 1s
server redis_6379_2 redis_2:6379 check inter 1s
server redis_6379_3 redis_3:6379 check inter 1s

Redis Server の Firewall 設定も忘れないでください。

HAProxy をリスタートします。

systemctl restart haproxy

Stat をブラウザで見ます。bk_redis の表に3つのサーバーの状態が見えます。
haproxy-status-3node.jpg
マスターとレプリカはそれぞれ、緑と赤で表示されています。レプリカのサーバーは実際には落ちているわけではなく、先程登録した external-check で動いているスクリプトでステータス 1 で返るようにしているからです。HAProxy からはステータス 0 で返るマスターサーバーが有効としてフォワードしています。

Master の切り替え

HAProxy サーバーのクライアントからの確認

マスターとして動作している Redis_2 のサーバーを止めます。

systemctl stop redis-server

Stats では一瞬全てが赤のステータスになります。
haxproxy-status-masterdown.jpg

HAProxy のホストの Redis-cli を使って接続します。接続されたサーバーは role:master となっています。

haproxy-redis-tokyo:~# redis-cli --tls --cacert  redissentinel-tokyo_ca.crt info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.198.87,port=6379,state=online,offset=29561919,lag=1
master_failover_state:no-failover
master_replid:a284a16594fa8ce12b0db3b2d52eb4a185132ed5
master_replid2:4831258c3aa4c7d8d6c9e0eb1244acb86c511c8a
master_repl_offset:29562064
second_repl_offset:29497534
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:28493428
repl_backlog_histlen:1068637

最終的には、以下のように HAProxy は Redis_3 にフォワードしています。
redis_haproxy-failover.jpg.jpg

Stats は以下のように見えます。Redis_3 が緑色になっています。
haproxy-status-3takesmasterrole.jpg

Redis Client アプリケーションからの接続

先程までは、HAProxy のサーバー内から接続していましたが、ここからはリモートにある Redis Client から接続を確認します。
redis-haproxy-client.jpg.jpg

/etc/hosts に HAProxy の Private IP を登録します。

{PUBLIC_IP_ADDRESS} redis-haproxy

REDISCLI_AUTH 環境変数をセットします。Redis Sentinel がインストールされたときに作成されたユーザーのホームディレクトリ配下に作成された deployment-secrets.txt に記載されている redis password が該当します。

HAproxy に接続します。

redisclient:~# redis-cli -h redis-haproxy --tls --cacert redissentinel-tokyo_ca.crt ping
PONG

HAProxy経由では必ず Master ロールのサーバーに接続されます。

redisclient:~# redis-cli -h redis-haproxy --tls --cacert redissentinel-tokyo_ca.crt info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.198.87,port=6379,state=online,offset=29619571,lag=1
master_failover_state:no-failover
master_replid:a284a16594fa8ce12b0db3b2d52eb4a185132ed5
master_replid2:4831258c3aa4c7d8d6c9e0eb1244acb86c511c8a
master_repl_offset:29619585
second_repl_offset:29497534
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:28554748
repl_backlog_histlen:1064838
redisclient:~# redis-cli -h redis-haproxy --tls --cacert redissentinel-tokyo_ca.crt role
1) "master"
2) (integer) 29621755
3) 1) 1) "192.168.198.87"
      2) "6379"
      3) "29621321"

止めていた Redis_2 の redis-server を起動します。

redissentinel-tokyo2:~# systemctl start redis-server

マスターではなくレプリカとして起動していることも分かります。

haproxy-redis-tokyo:~# /usr/bin/redis-cli --no-auth-warning -a $password -h redis_2 -p 6379 --tls --cacert /etc/haproxy/tls/redissentinel-tokyo_ca.crt INFO REPLICATION | egrep role
role:slave

redsi-haproxy-client-allup.jpg.jpg

マスターサーバーの停止

Redis Client で redis-cli を HAproxy に接続した状態にします。

redisclient:~# redis-cli -h redis-haproxy --tls --cacert redissentinel-tokyo_ca.crt
redis-haproxy:6379> ping
PONG
redis-haproxy:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.198.87,port=6379,state=online,offset=29954946,lag=0
slave1:ip=192.168.198.103,port=6379,state=online,offset=29954946,lag=0
master_failover_state:no-failover
master_replid:a284a16594fa8ce12b0db3b2d52eb4a185132ed5
master_replid2:4831258c3aa4c7d8d6c9e0eb1244acb86c511c8a
master_repl_offset:29955235
second_repl_offset:29497534
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:28902228
repl_backlog_histlen:1053008

マスターとして動作している Redis_3 を停止します。

redissentinel-tokyo3:~# systemctl stop redis-server

下図の状態になります。
redis-haproxy-redis3-down.jpg

マスターが変わったことを確認します。今回再度 Redis_2 がマスターになりました。
redis-master-switch-from3to2.jpg

Redis Client にて redis-cli で接続した状態から再度コマンドを実行します。

redis-haproxy:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.198.87,port=6379,state=online,offset=29971306,lag=1
master_failover_state:no-failover
master_replid:fbb1e8bf9574889409d0bdfdcb78d4d56387877b
master_replid2:a284a16594fa8ce12b0db3b2d52eb4a185132ed5
master_repl_offset:29971306
second_repl_offset:29967061
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:29765770
repl_backlog_histlen:205537
redis-haproxy:6379> role
1) "master"
2) (integer) 29974806
3) 1) 1) "192.168.198.87"
      2) "6379"
      3) "29974806"

redis-cli は接続した状態でマスターの IP が変更されていることが分かります。

redis-haproxy-client-failover-again.jpg

Redis-server のマスターがダウンしたときにログやファイルにどのように記録されているかは下記を参考にしてください。

まとめ

Redis Sentinel と HAProxy を使うことで、Redis Server の高可用性を保ちながら、アプリケーションは常に Redis のマスターサーバーに接続できることを確認しました。

HAProxy を H/A 構成にすれば、SPOF を排除できますので、以下の記事も参考にしてください。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?