“AI 技术+人才”如何成为企业增长新引擎?戳此了解>>> 了解详情
写点什么

降本 40%!Redis 多租户集群的容器化实践

  • 2021-02-19
  • 本文字数:7158 字

    阅读完需:约 23 分钟

降本40%!Redis多租户集群的容器化实践

一、为什么需要 Redis 多租户平台

1、Redis 集群最初的样子

在公司发展初期,公司只有虚机环境,没有容器。Redis 集群分两种,一种是独享集群,即一个集群里只有一个应用;另一种是混部的集群,即多个应用共享同一个集群。



现在看他们各自的优缺点:


1)独享部署

优点:

  • 稳定性高,相比混部集群,不会被其他应用影响。


缺点:

  • 资源利用率低,不同应用无法共享 Redis 服务器资源:虚机最低配 2C4G,且对 Redis Cluster 集群模式 1 个 Redis 集群至少需要 3 台服务器,对于访问量少的集群,会浪费成本;

  • Redis 集群数多,运维成本高:虚机环境下,集群的创建、销毁、扩容、缩容、容灾等,都依赖人工运维,运维工作量跟集群数成正比。


2)混合部署

优点:

  • 相比独享集群,资源利用率更高;

  • 相比独享集群,集群数少,运维成本低。


缺点:

  • 不同应用可能互相影响;

  • 不用应用存在 key 重复的风险。


对于初创型公司,如果也有同样的 Case,可以借鉴我接下来讲的方案优化,降本提效、提升稳定性。


2、Redis 多租户平台建设的初衷

我们建设 Redis 多租户平台,就是为了保留上面提到的独享集群和混部集群的优点,避免他们的不足。


建设多租户平台的目的:

  • 提升资源利用率;

  • 降低运维成本;

  • 确保不同租户间互不影响。

二、Redis 多租户平台方案

在最初做 Redis 多租户平台时,公司只有虚机环境,还没有容器环境。

1、Redis 多租户平台的设计方案

Redis 多租户平台的设计图如下:



Redis 多租户平台分为三部分,分别是 Redis 管理后台、Redis 的客户端、Redis 集群。


对于 Redis 集群的部署,我们没有做持久化,通过一主一从数据冗余来保证高可用:其中”主“承接所有读写流量,”从“不承接任何读写流量,仅用于”主“故障时做灾备。


集群模式采用的 Redis Cluster。


某个节点的部署:

  • 1 个虚机节点部署 2 个 Redis 实例,一主一从;

    主从不是一对:避免单个虚机节点故障时,主从同时不可用  导致丢数据,且影响业务。

  • 对于同一个 redis 实例,可以多个应用(即多个 namespace)共享。


业务应用在使用 Redis 多租户平台前需要做什么呢?

  • 首先,一个业务应用要去 Redis 管理后台去申请一个属于这个应用的 namespace,通常 namespace 由"集群名 @应用名"组成,他作为该应用在 Redis 多租户平台上的唯一标识;

  • 其次,这个业务应用的负责人要提供这个应用历史的内存峰值。假设应用 1 的内存峰值是 2G,应用 2 的内存峰值是 3G,那么新的 Redis 多租户申请的资源预留 2 倍,及(2G+3G)*2 = 10G;

  • 最后,这个应用需要接入由我们基础架构提供的 Redis 客户端来访问 Redis 多租户集群。


业务应用在访问 Redis 多租户集群时,假设执行指令“set key1 abc”,那么 Redis 客户端会自动把原来的 key 前面加上“namespace:”前缀,即指令变成“set cluster1@app1:key1 abc",其中 cluster1 是 Redis 集群名,app1 是应用名。


这样,我们就可以根据 namespace 来区分不同应用在 Redis 集群中的数据了。


但是,这个方案仍然是多个应用共享 Redis 集群资源,仍存在不同租户(应用)间互相影响的风险。


2、如何保证租户间资源互不影响

保证不同租户间资源互不影响,我们是通过“监控、告警、问题根因定位、限流 or 禁用指令”这四步来完成的。

1)监控

对于 Redis 集群,我们会监控 ops,热 key 等等多维度指标,重点是内存使用率。


