在 Redis Sentinel 架构前,将 HAProxy 部署并始终连接到主服务器,以确保始终能够连接到主服务器
Redis Sentinel是一种高可用性解决方案,可以检测到主服务器的故障并动态地升级从服务器为主服务器。然而,从应用程序的角度来看,有一件事让人担心,那就是要怎么知道主服务器的IP地址。Redis Sentinel已经提供了关于客户端构建的文档。
通过使用这种方法,可以通过与Sentinel进行通信来动态获取主服务器的IP。但实际上,很多情况下不希望更改应用程序的规格。在本文中,将介绍使用HAProxy的方法,以避免更改应用程序的规格,并保持与主服务器的持续连接。
Redis哨兵
准备Redis Sentinel环境。本次将在以下文章中介绍的环境下进行。
从应用程序(Redis 客户端)能够看到的 Redis Sentinel
以下是关于不使用HAProxy的一种情况的说明。
為了解決這個問題,本文將介紹使用 HAProxy 的例子。
准备运行HAProxy的实例。
创建 Linode。
-
- Tokyo Region
-
- Ubuntu 22.04 LTS
-
- Linode 2GB Plan
-
- Private IP をオンにする (後からでも追加可能)
-
- Linode Label: haproxy-redis-tokyo
- Tags: Redis Sentinel と同じ
将设置好的私有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服务器,需要在/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服务器的配置设置
我会配置防火墙以允许 HAProxy 访问。
<source address="{PUBLIC_IP_ADDRESS}"/>
首先必须在主服务器环境下进行设置,最终需要在所有Redis服务器正常运行的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 的配置文件
在 “/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
设置用于统计页面的配置
当您使用此设置后,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命令行界面
為了檢查設定,取得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
安装完成后,检查版本。
haproxy-redis-tokyo:~# redis-cli -v
redis-cli 6.0.16
为了建立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
如果能打乒乓球,就表示连接已成功经过认证。
我会确认是否可以进行书写。
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 服务器
我为了使用redis-cli,安装了Redis,但是如果Redis服务器正在运行,可能会在以后出现问题,所以我将其停止。
systemctl stop redis-server
systemctl disable redis-server
实际发生的问题是与HAProxy的绑定设置冲突。尽管本次不会实施,但当在实现HAProxy冗余时,如果redis-server正在监听6379端口,则类似如下的绑定设置会导致HAProxy启动失败。
bind *:6379
因此,需要指定特定的 IP。例如,需要进行以下设置。
bind {Server の IP address}:6379
通过这样的设置,问题可以得到解决,但是在另一篇文章中介绍的使用 keepalive 设置的 VIP,由于作为备份的服务器端没有该 VIP 运行,所以无法在启动 HAProxy 时绑定该 VIP。因此,即使通过 keepalived 进行故障转移,也无法连接到 HAProxy,因为它没有绑定到 VIP。同样,通过停止 HAProxy 上的 redis-server,也可以更轻松地进行像 bind *:6379 这样的设置,并且问题也会得到解决。
将监测功能添加到HAProxy中。
外部检查
HAProxy的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
将指定为“external_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
这是一个由外部检查启动的脚本。
#!/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配置文件。
将 haproxy.cfg 中的 global 部分更改为以下内容:新增 insecure-fork-wanted、insecure-setuid-wanted、external-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
将后端设置为使用 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
统计数据
请访问http://公共IP地址的HAProxy服务器:7000/。将显示以下页面,请先确认页面是否已打开。输入在haproxy.cfg文件的auth中指定的用户名和密码。
然后将所有的Redis服务器注册到haproxy.cfg中。进行如下修改。
変更前
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
将HAProxy重新启动。
systemctl restart haproxy
主人的切换
来自HAProxy服务器的客户端确认。
停止 Redis_2 服务器,作为主服务器运行。
systemctl stop redis-server
使用HAProxy主机的Redis-cli进行连接。连接到的服务器的角色是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
Redis客户端连接从应用程序
将 HAProxy 的私有 IP 注册到 /etc/hosts 文件中。
{PUBLIC_IP_ADDRESS} redis-haproxy
设置REDISCLI_AUTH环境变量。该变量对应于在安装了Redis Sentinel的情况下,用户的主目录下创建的deployment-secrets.txt文件中记录的redis密码。
连接到HAProxy。
redisclient:~# redis-cli -h redis-haproxy --tls --cacert redissentinel-tokyo_ca.crt ping
PONG
通过HAProxy,将始终连接到主服务器。
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
停止主服务器
使用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客户端中,从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服务器主节点宕机时,日志和文件是如何记录的。
总结一下
通过使用Redis Sentinel和HAProxy,我们保持了Redis服务器的高可用性,并确保应用程序始终能够连接到Redis的主服务器。
如果将HAProxy配置为高可用性(H/A)模式,可以消除单点故障(SPOF),请参考以下文章。