当前位置: 首页 > 科技观察

运维:终于不用再背上万实例的Redis集群了!_0

时间:2023-03-21 20:37:52 科技观察

背??景小米的Redis被大规模使用。现在已经有数万个实例,每天有数万亿的访问量,支持几乎所有的产品线和生态链公司。此前,Redis全部部署在物理机上,没有资源隔离,给管理和治理带来了很大的困难。我们的运维人员工作压力很大,机器宕机和网络抖动导致的Redis节点下线往往需要人工干预。因为没有CPU资源隔离,slave节点使用RDB或者由于流量突然增加导致节点QPS突然增加导致节点CPU使用率增加,可能影响集群的节点或者其他集群,导致不可预测的延误。.Redis分片方式采用社区的RedisCluster协议,集群独立分片。RedisCluster在带来一定易用性的同时,也提高了应用开发的门槛。应用开发者需要对RedisCluster有一定的了解,同时需要使用智能客户端来访问RedisCluster。这些智能客户端的配置参数很多,应用开发者无法完全掌握和设置这些参数,踩了很多坑。同时,由于智能客户端需要做分片计算,也给应用端的机器带来了一定的负载。1、为什么是K8s1。资源隔离目前RedisCluster是部署在物理机集群上。为了提高资源利用率和节约成本,多个业务线的Redis集群采用混合分布的方式。由于没有隔离CPU资源,经常会出现某个Redis节点CPU占用率过高,导致其他Redis集群节点无法竞争CPU资源,造成延迟抖动。由于不同集群混合分布,此类问题难以快速定位,影响运维效率。K8s容器化部署可以指定CPU请求和CPU限制,避免资源争用,提高资源利用率。2、自动化部署目前RedisCluster在物理机上的部署过程非常繁琐。需要查看元信息库,找到有空闲资源的机器,手动修改很多配置文件,然后一个一个部署节点。最后,使用redis_trib工具创建集群并初始化新集群工作通常需要一两个小时。K8s通过StatefulSet部署Redis集群,使用configmap管理配置文件。新集群的部署时间仅需几分钟,大大提高了运维效率。2、K8s客户端如何通过LVSVIP接入,通过RedisProxy将服务请求转发给RedisCluster。这里我们介绍RedisProxy来转发请求。1、RedisCluster部署方式Redis部署为一个StatefulSet。作为有状态服务,选择StatefulSet是最合理的,它可以将节点的RDB/AOF持久化到分布式存储中。当节点重启漂移到其他机器时,可以通过挂载的PVC(PersistentVolumeClaim)获取原来的RDB/AOF来同步数据。我们选择的持久化存储PV(PersistentVolume)是CephBlockService。Ceph的读写性能低于本地磁盘,会带来100~200ms的读写延迟。但是由于Redis的RDB/AOF写入都是异步的,分布式存储带来的读写延迟对服务没有影响。2.Proxy的选择开源的RedisProxies有很多。常见的开源RedisProxies如下:我们希望继续使用RedisCluster来管理Redis集群,所以不再考虑Codis和Twemproxy。redis-cluster-proxy是Redis在6.0版本正式推出的,支持RedisCluster协议。不过目前还没有稳定的版本,暂时还不能大规模应用。唯一的选择是Cerberus和Predixy。我们在K8s环境下对Cerberus和Predix进行了性能测试,结果如下:测试环境测试工具:redis-benchmarkProxyCPU:2coreClientCPU:2coreRedisCluster:3个master节点,每个节点1个CPU测试结果在同样的工作负载和配置下,Predixy的最高QPS要好于Cerberus,延迟也比较接近。综合来看,Predixy的性能比Cerberus提升了33%~60%,而且数据的key/value越大,Predixy的优势越明显,所以我们最终选择了Predixy。为了适应业务和K8s环境,我们在Predix上线前做了很多改动,增加了很多新的功能,比如动态切换后端RedisCluster、黑白名单、异常操作审计等。3.ProxyDeploymentModeProxy部署为deployment,无状态,轻量级。通过LB对外提供服务,易于实现动态扩缩容。同时我们为Proxy开发了动态切换后端RedisCluster的功能,可以在线添加和切换RedisCluster。4、Proxy自动扩缩容方式我们使用K8s原生的HPA(Horizo??ntalPodAutoscaler)来实现Proxy的动态扩缩容。当所有Proxypod的平均CPU使用率超过一定阈值时,会自动触发扩容。HPA会将Proxy副本数增加1,然后LVS会检测到新的Proxypod并切断部分流量。如果扩容后CPU使用率仍然超过指定阈值,则继续触发扩容逻辑。但是在扩容成功后的5分钟内,无论CPU使用率降到多低,都不会触发伸缩逻辑,从而避免了频繁伸缩对集群稳定性的影响。HPA可以配置集群中Pod的最小(MINPODS)和最大(MAXPODS)数量。无论集群负载多低,都不会收缩到MINPODS以下的Pod数量。建议客户根据自己的实际业务情况确定MINPODS和MAXPODS的取值。3、为什么Proxy1、Redispod重启会导致IP变化使用RedisCluster的Redis客户端需要配置集群的部分IP和Port,以便在客户端重启时找到RedisCluster的入口。对于物理机集群部署的Redis节点,即使实例重启或者机器重启,IP和Port都可以保持不变,客户端仍然可以找到RedisCluster的拓扑结构。但是对于部署在K8s上的RedisCluster,重启pod并不能保证IP不变(即使是在原来的K8s节点上重启),所以当客户端重启的时候,可能会找不到入口Redis集群。通过在客户端和RedisCluster之间增加一个Proxy,从而对客户端屏蔽RedisCluster的信息。Proxy可以动态感知RedisCluster的拓扑变化。客户端只需要以LVS的IP:Port为入口,将请求转发到Proxy上,就可以像单机版Redis一样使用RedisCluster集群,不需要Redis智能客户端。2.Redis处理高连接负载在6.0版本之前,Redis在单线程中处理大部分任务。当Redis节点的连接数很高时,Redis需要消耗大量的CPU资源来处理这些连接,导致延迟增加。有了Proxy,大量的连接都在Proxy上,Proxy和Redis实例之间只维护很少的连接,减轻了Redis的负担,避免了连接增加导致的Redis延迟增加。3、集群迁移切换需要重启应用。在使用过程中,随着业务的增长,Redis集群的数据量会不断增加。当每个节点的数据量过大时,BGSAVE时间会大大延长,降低集群的可用性。花费。同时QPS的提升也会增加各节点的CPU占用率。这些都需要通过增加扩展集群来解决。目前RedisCluster的水平扩展能力不是很好,原生的slots迁移方案效率很低。添加节点后,一些客户端,如Lettuce,由于安全机制的原因,无法识别新节点。此外,迁移时间完全不可预测,迁移过程中遇到的问题无法回滚。目前物理机集群的扩容计划是:按需创建新集群;使用同步工具将数据从旧集群同步到新集群;确认数据无误后,与业务沟通,重启服务,切换到新集群。整个过程繁琐且风险大,需要业务重启服务。有了Proxy层,后端集群的创建、同步和切换都可以对客户端屏蔽。新旧集群同步完成后,可以通过向Proxy发送命令将连接切换到新集群,实现客户端完全感知不到的集群扩缩容。4、数据安全风险Redis通过AUTH实现鉴权。客户端直接连接Redis,密码还是需要保存在客户端。使用Proxy,客户端只需要通过Proxy密码访问Proxy,不需要知道Redis密码。Proxy还对FLUSHDB、CONFIGSET等操作进行了限制,避免了客户误操作清除数据或修改Redis配置,大大提高了系统的安全性。同时,Redis不提供审计功能。我们在Proxy上增加了高危操作的日志保存功能,可以在不影响整体性能的情况下提供审计能力。四、Proxy带来的问题1、多一跳Proxy造成的延迟是在客户端和Redis实例之间。客户端访问Redis数据,需要先访问Proxy,再访问Redis节点。额外的一跳会导致延迟增加。经测试,多跳一跳会增加0.2~0.3ms的延迟,但通常业务是可以接受的。2.Pod漂移导致IP变化。Proxy是通过部署在K8s上部署的,也会有节点重启导致IP变化的问题。我们的K8sLB方案可以感知proxy的IP变化,动态切换LVS流量到重启的proxy。3、LVS带来的延迟LVS也会带来延迟。在下表的测试中,对于不同数据长度的get/set操作,LVS引入的延迟小于0.1ms。五、K8s带来的好处1、轻松部署通过运维平台调用K8sAPI部署集群,大大提高了运维效率。2.解决端口管理问题。目前小米在物理机上部署的Redis实例是通过端口区分的,离线端口不能复用。也就是说,整个公司的每个Redis实例都有一个唯一的端口号。目前,65,535个端口中的40,000多个已被使用。按照目前的业务发展速度,港口资源将在两年内枯竭。采用K8s部署,每个Redis实例对应的K8spod拥有独立的IP,不存在端口耗尽和管理复杂的问题。3、降低客户使用门槛。对于应用,只需要使用单机版的非智能客户端连接VIP,降低了使用门槛,避免了繁琐复杂的参数设置。同时,由于VIP和端口是固定的,应用不再需要自己管理RedisCluster的拓扑结构。4.提高客户端性能使用非智能客户端也可以减少客户端的负载,因为智能客户端需要在客户端对key进行哈希处理,来决定将请求发送到哪个Redis节点,当请求时哪个Redis节点会消费客户端QPS是端机CPU资源比较高的。当然,为了降低客户端应用迁移的难度,我们让Proxy也支持智能客户端协议。5.动态升级扩容Proxy支持动态添加和切换RedisCluster的功能,使得RedisCluster的集群升级、扩容和切换过程可以完全不被业务方感知。比如业务端使用30节点的RedisCluster集群。由于业务量的增加,数据量和QPS都在快速增长,需要将集群规模扩大一倍。如果在原有物理机上扩容,需要进行如下流程:协调资源,部署新的60节点集群;手动配置迁移工具,将当前集群的数据迁移到新集群;验证数据无误后,通知业务方修改RedisCluster连接池拓扑,重启服务。RedisCluster虽然支持在线扩容,但扩容过程中槽位的搬迁会影响线上业务,且迁移时间不可控,所以现阶段很少使用这种方式,只有在资源严重不足的情况下才偶尔使用。在新的K8s架构下,迁移过程如下:通过API接口新建一个60个节点的集群;通过API接口创建集群同步工具,将数据迁移到新集群;验证数据无误后,发送给Proxy命令添加新的集群信息,完成切换。整个过程业务方完全不知情。集群升级也很方便:如果业务方可以接受一定的延迟毛刺,可以通过StatefulSet在非高峰时段滚动升级实现;如果业务有延迟需求,可以通过新建集群迁移数据来实现。6.提高服务稳定性和资源利用率通过K8s的资源隔离能力,可以和其他类型的应用混合使用,在保证服务稳定性的同时提高资源利用率。六、遇到的问题1、Pod重启导致数据丢失。当K8spod遇到问题重启时,由于重启速度太快,在RedisCluster集群发现并切掉master之前,pod就会重启。如果pod上的Redis是slave,则不会有影响。但是如果Redis是master,没有AOF,重启后会清空原来的内存数据,Redis会重新加载之前存储的RDB文件,但是RDB文件不是实时数据。之后slave也会将自己的数据同步到之前RDB文件中的数据镜像中,这样会造成部分数据丢失。StatefulSet是有状态服务,部署的pod名称有固定的格式(StatefulSet名称+编号)。我们在初始化RedisCluster的时候,把编号相邻的pod设置为主从关系。当pod重启时,其slave由pod名称决定,在pod重启前向slave节点发送clusterfailover命令,强制将存活的slave节点割为master。这样,重启后,该节点会自动作为从节点加入集群。LVSMappingDelayProxypod通过LVS实现负载均衡。LVS对后端IP:Port的映射有一定延迟才能生效。Proxy节点突然下线会导致部分连接丢失。为了减少Proxy运维对业务的影响,我们在Proxy部署模板中增加了如下选项:lifecycle:preStop:exec:command:-sleep-"171"用于普通Proxypod下线,比如clustershrinkage,rolling更新Proxy版本,其他K8s可控Pod下线。pod下线前,会发消息给LVS,等待171秒。这个时间足以让LVS在业务不知情的情况下,逐渐将这个pod的流量切换到其他pod。2、K8sStatefulSet无法满足RedisCluster的部署需求。K8s原生StatefulSet不能完全满足RedisCluster部署的要求:1)RedisCluster不允许主备关系相同的节点部署在同一台机器上。这很容易理解。如果机器宕机,数据片段将不可用。2)RedisCluster不允许超过半数的集群主节点失效,因为如果超过半数的主节点失效,就会没有足够多的节点去投票满足gossip协议的要求。因为RedisCluster的主备可能随时切换,我们无法避免同一台机器上的所有节点都是master节点的情况,所以我们不能让超过1/4的集群节点部署在部署期间的同一台机器。为了满足上述需求,原生的StatefulSet可以使用反亲和功能,保证同一集群同一台机器上只部署一个节点,但机器利用率很低。因此,我们开发了一个基于StatefulSet的CRD:RedisStatefulSet,它将使用各种策略部署Redis节点。同时在RedisStatefulSet中增加了一些Redis的管理功能。这些我们将在其他文章中继续详细讨论。七、总结目前,集团内多个业务的几十个Redis集群都部署在K8s上,并运行了半年多。得益于K8s的快速部署和故障迁移能力,这些集群的运维工作量远低于物理机上的Redis集群,稳定性得到了充分验证。我们在运维过程中也遇到了很多问题。文中提到的很多功能都是根据实际需要提炼出来的。目前还有很多问题需要后续逐步解决,进一步提高资源利用率和服务质量。1.混合VS。独立部署物理机Redis实例独立部署。所有Redis实例都部署在一台物理机上,方便管理,但资源利用率不高。Redis实例使用了CPU、内存和网络IO,但存储空间基本上被浪费了。在K8s上部署Redis实例也可能在机器上部署任何其他类型的服务。虽然这样可以提高机器的利用率,但是对于Redis等需要高可用和延迟的服务来说,如果因为机器内存耗尽而被逐出是不可接受的。这就需要运维人员监控部署Redis实例的所有机器的内存。一旦内存不足,就会砍掉master,迁移节点,但这会增加运维的工作量。同时,如果混合部门的其他服务是网络吞吐量高的应用,也可能影响到Redis服务。虽然K8s的反亲和性功能可以选择性地将Redis实例部署到没有此类应用的机器上,但在机器资源紧张的情况下,这种情况无法避免。2、RedisCluster管理RedisCluster是一个P2P集群架构,没有中心节点。它依靠八卦协议传播协作自动修复集群的状态。节点下线、网络问题可能会导致RedisCluster部分节点状态出现问题,例如集群中出现Failed或者拓扑中出现握手节点,甚至脑裂。对于这种异常状态,我们可以在RedisCRD中增加更多的功能来逐步解决,进一步提高运维效率。3、审计和安全Redis本身只提供Auth密码认证保护功能,没有权限管理,安全性较差。通过Proxy,我们可以通过密码来区分客户端类型。管理员和普通用户使用不同的密码登录,可执行的操作权限也不同,从而实现权限管理、操作审计等功能。4.支持多个Redis集群。由于gossip协议的限制,单个RedisCluster水平扩展能力有限。当集群规模为300个节点时,这种拓扑变化的节点选择效率会显着降低。同时,由于单个Redis实例的容量不宜过高,单个RedisCluster很难支撑超过TB的数据规模。通过Proxy,我们可以对key进行逻辑切片,从而实现单个Proxy可以访问多个RedisClusters。从客户端的角度来看,相当于接入了一个可以支持更大数据规模的RedisCluster。最后,Redis等有状态服务的容器化部署在国内大厂还没有成熟的经验,小米云平台也在探索过程中逐步完善。目前我们新建的集群大部分已经部署在K8s上,我们计划在一到两年内将集团内的大部分物理机Redis集群迁移到K8s上。这样可以有效减轻运维人员的负担,在不显着增加运维人员的情况下,可以维护更多的Redis集群。