2)告警

由于我们 Redis 多租户集群,为用户峰值还多预留了一倍的资源,所以当用户达到内存使用峰值时,只会占用 50%的内存。

  • 内存使用率达到 60%,通过企业 IM 做 1 级告警;

  • 内存使用率达到 75%,通过短信做 2 级告警;

  • 内存使用率达到 85%,通过电话做 3 级告警。


3)问题根因定位

当告警 1 级时,管理员就要分析告警原因了,首先执行”RDB 内存分析工具“,分析内存不足是哪个应用流量激增导致的,然后分析是 bigkey 导致的还是就是请求量大导致的。


4)限流 or 禁用指令

定位到问题后,通过限流或禁用指令来确保 Redis 集群不被激增流量打挂:


  • bigkey 导致:采用禁用指令方式

    禁用指令的实现方案为,通过管理后台,定时下发配置给 Redis 客户端,指明要对该应用下的哪个 key 做禁用指令。

  • 流量高导致:采用限流

    限流也是管理后台下发配置给 Redis 客户端,做应用维度的限流。

3、RDB 分析工具的原理

上面”问题根因定位“时用到了 RDB 内存分析工具,这里我们基于开源做了一些定制化开发,原理是伪装成 Redis 从节点,Dump RDB 数据到本地内存,然后逐个 key 解析,分析出各应用的内存占比、bigkey top 10、key 的总数。


另外,RDB 内存分析工具会在每天业务低峰时段执行,这样,在故障时再执行 RDB 内存分析工具,把结果跟低峰时段的执行结果做比较,就能定位出是哪个业务应用增长快导致的告警。

4、问题与挑战

1)租户间资源“假”隔离

如果流量增长太快,来不及做限流或禁用指令,那么不同租户间理论上仍存在互相影响的微小可能。


挑战:怎样既能“真”隔离又能共享资源,租户间资源如果“真”隔离,就不会互相影响了。


2)解决激增流量告警时做限流或禁用指令都是对业务有损的

虽然限流或禁用指令的目标是异常使用 redis 的某个应用的 key, 但这样做对业务是有损的。


挑战:能否做到租户间既不互相影响又对业务无损呢?


3)集群数虽减少了但运维工作量仍不小

相比独享集群,集群数虽少了,但集群的创建、销毁、扩容、缩容、故障容灾等,仍需要人工运维,运维工作量跟集群数成正比。


挑战:运维成本是否有进一步降低的空间?


对于这几个问题,我们在 Redis 容器云的方案中都一一解决了。

三、Redis 容器云数据库平台优化实战

在这个阶段,我们公司开始支持容器环境,这时我们想,Redis 作为有状态服务,通过上容器是否可以解决我们当前存在的问题。

1、为什么需要租户间资源严格隔离

按上面的分析,大家知道不同租户间的资源仅做到逻辑隔离,存在互相影响的风险,因此要消除风险,需要做到租户间严格隔离资源边界。


这里有人会问,Redis 多租户的目的,本来就是为了共享资源提升资源利用率以降低成本,如果严格隔离了租户间的资源,那多租户的意义何在呢?


通常服务器的最低配是有限制的,例如 2C4G,但可能我的应用仅需要 0.5C1G,假设我们可以做到资源边界的严格隔离,那么 2C4G 的资源就仅分配给 4 个 0.5C1G 的应用,这样这 4 个应用就都没有了互相影响的风险。


达到这样的效果,我们很容易想到借助容器来实现。但做了严格的资源边界隔离,在保证租户间不会互相影响的同时,带来的弊端是不能够充分共享资源。


综上,我们推荐的做法是:

  • 对于 Redis 可用性非常敏感的应用,推荐采用资源的严格边界隔离方式,即一个 Pod 里只部署一个应用;

  • 对于 Redis 可用性不是非常敏感的应用,推荐采用资源的逻辑隔离方式,即一个 Pod 里同时部署多个应用,当激增流量时,通过容器水平扩容来解决。


这样在稳定性与最大限度提高资源利用率之间,根据应用对稳定性的不同要求,做出了相应的取舍。

2、如何借助 K8S 容器化 Redis 多租户集群

1)K8S 容器化 Redis 多租户集群的目的

  • 可做租户间资源边界的严格隔离:通过容器 Pod 最小化资源单位;

  • 解决激增流量对集群的影响:借助 Redis 集群节点水平扩容机制来解决激增流量时租户间互相影响,且不再有虚机方案时限流或禁用指令对业务有损的问题;

  • 降低运维成本:容器环境运维自动化,解决 Redis 集群规模大,人工运维成本高的问题。


2)K8S 容器化 Redis 集群面临的挑战

  • K8S 如何部署 Redis 有状态服务;

  • 容器 Crash 后如何不影响服务可用性;

  • 容器重启后如何保证 Redis 内存中的数据不丢;

  • 节点水平扩容时如何做到 slots 迁移时不影响业务。


3)Redis 多租户容器化的架构设计



上图主要分四个部分:

  • K8s 管理的 Redis 集群;

  • K8s 控制器面板;

  • Redis 管理后台;

  • 由我们基础架构提供的 Redis 客户端。


由 K8S 管理的 Redis 集群设计要点:

  • 一个 node 中部署 2 个 Pod,一主一从,但主从不是一对,避免一个 node 宕机一主一从同时故障导致丢数据;同时这一主已从也不是同一个集群,目的是确保一个 node 宕机时,最多只影响一个集群中的一个主节点;

  • 基于开源实现了定制化的 Redis Operator;

  • 用 anti-affinity 来确保同一组 master 和 slave 不会部署到同一个 node 上;

  • VKShark 服务为 K8s 的 api-server 的代理和 controller:当访问 k8s api-server 时,通过 VKShark 做代理;同时 VKShark 作为 controller 会监听 k8s node 的节点的变化,然后通知 Redis 管理后台;

  • 通过 Redis operator 的 CRD 来配置一组主从节点作为相同的 StatefulSet 管理。


节点扩容调用流程:

  • 由 Redis 管理后台选择集群,输入扩容节点数如 1 后,点击按钮发起节点扩容请求;

  • 扩容请求经由 VKShark 做代理,访问 k8s api-server, api-server 会把相应的 CRD 持久化到 ETCD 中;

  • Redis operator 监控 api-server, 发现有扩容请求,会创建 statefulset,通过 api-server 调用 k8s API 做节点扩容,新建一主一从两个 Pod;

  • Redis operator 经过 api-server 监听 Pod 的变化,并把新节点加入到 Redis Cluster 集群;

  • VKShark 通过 api-server 监听 Pod 的变化,发现有新节点,通知 Redis 管理后台;

  • Redis 管理后台先检测新的 Pod 是否已加入到了 Redis Cluster 集群,如已加入则只需 migrate 指令做槽位迁移;

  • Redis 管理后台会定时下发 Redis 的集群节点信息给 Redis 客户端;

  • Redis 客户端用最新的节点信息计算 key 的 slot。


3、如何让激增流量租户互不影响且对业务无损

1)容器可快速自动水平扩容  

有了容器环境,对于 Redis 可用性敏感的应用,可以一个 Pod 只部署一个应用,这个应用间不会互相争抢资源。那么对于某个应用,如果他的流量激增,容器环境也可以通过自动水平扩容来解决,而不必像虚机时 Redis 多租户方案那样通过限流或禁用指令对业务有损的方式。


当然,这里节点不能无限制扩容,当扩容节点数达到我们预先设置的最大阈值时,说明该流量激增属于”非正常“,有可能是系统 bug 等导致,这时会采取限流与禁用指令的方式


2)Redis Cluster 集群扩容时迁移槽位是否会影响业务

访问正在迁移的槽位中的某个 key:

槽位中 key 的迁移指令的迁移指令是同步阻塞的,所以访问正在迁移的 key,请求会被拒绝,这时利用我们基础架构封装的 Jedis 的客户端来做重试,在重试周期内,如果 key 迁移完成,就可以正常访问了,这在绝大多数情况下都是正常的。但如果迁移的 key 是 bigkey, 可能迁移时间很久,在 Redis 客户端重试周期内没有完成迁移,那么此时业务对这个 key 的访问就是失败的,后面会讲怎样解决 bigkey 的问题。


一个 slot 下可能有部分 key 被迁移完成,部分 key 正在等待迁移,如果被读写的 key 所属的 slot 正在被迁移,则 Redis Cluster 会自动处理:先去源节点找,没找到则重定向到目标节点,这个处理过程是 Redis Cluster 自带的,详细过程不再赘述。


3)如何解决大 Key、热 Key 等原因导致的 Redis 集群热点问题

对于 Redis 某些数据结构,如集群类型,会导致大 key; 某些节点访问频繁,会产生热 key。大 key 或热 key 都会导致集群中某节点成为热点。


如何定位大 key 热 key:

  • 大 key:通过 RDB 内存分析工具,可获取 top n 的大 key;

  • 热 key:通过 Redis 客户端向服务端监控投递监控数据。


解决方案:

  • 定位到导致问题的 Key 后,通过 Key 计算出对应的 slot,然后扩容个新节点,把除大 Key 或热 Key 之外的槽位迁移至新节点中。然后在集群节点扩容缩容做槽位迁移时,排除掉大 key 热 key 槽位所在的节点;

  • 大 key 热 key 通常是业务使用不当导致,也会线下通知业务进行治理。


4)如何做 Redis 多租户平台故障容灾

①Redis 节点故障怎样做故障容灾


Redis 上容器后,单个节点故障:

  • 如故障的是从节点,对服务无影响,因为主节点承接所有读写流量,从节点仅作为主节点灾备,从故障后,Redis operator 会监听到,并拉起新的从节点,然后加入到 Redis cluster 集群,再从主节点向从节点同步数据,这时仅在数据同步时对主节点的资源有些消耗,在实际使用中我们发现对主节点资源的消耗对集群基本无大的影响;

  • 如故障的是主节点,主节点是承接业务所有的读写流量,确认主节点故障的时间是可配置的,如配置 5 秒,那么在确认周期内,对业务是有影响的,待确认主节点故障后,从节点会顶替主节点。由于主节点向从节点做数据同步是异步的,所以在主节点故障时,还没来得及向从节点同步的数据,是会丢失的。在从节点顶替主节点后,Redis Operator 会拉起新的 Pod 作为从节点,然后加入 Redis cluster 集群,从主节点同步数据。


②如何做 Redis 机房故障容灾


当前公司有 A、B 两个机房,假设 A 机房故障时,公司监控系统会监控发现,然后可通过 Redis 管理平台下发 B 机房的 Redis 集群节点地址给 Redis 客户端,Redis 客户端收到后,会自动重连到机房 B,从而做到了机房故障容灾切换。


当然原理 A 机房中 Redis 中的数据是全部丢失的,这个方案主要用于 Redis 作为缓存,同时业务可降级的场景。重点核心业务不强依赖于 Redis。


5)如何保证容器中 Redis 节点重启后数据不丢

Redis 目前公司内几乎所有的业务使用都作为缓存数据使用,无需持久化,其高可用通过主从节点数据冗余来保证。


对于个别需要持久化的 Redis 需求,K8S 可通过存储持久化数据到文件存储,当容器重启后可从文件存储中重新获取持久化数据,这里我们验证了持久化数据到 PVC 共享存储,采用的阿里云的 NAS 文件存储,测试是 OK 的,待线上实际场景验证。


6)多租户共享 Key 的解决方案

多租户共享 key,是指两个不同的应用要使用相同的 key 来共享数据。由于只有相同 namespace 的中的 key 的前缀才相同,要两个应用共享 key,就需要让这两个应用在同一个 namespace 中,Redis 平台会把这两个应用视为同一个应用。


由于 namespace 是资源配额的最小单位,这两个应用需要共同申请资源配额:即不只 key 共享,申请的资源配额也要共享。

四、Redis 多租户平台收益总结

1、降低服务器使用成本 – 资源共享

在 VIPKID 实战中,对比 Redis 容器云优化前后,服务器使用成本减少了 40%。

2、提升运维效率 – 从人工运维到自动化运维

