16

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

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=Mzg4NzU1MzIzMA%3D%3D&%3Bmid=2247532798&%3Bidx=1&%3Bsn=3bbb643ae3b5fe196978a1f4042813a8
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

AB7Bn23.gif!mobile

本文根据石鹏老师在〖deeplus直播第260期〗线上分享演讲内容整理而成。 (文末有获取本期PPT&回放的方式,不要错过)

3em2uaf.png!mobile

石鹏

VIPKID基础架构存储平台负责人

  • 曾就职于摩托罗拉、爱奇艺,10余年专注于高并发、高可用、分布式存储方向,发表过相关专利20余篇;

  • 目前负责VIPKID基础架构存储平台,包括Redis平台、Kafka平台、RocketMQ平台、 ElasticSearch搜索平台、数据库访问平台、对象存储平台等。

本文主要 分享内容如下:

IVvm6nq.png!mobile

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

1、Redis集群最初的样子

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

vmAVr2J.png!mobile

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

1)独享部署

优点:

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

缺点:

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

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

2)混合部署

优点:

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

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

缺点:

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

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

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

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

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

建设多租户平台的目的:

  • 提升资源利用率;

  • 降低运维成本;

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

二、Redis多租户平台方案

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

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

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

UVJZJnA.png!mobile

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多租户容器化的架构设计

IJZ3Eva.png!mobile

上图主要分四个部分:

  • 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:R edis管理平台有演示环境吗?

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有多久没有被访问了,然后拿着这个结果找对应的业务聊,逐步进行治理。

获取本期PPT

请添加右侧二维码微信

uMNBrmU.jpg!mobile

↓点这里 回看本期直播


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK