当前位置: 代码网 > 服务器>服务器>Linux > Redis集群实例分析

Redis集群实例分析

2025年03月30日 Linux 我要评论
一、why k8s1、资源隔离当前的redis cluster部署在物理机集群上,为了提高资源利用率节约成本,多业务线的redis集群都是混布的。由于没有做cpu的资源隔离,经常出现某redis节点c

一、why k8s

1、资源隔离

当前的redis cluster部署在物理机集群上,为了提高资源利用率节约成本,多业务线的redis集群都是混布的。由于没有做cpu的资源隔离,经常出现某redis节点cpu使用率过高导致其他redis集群的节点争抢不到cpu资源引起时延抖动。因为不同的集群混布,这类问题很难快速定位,影响运维效率。k8s容器化部署可以指定 cpu request 和 cpu limit ,在提高资源利用率的同时避免了资源争抢。

2、自动化部署

当前redis cluster在物理机上的部署过程十分繁琐,需要通过查看元信息数据库查找有空余资源的机器,手动修改很多配置文件再逐个部署节点,最后使用redis_trib工具创建集群,新集群的初始化工作经常需要一两个小时。

k8s通过statefulset部署redis集群,使用configmap管理配置文件,新集群部署时间只需要几分钟,大大提高了运维效率。

二、how k8s

客户端通过lvs的vip统一接入,通过redis proxy转发服务请求到redis cluster集群。这里我们引入了redis proxy来转发请求。

redis集群实例分析

1、redis cluster部署方式

redis部署为statefulset,作为有状态的服务,选择statefulset最为合理,可以将节点的rdb/aof持久化到分布式存储中。当节点重启漂移到其他机器上时,可通过挂载的pvc(persistentvolumeclaim)拿到原来的rdb/aof来同步数据。

ceph块服务是我们所选择的持久化存储pv(persistentvolume)。ceph的读写性能较本地硬盘差,会导致读写延迟增加100~200毫秒。分布式存储的读写延迟并没有影响服务,因为redis的rdb/aof写出是异步的。

redis集群实例分析

2、proxy选型

开源的redis proxy有很多,常见的开源redis proxy如下:

我们希望能够继续使用redis cluster来管理redis集群,所以codis和twemproxy不再考虑。redis-cluster-proxy是redis官方在6.0版本推出的支持redis cluster协议的proxy,但是目前还没有稳定版,暂时也无法大规模应用。

备选就只有cerberus和predixy两种。下面是我们在k8s环境中对cerberus和predixy进行的性能测试结果:

测试环境

测试工具: redis-benchmark

proxy cpu: 2 core

client cpu: 2 core

redis cluster: 3 master nodes, 1 cpu per node

测试结果

redis集群实例分析

redis集群实例分析

predixy在相同的工作负载和配置下,能够获得更高的qps,而其延迟也与cerberus相当接近。综合来看,predixy比cerberus的性能要高33%~60%,并且数据的key/value越大,predixy优势越明显,所以最后我们选择了predixy。

为了适应业务和k8s环境,在上线前我们对predixy做了大量的改动,增加了很多新的功能,比如动态切换后端redis cluster、黑白名单、异常操作审计等。

3、proxy部署方式

由于其无状态轻量化的部署特性,使用代理(proxy)作为部署方式,经由负载均衡(lb)提供服务,能够轻松地实现动态的扩容和缩容。同时,我们为proxy开发了动态切换后端redis cluster的功能,可实现在线添加和切换redis cluster。

4、proxy自动扩缩容方式

我们使用k8s原生的hpa(horizontal pod autoscaler)来实现proxy的动态扩缩容。当proxy所有pod的平均cpu使用率超过一定阈值时,会自动触发扩容,hpa会将proxy的replica数加1,之后lvs就会探测到新的proxy pod并将一部分流量切过去。当cpu使用率超过规定的阈值后进行扩容,若仍未达到要求则会持续触发扩容逻辑。但是在扩容成功5分钟内,不论cpu使用率降到多低,都不会触发缩容逻辑,这样就避免了频繁的扩缩容给集群稳定性带来的影响。

hpa可配置集群的最少(minpods)和最多(maxpods)pod数量,集群负载再低也不会缩容到minpods以下数量的pods。建议客户自行判断其实际业务状况以确定minpods和maxpods的取值。

三、why proxy

1、redis pod重启可导致ip变化

使用redis cluster的redis客户端,都需要配置集群的部分ip和port,用于客户端重启时查找redis cluster的入口。对于物理机集群部署的redis节点,即便遇到实例重启或者机器重启,ip和port都可以保持不变,客户端依然能够找到redis cluster的拓扑。但是部署在k8s上的redis cluster,pod重启是不保证ip不变的(即便是重启在原来的k8s node上),这样客户端重启时,就可能会找不到redis cluster的入口。

通过在客户端和redis cluster之间加上proxy,就对客户端屏蔽了redis cluster的信息,proxy可以动态感知redis cluster的拓扑变化,客户端只需要将lvs的ip:port作为入口,请求转发到proxy上,即可以像使用单机版redis一样使用redis cluster集群,而不需要redis智能客户端。

2、redis处理连接负载高

在6.0版本之前,redis都是单线程处理大部分任务的。当redis节点的连接较高时,redis需要消耗大量的cpu资源处理这些连接,导致时延升高。有了proxy之后,大量连接都在proxy上,而proxy跟redis实例之间只保持很少的连接,这样降低了redis的负担,避免了因为连接增加而导致的redis时延升高。

3、集群迁移切换需要应用重启

在使用过程中,随着业务的增长,redis集群的数据量会持续增加,当每个节点的数据量过高时,bgsave的时间会大大延长,降低集群的可用度。同时qps的增加也会导致每个节点的cpu使用率增高。这都需要增加扩容集群来解决。目前redis cluster的横向扩展能力不是很好,原生的slots搬移方案效率很低。新增节点后,有些客户端比如lettuce,会因为安全机制无法识别新节点。另外迁移时间也完全无法预估,迁移过程中遇到问题也无法回退。

当前物理机集群的扩容方案是:

  •  按需创建新集群;

  •  使用同步工具将数据从老集群同步到新集群;

  •  确认数据无误后,跟业务沟通,重启服务切换到新集群。

整个过程繁琐而且风险较大,还需要业务重启服务。

有了proxy层,可以将后端的创建、同步和切换集群对客户端屏蔽掉。新老集群同步完成之后,向proxy发送命令就可以将连接换到新集群,可以实现对客户端完全无感知的集群扩缩容。

4、数据安全风险

redis是通过auth来实现鉴权操作,客户端直连redis,密码还是需要在客户端保存。使用代理,客户端只需要通过代理的密码来访问redis,而不必知道redis的密码。proxy还限制了flushdb、config set等操作,避免了客户误操作清空数据或修改redis配置,大大提高了系统的安全性。

同时,redis并没有提供审计功能。我们已经在代理服务器上添加了高风险操作的日志记录功能,不会影响整体性能的前提下,提供了审计能力。

四、proxy带来的问题

1、多一跳带来的时延

proxy在客户端和redis实例之间,客户端访问redis数据需要先访问proxy再访问redis节点,多了一跳,会导致时延增加。根据测试结果,增加一跳会导致时延增加0.2~0.3ms,但通常对于业务来说这是可接受的。

2、pod漂移造成ip变化

在k8s上,代理(proxy)通过部署(deployment)实现,因此同样存在节点重启导致ip变化的问题。我们k8s的lb方案可以感知到proxy的ip变化,动态的将lvs的流量切到重启后的proxy上。

3、lvs带来的时延

在下表所示的测试中,不同数据长度的get/set操作引入的lvs时延都小于0.1ms。

redis集群实例分析