在 Redis 集群部署在虚机时代,Redis 集群需要人工维护,集群数越多,运维成本越高。当 Redis 上 K8S 容器后,一切运维操作都是自动的,这时我们已经不再关注集群数的多少,依赖 K8S 与 Redis 管理平台配合,做到了全自动化运维。

3、提升系统稳定性 – 故障容灾自动化

借助于 Redis 多租户平台对节点故障容灾与机房故障容灾的能力,做到了故障容灾的自动化,大大缩短了故障容灾的处理时间。

Q&A

Q1:Redis 集群,一个服务器和另一个服务器是怎样均衡负载的?监控工具么?

A1:A1:我们 Redis 集群采用 Redis Cluster 集群模式,slot 的范围是 0~16383,通过算法把槽位平均负载到 Redis 集群的每个节点中。


Q2:Redis 在 K8S 环境自动扩容后,slot 也是自动迁移吗?

A2:K8S 自动扩容后,slot 是自动迁移的,但在扩容时,不是由 redis operator 触发的,而是由 Redis 管理后台触发,因为扩容时做槽位到节点的负载均衡,是不包含 bigkey 所在槽位专用的节点的,管理后台才知道哪个节点是 bigkey 专用的。


Q3:如何避免多个用户同时读取 Redis 中相同的数据?

A3:这位同学是想问,不同租户间数据怎样不会互相读取到吧?这是通过每个应用申请不同的 namespace 来区分的,例如,应用 1 和应用 2 在同一个 Redis 多租户集群,他们都执行了 set aaa bbb 指令,但应用 1 的 namespace 是 cluster1@app1, 应用 2 的 namespace 是 cluster1@app2,那么他们的 set aaa bbb 指令经过 Redis 客户端后,变成了"set cluster1@app1:aaa bbb"和“set cluster1@app2:aaa bbb”,通过 key 的不同前缀来区分。


Q4:Pod 的 IP 是固定的吗?

A4:Pod 的 IP 重启后是变化的。具体细节详见问题 6 答案。


Q5: Redis 内存大小配置多少?配置 rdb 吗?

A5:Redis 多租户集群的内存分配,跟里面应用的峰值内存有关,例如有两个应用共享 Redis 集群,一个应用内存峰值是 2G,另一个应用内存峰值是 3G,那么虚机环境下,集群的内存会预留 2 倍,即分配 10G(10G=(2G+3G)*2);容器环境下,预留 1.5 倍,即 7.5G(7.5G = (2G+3G)* 1.5)。


我们公司的 Redis 场景全部是内存场景,无持久化场景,Redis 数据的高可用是通过一主一从冗余实现,其中”主“承接业务的全部读写流量,”从“不承接流量,仅用于”主“故障时做灾备。做 RDB 仅在故障定位时,dump RDB 模型做问题分析定位。


Q6:Redis 是通过容器 IP 访问,还是通过 K8S 访问?

A6:通过容器中 Pod 的 IP 访问。当 Pod 重启后,IP 会变化。Redis 客户端会缓存集群中所有 IP 列表,Redis 管理后台会定时同步最新的集群 IP 列表给 Redis 客户端,假设 Pod 重启 IP 变化了,但 Redis 客户端还没来得及从 Redis 管理后台同步到最新 IP,那么 Redis 客户端会访问旧的 IP,这时它找不到该 IP,会访问集群中的其他 IP 来拉取集群中最新的 IP 列表,从而访问到变化的 IP。


Q7:Redis 管理平台有演示环境吗?

A7:Redis 管理平台,主要提供监控、数据展示、自动化运维等能力,有机会可以单独交流。


Q8:Redis 是共享存储还是本地存储,如果用共享存储,是否遇到过 aof 刷缓存导致的线程阻塞?

A8:当前我们公司的场景 Redis 数据全部存在内存中,通过主从数据冗余来保证数据高可用,暂没有持久化场景。


Q9:Redis 集群如何解决 key 分布不均的问题呢?

A9:某个 key 具体落到哪个槽位,是通过负载均衡算法保证每个槽位中的 key 数量保持均衡,然后确保槽位平均分配到 Redis 集群中的每个节点。


当然每个槽位中 key 数量基本相同的情况下,每个 key 的大小不同,对于 bigkey 的治理方案,请参考我发的 PPT 及文档;对于槽位落点不均衡的情况,我们在业务低峰时段有巡检,然后通过自定义算法,平均负责槽位到每个 Redis 节点(大 key、热 key 的槽位除外)。


Q10:Redis 集群压测工具有推荐么?

A10:都可以,无特别推荐,我们 QA 选的 redis-benchmark。


Q11:Key 生命周期更新策略的优化?

A11 : 对于 Redis 无过期时间的 key 的治理,我们的策略是通过 RDB 内存分析工具按 key 的大小从大到小排名,找出 top n 的 key, 再用 debug 命令逐个查找每个 key 有多久没有被访问了,然后拿着这个结果找对应的业务聊,逐步进行治理。


嘉宾介绍:

石鹏,VIPKID 基础架构存储平台负责人。曾就职于摩托罗拉、爱奇艺,10 余年专注于高并发、高可用、分布式存储方向,发表过相关专利 20 余篇;目前负责 VIPKID 基础架构存储平台,包括 Redis 平台、Kafka 平台、RocketMQ 平台、 ElasticSearch 搜索平台、数据库访问平台、对象存储平台等。


本文转载自:dbaplus 社群(ID:dbaplus)

原文链接:降本40%!Redis多租户集群的容器化实践

2021-02-19 08:003606

评论

发布
暂无评论
发现更多内容

第十周作业

晨光

架构师训练营第十周作业

一剑

中国计算机软件开发合同纠纷分析报告(2019-3)

朱又生

大数据 项目管理 计算机软件开发合同纠纷 风险管理

SpreadJS 纯前端表格控件应用案例:生产采购管理软件

葡萄城技术团队

RushPlayer“一键下马”系列之-JavPlayer

flow

娱乐至穷

北柯

学习 互联网 娱乐 抖音

微服务

石刻掌纹

如何通过electron构建桌面跨平台音视频应用

ZEGO即构

音视频 Electron RTC

低/零代码的认知误区有哪些?

代码制造者

编程语言 低代码 零代码 信息化 开发应用

即大数据后-贵阳能否成为区块链的机遇之城?

CECBC

区块链 大数据 贵阳

央行清算总中心与三家银行签署区块链福费廷交易平台合作协议

CECBC

区块链技术 人民银行

35岁腾讯员工被裁员感叹:北京一套房,存款700多万,失业好焦虑

程序员生活志

程序员

将设计模式应用到日常的curd中-模板方法和装饰器

LSJ

Java 设计 设计模式 装饰器 模板方法

快速学习秘诀:费曼学习法

池建强

学习

SpreadJS 纯前端表格控件应用案例:医疗行业智能报表系统

葡萄城技术团队

什么是死锁?如何解决死锁?

奈学教育

信息管理软件需求分析阶段的实践经验及论述(2010年)

朱又生

项目管理 产品经理 需求分析 用户需求调研

中国计算机软件开发合同纠纷分析报告(2019-2)

朱又生

大数据 项目管理 计算机软件开发合同纠纷 风险管理

第十周总结

晨光

Flink 支持的重启策略有哪些

古月木易

flink

华章科技好书5折优惠,满99再减10元

华章IT

Python AI 数字化转型 Java 25 周年 计算机科学丛书

Flink 支持的重启策略有哪些

奈学教育

flink

week10 学习总结

任小龙

只加两行代码,为什么用了整整两天时间?

程序员生活志

编程 bug

anyRTC 4.0 以心铸造,以梦相承

anyRTC开发者

anyRTC 4.0 官网升级

Kubernetes 网络通讯模型解析

ninetyhe

架构师训练营第10周

大丁💸💵💴💶🚀🐟

软件规模扩张与其组织粒度的进化

superman

中台 微服务 服务化改造

Oracle常用命令

阡陌r

中国计算机软件开发合同纠纷分析报告(2019-1)

朱又生

大数据 项目管理 计算机软件开发合同纠纷 风险管理

django-admin和manage.py用法

BigYoung

Python django django-admin manage.py

降本40%!Redis多租户集群的容器化实践_文化 & 方法_dbaplus社群_InfoQ精选文章