Redis集群实例分析

54次阅读
没有评论

共计 6829 个字符,预计需要花费 18 分钟才能阅读完成。

这篇“Redis 集群实例分析”文章的知识点大部分人都不太理解,所以丸趣 TV 小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Redis 集群实例分析”文章吧。

一、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 来转发请求。

1、Redis Cluster 部署方式

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

我们选择的持久化存储 PV(PersistentVolume)是 Ceph Block Service。Ceph 的读写性能低于本地磁盘,会带来 100~200ms 的读写时延。但由于 Redis 的 RDB/AOF 的写出都是异步的,分布式存储带来的读写延迟对服务并没有影响。

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

测试结果

在相同 workload 和配置下,Predixy 的最高 QPS 要优于 Cerberus,时延也比较接近。综合来看,Predixy 比 Cerberus 的性能要高 33%~60%,并且数据的 key/value 越大,Predixy 优势越明显,所以最后我们选择了 Predixy。

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

3、Proxy 部署方式

Proxy 作为 deployment 部署,无状态轻量化,通过 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,密码还是需要在客户端保存。而使用 Proxy,客户端只需要通过 Proxy 的密码来访问 Proxy,不需要知道 Redis 的密码。Proxy 还限制了 FLUSHDB、CONFIG SET 等操作,避免了客户误操作清空数据或修改 Redis 配置,大大提高了系统的安全性。

同时,Redis 并没有提供审计功能。我们在 Proxy 上增加了高危操作的日志保存功能,可以在不影响整体性能的前提下提供审计能力。

四、Proxy 带来的问题

1、多一跳带来的时延

Proxy 在客户端和 Redis 实例之间,客户端访问 Redis 数据需要先访问 Proxy 再访问 Redis 节点,多了一跳,会导致时延增加。经测试,多一跳会增加 0.2~0.3ms 的时延,不过通常这对业务来说是可以接受的。

2、Pod 漂移造成 IP 变化

Proxy 在 K8s 上是通过 deployment 部署的,一样会有节点重启导致 IP 变化的问题。我们 K8s 的 LB 方案可以感知到 Proxy 的 IP 变化,动态的将 LVS 的流量切到重启后的 Proxy 上。

3、LVS 带来的时延

LVS 也会带来时延,如下表中的测试,不同的数据长度 get/set 操作,LVS 引入的时延小于 0.1ms。

五、K8s 带来的好处

1、部署方便

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

2、解决端口管理问题

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

3、降低客户使用门槛

对应用来说,只需要使用单机版的非智能客户端连接 VIP,降低了使用门槛,避免了繁琐复杂的参数设置。同时由于 VIP 和端口是固定不变的,应用程序不再需要自己管理 Redis Cluster 的拓扑。

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 的 deployment 模板中增加了如下选项:

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 部署的要求:

1)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 集群实例分析”这篇文章的内容,相信大家都有了一定的了解,希望丸趣 TV 小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注丸趣 TV 行业资讯频道。

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-03发表,共计6829字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)