五、k8s带来的好处

1、部署方便

通过运维平台调用k8s api部署集群,大大提高了运维效率。

2、解决端口管理问题

目前小米在物理机上部署redis实例是通过端口来区分的,并且下线的端口不能复用,也就是说整个公司每个redis实例都有唯一的端口号。目前65535个端口已经用到了40000多,按现在的业务发展速度,将在两年内耗尽端口资源。而通过k8s部署,每一个redis实例对应的k8s pod都有独立的ip,不存在端口耗尽问题和复杂的管理问题。

3、降低客户使用门槛

对应用来说,只需要使用单机版的非智能客户端连接vip,降低了使用门槛,避免了繁琐复杂的参数设置。应用程序无需自行处理redis cluster的拓扑,因为vip和端口是静态固定的。

4、提高客户端性能

使用非智能客户端还可以降低客户端的负载,因为智能客户端需要在客户端对key进行hash以确定将请求发送到哪个redis节点,在qps比较高的情况下会消耗客户端机器的cpu资源。当然,为了让客户端应用迁移更加容易,我们还让proxy支持了智能客户端协议。

5、动态升级和扩缩容

proxy支持动态添加切换redis cluster的功能,这样redis cluster的集群升级和扩容切换过程可以做到对业务端完全无感知。例如,业务方使用30个节点的redis cluster集群,由于业务量的增加,数据量和qps都增长的很快,需要将集群规模扩容两倍。如果在原有的物理机上扩容,需要以下过程:

  •  协调资源,部署60个节点的新集群;

  •  手动配置迁移工具,将当前集群的数据迁移到新集群;

  •  验证数据无误后,通知业务方修改redis cluster连接池拓扑,重启服务。

虽然redis cluster支持在线扩容,但是扩容过程中slots搬移会对线上业务造成影响,同时迁移时间不可控,所以现阶段很少采用这种方式,只有在资源严重不足时才会偶尔使用。

在新的k8s架构下,迁移过程如下:

  •  通过api接口一键创建60个节点的新集群;

  •  同样通过api接口一键创建集群同步工具,将数据迁移到新集群;

  •  验证数据无误后,向proxy发送命令添加新集群信息并完成切换。

整个过程对业务端完全无感知。

集群升级也很方便:如果业务方能接受一定的延迟毛刺,可以在低峰时通过statefulset滚动升级的方式来实现;如果业务对延迟有要求,可以通过创建新集群迁移数据的方式来实现。

6、提高服务稳定性和资源利用率

利用k8s自带的资源隔离功能,使不同类型的应用程序可以混合部署。这不仅可以提高资源利用率,还能确保服务的稳定性。

六、遇到的问题

1、pod重启导致数据丢失

k8s的pod碰到问题重启时,由于重启速度过快,会在redis cluster集群发现并切主前将pod重启。如果pod上的redis是slave,不会造成什么影响。但如果redis是master,并且没有aof,重启后原先内存的数据都被清空,redis会reload之前存储的rdb文件,但是rdb文件并不是实时的数据。之后slave也会跟着把自己的数据同步成之前的rdb文件中的数据镜像,会造成部分数据丢失。

在部署statefulset时,pod名称遵循一定的命名格式,包含固定的编号,因此statefulset是一种有状态服务。我们在初始化redis cluster时,将相邻编号的pod设置为主从关系。在重启pod时,通过pod名确定它的slave,在重启pod前向从节点发送cluster failover命令,强制将活着的从节点切主。这样在重启后,该节点会自动以从节点方式加入集群。

lvs映射时延

proxy的pod是通过lvs实现负载均衡的,lvs对后端ip:port的映射生效有一定的时延,proxy节点突然下线会导致部分连接丢失。为了最小化proxy运维对业务的影响,我们已在proxy的部署模板中添加了以下选项:

lifecycle:    prestop:      exec:        command:        - sleep        - "171"
登录后复制

对于正常的proxy pod下线,例如集群缩容、滚动更新proxy版本以及其它k8s可控的pod下线,在pod下线前会发消息给lvs并等待171秒,这段时间足够lvs将这个pod的流量逐渐切到其他pod上,对业务无感知。

2、k8s statefulset无法满足redis cluster部署要求

k8s原生的statefulset不能完全满足redis cluster部署的要求:

在redis cluster中,不能将具有主备关系的节点部署在同一台机器上。这个很好理解,如果该机器宕机,会导致这个数据分片不可用。

2)redis cluster不允许集群超过一半的主节点失效,因为如果超过一半主节点失效,就无法有足够的节点投票来满足gossip协议的要求。因为redis cluster的主备是可能随时切换的,我们无法避免同一个机器上的所有节点都是主节点这种情况,所以在部署时不能允许集群中超过1/4的节点部署在同一台机器上。

为了满足上面的要求,原生statefulset可以通过 anti-affinity 功能来保证相同集群在同一台机器上只部署一个节点,但是这样机器利用率很低。

因此我们开发了基于statefulset的crd:redisstatefulset,会采用多种策略部署redis节点。在redisstatefulset中添加了一些用于管理redis的功能。这些我们将会在其他文章中来继续详细探讨。

七、总结

数十个redis集群已经在k8s上部署并运行了六个月以上,这些集群涉及到集团内部的多个业务。得益于k8s的快速部署和故障迁移能力,这些集群的运维工作量比物理机上的redis集群低很多,稳定性也得到了充分的验证。

在运维过程中我们也遇到了不少问题,文章中提到的很多功能都是根据实际需求提炼出来的。在接下来的过程中,仍需要逐步解决许多问题,以提升资源利用效率和服务质量。

1、混布 vs. 独立部署

物理机的redis实例是独立部署的,单台物理机上部署的都是redis实例,这样有利于管理,但是资源利用率并不高。redis实例使用了cpu、内存和网络io,但存储空间基本都是浪费的。在k8s上部署redis实例,其所在的机器上可能也会部署其他任意类型的服务,这样虽然可以提高机器的利用率,但是对于redis这样的可用性和时延要求都很高的服务来说,如果因为机器内存不足而被驱逐,是不能接受的。这就需要运维人员监控所有部署了redis实例的机器内存,一旦内存不足,就切主和迁移节点,但这样又增加运维的工作量。

如果混合部署中还有其他高网络吞吐量的应用程序,那么也可能会对redis服务产生负面影响。虽然k8s的anti-affinity功能可以将redis实例有选择地部署到没有这类应用的机器上,但是在机器资源紧张时,还是无法避免这种情况。

2、redis cluster管理

redis cluster是一个p2p无中心节点的集群架构,依靠gossip协议传播协同自动化修复集群的状态,节点上下线和网络问题都可能导致redis cluster的部分节点状态出现问题,例如会在集群拓扑中出现failed或者handshake状态的节点,甚至脑裂。对这种异常状态,我们可以在redis crd上增加更多的功能来逐步解决,进一步提高运维效率。

3、审计与安全

redis仅仅提供了auth密码认证保护功能,缺乏权限管理,因此安全性相对较低。通过proxy,我们可以通过密码区分客户端类型,管理员和普通用户使用不同的密码登录,可执行的操作权限也不同,这样就可以实现权限管理和操作审计等功能。

4、支持多redis cluster

单个redis cluster由于gossip协议的限制,横向扩展能力有限,集群规模在300个节点时,节点选主这类拓扑变更的效率就明显降低。同时,由于单个redis实例的容量不宜过高,单个redis cluster也很难支持tb以上的数据规模。通过proxy,我们可以对key做逻辑分片,这样单个proxy就可以接入多个redis cluster,从客户端的视角来看,就相当于接入了一个能够支持更大数据规模的redis集群。

以上就是redis集群实例分析的详细内容,更多请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com