公司之前生产环境一直用的是单节点的Redis,生产环境版本是6.2.6,开发需要使用PEXPIRETIME命令,这个命令是7.0版本之后才有的,所以趁着这个机会,升级Redis版本顺便把单节点转成集群。
说明
k8s部署redis集群
- 推荐7.0+版本,这里是7.4.0,因为当使用 redis-cli 7.0前的版本组建集群时只能使用ip端口,而不能使用pod的名称
- 由于redis重启pod的ip会变化,集群会失效,k8s环境需要配置
cluster-announce-ip
通告地址,否则某些场景集群会失败
看过网上一些k8s部署redis集群的文章在某些场景下多少都有点问题,比如升级、不正当重启等情况都可能导致集群失效,容易踩坑
本例中部署6节点3主3从的集群。对升级、重启、高可用都进行了测试,均正常
要求
- 使用nfs作为redis的存储,需要准备好存储类,本例是
nfs-client
- 本例操作都在test命名空间,请改成你自己的命名空间
- redis的密码这里为
hello_redis@234
,请改成你自己的
部署
准备redis-cluster-sts.yaml
,如下内容
1 | # 1. redis的无头服务 |
创建上面的资源
1 | kubectl -n test apply -f redis-cluster-sts.yaml |
查看pod已经起来了
1 | # kubectl -n midd get pod |
开始使用redis-cli创建集群,会组建3主3从的集群,然后分配槽位。会提示你输入yes
就行
1 | redis-cli这里使用6个pod的短域名建集群,即pod名称.svc名称 |
查看集群状态、测试数据读写,进入任意一个pod执行
1 | # kubectl -n test exec -it redis-cluster-sts-0 -- bash |
每个节点都有noeds.conf
文件,包含了集群节点的主从关系,它是redis自己维护的,一般不需要去修改。它们内容应该是一致的(排序可能不一样),如果不一致说明集群有问题,需要排查
1 | # kubectl -n test exec -it redis-cluster-sts-0 -- cat /data/nodes.conf |
客户端如何连接
集群内其它pod可以通过 svc或pod的FQDN访问
- 通过svc访问
1 | redis-cluster-headless:6379 # 同一个命名空间访问,短域名 |
- 通过pod的FQDN访问,6个节点
1 | # 同一个命名空间访问,短域名 |
推荐第2种,写6个节点的主机名与端口,客户端的库保证高可用
如:nacos配置可以修改成如下:
1 | redis: |
各种测试
对redis集群进行 缩容、升级、重启,验证redis集群是否正常
重启
重启redis集群。如下可以看到集群正常
1 | # kubectl -n test rollout restart sts redis-cluster-sts |
另外一种重启,先缩容为0个,然后扩容为6,可以看到集群依然正常
1 | kubectl -n test scale statefulset redis-cluster-sts --replicas 0 && sleep 30 |
缩容
- 现在6个节点,缩容为5个,这样模拟一个节点故障,可以看到集群正常
1 | # kubectl -n test scale statefulset redis-cluster-sts --replicas 5 |
升级
将redis版本升级,集群是否正常?在本例中将7.4.0
升级到7.4.1
1 | kubectl -n test set image sts redis-cluster-sts redis-cluster=docker-0.unsee.tech/redis:7.4.1 |
同理 查看集群状态,数据可以读写表示正常
1 | 127.0.0.1:6379> CLUSTER INFO # 状态正常 |
其它
k8s中部署redis集群最重要的就是配置--cluster-announce-ip
通告地址,因为pod的变化的,重启后pod地址变化了导致集群失败
- 强烈建议配置
--cluster-announce-ip
为 pod的FQDN如下
1 | env: |
- 不建议是pod的ip
1 | env: |
这也是很多网上这样配置的。这个配置在 reids 升级(换镜像)、缩容为0在扩容为6时 集群失效,特别要注意
所以推荐第1种方式,但是需要7.0x或以上的版本。
至此,Redis三主三从集群就部署完成了。
数据迁移
使用Redis-shake迁移
介绍
redis-shake是阿里云Redis&MongoDB团队开源的用于redis数据同步的工具,用于在两个 redis之 间同步数据的工具,满足用户非常灵活的同步、迁移需求。
基本功能
redis-shake是阿里基于redis-port基础上进行改进的一款产品。它支持解析、恢复、备份、同步四个功能。以下主要介绍同步sync。
恢复restore:将RDB文件恢复到目的redis数据库。
备份dump:将源redis的全量数据通过RDB文件备份起来。
解析decode:对RDB文件进行读取,并以json格式解析存储。
同步sync:支持源redis和目的redis的数据同步,支持全量和增量数据的迁移,支持从云下到阿里云云上的同步,也支持云下到云下不同环境的同步,支持单节点、主从版、集群版之间的互相同步。需要注意的是,如果源端是集群版,可以启动一个RedisShake,从不同的db结点进行拉取,同时源端不能开启move slot功能;对于目的端,如果是集群版,写入可以是1个或者多个db结点。
同步rump:支持源redis和目的redis的数据同步,仅支持全量的迁移。采用scan和restore命令进行迁移,支持不同云厂商不同redis版本的迁移。
基本原理
redis-shake的基本原理就是模拟一个从节点加入源redis集群,首先进行全量拉取并回放,然后进行增量的拉取(通过psync命令)。如下图所示:

