文章目录
- 前言
- 技术积累
- 什么是Redis的哨兵模式
- 哨兵的三个任务
- 哨兵选主流程
- 哨兵模式存在的问题
- 实战演示
- 部署环境
- 创建目录
- 编写Redis配置文件
- 编写哨兵配置文件
- 编写Docker-Compose.yml
- 启动Redis Sentinel容器
- 执行yml文件
- 查看容器状态
- 查看Redis各个角色状态
- 查看主节点信息
- 查看从节点信息
- 查看哨兵的信息
- 主从复制验证
- 验证sentinel自动故障转移
- 1、查看当前主节点
- 2、停止主节点
- 3、查看是否故障转移,是否选除了主节点
- 4、继续验证slave2角色
- 5、启动redis-master
- 6、查看主节点是否加入集群且为从库角色
- 总结
前言
Redis 是一个高性能的键值存储系统,常用于缓存、消息队列等场景。为了保证高可用性,Redis 提供了哨兵模式(Sentinel)来实现主从切换和故障转移。本文将介绍如何使用 docker-compose 快速搭建 Redis 主从 + 哨兵集群环境。
技术积累
什么是Redis的哨兵模式
Redis的哨兵模式,就是在主从模式的基础上,额外部署若干独立的哨兵进程,通过哨兵进程去监视者Redis主从节点的状态,一旦发现主节点宕机,则哨兵可以重新从剩余slave节点中推选一个新的节点并将其升级为master节点,以此保证整个系统功能可以正常使用。
哨兵的三个任务
哨兵负责三个任务:监控,选主和通知。
- 监控:监控是指哨兵进程运行时,周期性(默认1秒)给所有主从节点发送 PING 命令,当主从节点收到 PING 命令后,会发送一个响应命令给哨兵,这样就可以检测他们是否仍然在线运行。
- 从库没有在规定时间内响应哨兵的PING命令,哨兵就会把它标记为"下线状态";
- 主库没有在规定时间呢响应哨兵的PING命令,哨兵就会判定主库下线启动选主流程。
- 选主:哨兵在主库挂了以后,按照一定规则从从库中选出作为新的主库。
- 通知:哨兵将选出的新主库连接信息发给其他从库,从库和新主库建立连接,执行replicaof命令,复制数据。同时,哨兵会把新主库的连接信息通知给客户端,让它们将操作请求发送给新主库上。
哨兵选主流程
哨兵机制具体步骤如下:
1、哨兵集群第一轮投票:判断主节点客观下线;
2、哨兵集群第二轮投票:选出哨兵leader,决定由哪个哨兵执行主从切换;
3、由哨兵 leader 进行选主;
4、由哨兵 leader 进行通知,实现主从故障转移。
哨兵模式存在的问题
在主从复制的基础上,哨兵引入了主节点的自动故障转移,进一步提高了Redis的高可用性;但是哨兵的缺陷同样很明显:
1、哨兵无法对从节点进行自动故障转移,在读写分离场景下,从节点故障会导致读服务不可用;
2、哨兵仍然没有解决写操作无法负载均衡、存储能力受到单机限制的问题,如果需要解决这些问题就是使用Redis Cluster集群。
实战演示
部署环境
Redis哨兵模式,一主二从三哨兵
Redis主节点(6379)
Redis从节点1(6380)
Redis从节点2(6381)
Redis哨兵1(26379)
Redis哨兵2(26380)
Redis哨兵3(26381)
创建目录
编写Redis配置文件
- redis-master
# 主节点配置
# 绑定的主机地址
bind 0.0.0.0
# 允许外网访问
protected-mode no
# 启用守护进程后,Redis会把pid写到一个pidfile中,在/var/run/redis.pid
daemonize no
# 当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定
pidfile /var/run/redis.pid
# 指定Redis监听端口,默认端口为6379
# 如果指定0端口,表示Redis不监听TCP连接
port 6379
# 当客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能
timeout 0
# 指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为verbose
# debug (很多信息, 对开发/测试比较有用)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
loglevel verbose
# 日志记录方式,默认为标准输出,如果配置为redis为守护进程方式运行,而这里又配置为标准输出,则日志将会发送给/dev/null
logfile redis.log################################ SNAPSHOTTING #################################
# RDB存储配置
# 指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
# Save the DB on disk:
#
# save <seconds> <changes>
#
# Will save the DB if both the given number of seconds and the given
# number of write operations against the DB occurred.
#
# 满足以下条件将会同步数据:
# 900秒(15分钟)内有1个更改
# 300秒(5分钟)内有10个更改
# 60秒内有10000个更改
# Note: 可以把所有“save”行注释掉,这样就取消同步操作了
save 900 1
save 300 10
save 60 10000
# 指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大
rdbcompression yes
# 指定本地数据库文件名,默认值为dump.rdb
dbfilename dump.rdb
# 指定本地数据库存放目录,文件名由上一个dbfilename配置项指定
dir /data################################# REPLICATION ################################################################### SECURITY ###################################
# 设置密码
requirepass 123456789############################## APPEND ONLY MODE ###############################
# 开启aof配置
appendonly yes
# 指定更新日志条件,共有3个可选值:
# no:表示等操作系统进行数据缓存同步到磁盘(快)
# always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全)
# everysec:表示每秒同步一次(折衷,默认值)
appendfsync everysec
# 指定更新日志文件名,默认为appendonly.aof
appendfilename "appendonly.aof"
- redis-slave1
# 从节点配置
# 绑定的主机地址
bind 0.0.0.0
# 允许外网访问
protected-mode no
# 启用守护进程后,Redis会把pid写到一个pidfile中,在/var/run/redis.pid
daemonize no
# 当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定
pidfile /var/run/redis.pid
# 指定Redis监听端口,默认端口为6379
# 如果指定0端口,表示Redis不监听TCP连接
port 6379
# 当客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能
timeout 0
# 指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为verbose
# debug (很多信息, 对开发/测试比较有用)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
loglevel verbose
# 日志记录方式,默认为标准输出,如果配置为redis为守护进程方式运行,而这里又配置为标准输出,则日志将会发送给/dev/null
logfile redis.log################################ SNAPSHOTTING #################################
# RDB存储配置
# 指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
# Save the DB on disk:
#
# save <seconds> <changes>
#
# Will save the DB if both the given number of seconds and the given
# number of write operations against the DB occurred.
#
# 满足以下条件将会同步数据:
# 900秒(15分钟)内有1个更改
# 300秒(5分钟)内有10个更改
# 60秒内有10000个更改
# Note: 可以把所有“save”行注释掉,这样就取消同步操作了
save 900 1
save 300 10
save 60 10000
# 指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大
rdbcompression yes
# 指定本地数据库文件名,默认值为dump.rdb
dbfilename dump.rdb
# 指定本地数据库存放目录,文件名由上一个dbfilename配置项指定
dir /data################################# REPLICATION #################################
# 设置当本机为slav服务时,设置master服务的ip地址及端口,在Redis启动时,它会自动从master进行数据同步
replicaof redis-master 6379
# 开启只读模式
replica-read-only yes
# 当master服务设置了密码保护时,slav服务连接master的密码
masterauth 123456789repl-diskless-load on-empty-db################################## SECURITY ###################################
# 设置密码
requirepass 123456789############################## APPEND ONLY MODE ###############################
# 开启aof配置
appendonly yes
# 指定更新日志条件,共有3个可选值:
# no:表示等操作系统进行数据缓存同步到磁盘(快)
# always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全)
# everysec:表示每秒同步一次(折衷,默认值)
appendfsync everysec
# 指定更新日志文件名,默认为appendonly.aof
appendfilename "appendonly.aof"
- redis-slave2 与slave1一致即可
编写哨兵配置文件
- reids-sentinel1.conf
# 服务端口
port 26379
# 可访问IP
bind 0.0.0.0
# By default Redis Sentinel does not run as a daemon. Use 'yes' if you need it.
daemonize no# When running daemonized, Redis Sentinel writes a pid file in
# /var/run/redis-sentinel.pid by default. You can specify a custom pid file
# location here.
pidfile /var/run/redis-sentinel.pid# Specify the log file name. Also the empty string can be used to force
# Sentinel to log on the standard output. Note that if you use standard
logfile "sentinel.log"# dir <working-directory>
dir /tmp# 监控的主机(格式为 sentinel monitor 主机名 主机ip 主机端口 多少个哨兵节点都认为主节点不可达时才会进行故障转移的数量)
sentinel monitor redis-master 172.16.8.212 6379 2# 监控的主机密码(格式为 sentinel auth-pass 主机名 主机连接密码)
sentinel auth-pass redis-master 123456789# 指定在多少毫秒内,哨兵节点没有收到主节点的响应时,就会将主节点判定为下线状态(格式为 sentinel down-after-milliseconds >主机名 超时毫秒数)
sentinel down-after-milliseconds redis-master 30000# ACL LOG
acllog-max-len 128# 指定在进行故障转移时可以同时同步的从节点数量(格式为 sentinel parallel-syncs 主机名 主机连接密码)
sentinel parallel-syncs redis-master 1# 指定进行故障转移的超时时间,即在多少毫秒内完成故障转移操作(格式为 sentinel failover-timeout 主机名 主机连接密码)
sentinel failover-timeout redis-master 180000# SECURITY
# By default SENTINEL SET will not be able to change the notification-script
# and client-reconfig-script at runtime. This avoids a trivial security issue
# where clients can set the script to anything and trigger a failover in order
# to get the program executed.sentinel deny-scripts-reconfig yes# You may enable hostnames support by enabling resolve-hostnames. Note
# that you must make sure your DNS is configured properly and that DNS
# resolution does not introduce very long delays.
SENTINEL resolve-hostnames no# When resolve-hostnames is enabled, Sentinel still uses IP addresses
# when exposing instances to users, configuration files, etc. If you want
# to retain the hostnames when announced, enable announce-hostnames below.
SENTINEL announce-hostnames no
- redis-sentinel2.conf 复制一份sentinel1
- redis-sentinel3.conf 复制一份sentinel1
编写Docker-Compose.yml
version: '3.8'services:redis-master:image: redis:6-alpinecontainer_name: redis-masterenvironment:- "TZ=Asia/Shanghai"ports:- "6379:6379"volumes:- ./redis-master/conf/redis.conf:/usr/local/etc/redis/redis.conf- ./redis-master/data:/data- ./redis-master/logs:/logscommand: redis-server /usr/local/etc/redis/redis.confnetworks:- redis-sentinelredis-slave1:image: redis:6-alpinecontainer_name: redis-slave1environment:- "TZ=Asia/Shanghai"ports:- "6380:6379"volumes:- ./redis-slave1/conf/redis.conf:/usr/local/etc/redis/redis.conf- ./redis-slave1/data:/data- ./redis-slave1/logs:/logscommand: redis-server /usr/local/etc/redis/redis.confdepends_on:- redis-masternetworks:- redis-sentinelredis-slave2:image: redis:6-alpinecontainer_name: redis-slave2environment:- "TZ=Asia/Shanghai"ports:- "6381:6379"volumes:- ./redis-slave2/conf/redis.conf:/usr/local/etc/redis/redis.conf- ./redis-slave2/data:/data- ./redis-slave2/logs:/logscommand: redis-server /usr/local/etc/redis/redis.confdepends_on:- redis-masternetworks:- redis-sentinelredis-sentinel1:image: redis:6-alpinecontainer_name: redis-sentinel1environment:- "TZ=Asia/Shanghai" ports:- '26379:26379'volumes:- ./redis-sentinel1/data:/data- ./redis-sentinel1/conf/sentinel.conf:/usr/local/etc/redis/sentinel.conf- ./redis-sentinel1/logs:/logscommand: redis-sentinel /usr/local/etc/redis/sentinel.confdepends_on: - redis-master- redis-slave1- redis-slave2networks:- redis-sentinelredis-sentinel2:image: redis:6-alpinecontainer_name: redis-sentinel2environment:- "TZ=Asia/Shanghai"ports:- '26380:26379'volumes:- ./redis-sentinel2/data:/data- ./redis-sentinel2/conf/sentinel.conf:/usr/local/etc/redis/sentinel.conf- ./redis-sentinel2/logs:/logscommand: redis-sentinel /usr/local/etc/redis/sentinel.confdepends_on: - redis-master- redis-slave1- redis-slave2networks:- redis-sentinelredis-sentinel3:image: redis:6-alpinecontainer_name: redis-sentinel3environment:- "TZ=Asia/Shanghai"ports:- '26381:26379'volumes:- ./redis-sentinel3/data:/data- ./redis-sentinel3/conf/sentinel.conf:/usr/local/etc/redis/sentinel.conf- ./redis-sentinel3/logs:/logscommand: redis-sentinel /usr/local/etc/redis/sentinel.confdepends_on: - redis-master- redis-slave1- redis-slave2networks:- redis-sentinelnetworks:redis-sentinel:driver: bridge
启动Redis Sentinel容器
执行yml文件
docker-compose -f docker-compose.yaml up -d
查看容器状态
docker ps -a
可以看到所有容器都已经成功启动了
查看Redis各个角色状态
- info replication 查看Redis节点信息
- info sentinel 查看Redis哨兵信息
查看主节点信息
docker exec -it redis-master redis-cli -h 127.0.0.1 -p 6379 -a 123456789 info replication
列出了其两个从节点信息
查看从节点信息
docker exec -it redis-slave1 redis-cli -h 127.0.0.1 -p 6379 -a 123456789 info replication
可以看到当前从节点信息
查看哨兵的信息
进入sentinel容器查看
docker exec -it redis-sentinel1 redis-cli -h 127.0.0.1 -p 26379 info sentinel
可以看到主节点信息、状态和Redis的配置详情(两个从节点,三个哨兵)
主从复制验证
写入数据到主节点
docker exec -it redis-master redis-cli -h 127.0.0.1 -p 6379 -a 123456789 SET name "test_senfel_data"
主节点读取数据查看是否设置成功
docker exec -it redis-master redis-cli -h 127.0.0.1 -p 6379 -a 123456789 GET name
从节点1读取数据,查看是否同步
docker exec -it redis-slave1 redis-cli -h 127.0.0.1 -p 6379 -a 123456789 GET name
从节点2读取数据,查看是否同步
docker exec -it redis-slave2 redis-cli -h 127.0.0.1 -p 6379 -a 123456789 GET name
验证sentinel自动故障转移
1、查看当前主节点
docker exec -it redis-sentinel1 redis-cli -h 127.0.0.1 -p 26379 info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=redis-master,status=ok,address=172.16.8.212:6379,slaves=2,sentinels=3
主节点是 redis-master
2、停止主节点
docker stop redis-master
D:\A-senfel\docker-data\redis\redis-sentinel>docker stop redis-master
redis-master
3、查看是否故障转移,是否选除了主节点
docker exec -it redis-sentinel1 redis-cli -h 127.0.0.1 -p 26379 info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=redis-master,status=ok,address=172.20.0.3:6379,slaves=2,sentinels=3
发现master地址已经变为了slave2的内网地址。
4、继续验证slave2角色
docker exec -it redis-slave2 redis-cli -h 127.0.0.1 -p 6379 -a 123456789 info replication
可以确定已经自动故障转移成功,master切换到slave2节点。
5、启动redis-master
docker start redis-master
D:\A-senfel\docker-data\redis\redis-sentinel>docker start redis-master
redis-master
6、查看主节点是否加入集群且为从库角色
docker exec -it redis-master redis-cli -h 127.0.0.1 -p 6379 -a 123456789 info replication
如上所述,重启后的库已将加入集群。
总结
通过 docker-compose 可以快速搭建 Redis 哨兵模式,适用于开发、测试及轻量级生产环境。该方案具备的优点是易于部署和维护、支持自动故障转移、数据多副本,提高可用性。但是哨兵无法对从节点进行自动故障转移,在读写分离场景下,从节点故障会导致读服务不可用,这就需要对从节点做额外的监控、切换操作。另外,哨兵仍然没有解决写操作无法负载均衡、存储能力受到单机限制的问题。如需进一步提升性能与稳定性,需要考虑使用 Redis Cluster 模式,这个我们下回分享,敬请鉴赏。