如果源端是集群模式,只需要启动一个redis-shake进行拉取,同时不能开启源端的move slot操作。如果目的端是集群模式,可以写入到一个结点,然后再进行slot的迁移,当然也可以多对多写入。
目前,redis-shake到目的端采用单链路实现,对于正常情况下,这不会成为瓶颈,但对于极端情况,qps比较大的时候,此部分性能可能成为瓶颈,后续我们可能会计划对此进行优化。另外,redis-shake到目的端的数据同步采用异步的方式,读写分离在2个线程操作,降低因为网络时延带来的同步性能下降。
迁移
安装redis-shake
1 | [root]# wget https://github.com/alibaba/RedisShake/releases/download/release-v220210903/release-v2 -20210903.tar.gz - |
配置参数文件
配置redis-shake.conf参数文件(修改的部分)
1 | [root]# cd release-v2.1.1-20210903 |
迁移
启动redis-shake
1 | [root]# ./redis-shake.linux -type=sync -conf=redis-shake.conf |
redis-full-check校验工具
简介
redis-full-check是阿里云Redis&MongoDB团队开源的用于校验2个redis数据是否一致的工具,通常用于redis数据迁移(redis-shake)后正确性的校验。
支持:单节点、主从版、集群版、带proxy的云上集群版(阿里云)之间的同构或者异构对比,版本支持2.x-5.x。
基本原理
下图是基本的逻辑比较:

redis-full-check通过全量对比源端和目的端的redis中的数据的方式来进行数据校验,其比较方式通过多轮次比较:每次都会抓取源和目的端的数据进行差异化比较,记录不一致的数据进入下轮对比。然后通过多伦比较不断收敛,减少因数据增量同步导致的源库和目的库的数据不一致。最后sqlite中存在的数据就是最终的差异结果。
redis-full-check对比的方向是单向:抓取源库A的数据,然后检测是否位于B中,反向不会检测,也就是说,它检测的是源库是否是目的库的子集。如果希望对比双向,则需要对比2次,第一次以A为源库,B为目的库,第二次以B为源库,A为目的库。
下图是基本的数据流图,redis-full-check内部分为多轮比较,也就是黄色框所指示的部分。每次比较,会先抓取比较的key,第一轮是从源库中进行抓取,后面轮次是从sqlite3 db中进行抓取;抓取key之后是分别抓取key对应的field和value进行对比,然后将存在差异的部分存入sqlite3 db中,用于下次比较。

不一致类型
redis-full-check判断不一致的方式主要分为2类:key不一致和value不一致。
key不一致
key不一致主要分为以下几种情况:
- lack_target : key存在于源库,但不存在于目的库。
- type: key存在于源库和目的库,但是类型不一致。
- value: key存在于源库和目的库,且类型一致,但是value不一致。
value不一致
不同数据类型有不同的对比标准:
- string: value不同。
- hash: 存在field,满足下面3个条件之一:
- field存在于源端,但不存在与目的端。
- field存在于目的端,但不存在与源端。
- field同时存在于源和目的端,但是value不同。
- set/zset:与hash类似。
- list: 与hash类似。
field冲突类型有以下几种情况(只存在于hash,set,zset,list类型key中):
- lack_source: field存在于源端key,field不存在与目的端key。
- lack_target: field不存在与源端key,field存在于目的端key。
- value: field存在于源端key和目的端key,但是field对应的value不同。
比较原理
对比模式(comparemode)有三种可选:
- KeyOutline:只对比key值是否相等。
- ValueOutline:只对比value值的长度是否相等。
- FullValue:对比key值、value长度、value值是否相等。
对比会进行comparetimes轮(默认comparetimes=3)比较:
- 第一轮,首先找出在源库上所有的key,然后分别从源库和目的库抓取进行比较。
- 第二轮开始迭代比较,只比较上一轮结束后仍然不一致的key和field。
- 对于key不一致的情况,包括lack_source ,lack_target 和type,从源库和目的库重新取key、value进行比较。
- value不一致的string,重新比较key:从源和目的取key、value比较。
- value不一致的hash、set和zset,只重新比较不一致的field,之前已经比较且相同的filed不再比较。这是为了防止对于大key情况下,如果更新频繁,将会导致校验永远不通过的情况。
- value不一致的list,重新比较key:从源和目的取key、value比较。
- 每轮之间会停止一定的时间(Interval)。
对于hash,set,zset,list大key处理采用以下方式:
- len <= 5192,直接取全量field、value进行比较,使用如下命令:hgetall,smembers,zrange 0 -1 withscores,lrange 0 -1。
- len > 5192,使用hscan,sscan,zscan,lrange分批取field和value。
校验
安装
GitHub:https://github.com/alibaba/RedisFullCheck
安装redis-full-check
1 | [root]# wget https://github.com/alibaba/RedisFullCheck/releases/download/release-v1.4.8-20200212/redis-full-check-1.4.8.tar.gz |
参数解释:
1 | -s, --source=SOURCE 源redis库地址(ip:port),如果是集群版,那么需要以分号(;)分割不同的db,只需要配置主或者从的其中之一。例如:10.1.1.1:1000;10.2.2.2:2000;10.3.3.3:3000。 |
校验
校验源端与目的端键值对
1 | [root]# ./redis-full-check --source=10.150.57.9:6379 --sourcepassword= --sourcedbtype=0 --target="10.150.57.13:6381;10.150.57.13:6382;10.150.57.13:6383" --targetpassword=Gaoyu@029 --targetdbtype=1 --comparemode=1 --qps=10 --batchcount=1000 --parallel=10 |
校验完毕,没有键冲突。
Redis集群监控
redis-exporter安装
redis-cluster-exporter.yaml:
1 | apiVersion: apps/v1 |
查看redis-exporter容器日志:

添加Prometheus配置
1 | - job_name: 'redis-cluster-exporter-uat_target' |
在Prometheus查看是否有数据收集:

创建Grafana可视化数据
导入仪表盘,id为763:
此时的仪表盘还不可以按集群和角色去筛选数据,需要修改下变量:

修改完成保存即可:

或者使用json,创建一个仪表盘:
1 | { |
