数万实例数百 TB 数据量,携程 Redis 治理演进之路

  1. 云栖社区>
  2. 云栖号资讯>
  3. 博客>
  4. 正文

数万实例数百 TB 数据量,携程 Redis 治理演进之路

云栖号资讯小哥 2020-05-14 14:22:25 浏览338
展开阅读全文

云栖号资讯:【点击查看更多行业资讯
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!

一、背景

携程自 2013 年开始使用 Redis,旧时期为 Memcached 和 Redis 混用状态。由于 Redis 在处理性能,可储存 key 的多样化上有着显著的优势,2017 年开始,Memcached 全部下线,全公司开始大规模使用 Redis。Redis 实例数量也由刚开始的几十个增长到几万个,数据量达到百 TB 规模。作为 Redis 的运维方,为保证 Redis 的高可用性,DBA 的压力也随 Redis 使用规模的增大而增大,集群的扩容,上下线,实例扩容都面临着不小的挑战。

携程 Redis 并非采用原生的 cluster 或者第三方开源的 proxy,而是使用自主研发的 CRedis Client 组件,部署在所有的应用端。该方案的好处是,Redis 的使用者只需要知道自己的 Redis 名称,就可以访问自己的 Redis,而不需要关心 Redis 的实际部署情况。
这也意味着全部的运维操作都需要在 CRedis 中注册并推送到所有的客户端,为保障 Redis 的持续可用性,DBA 的所有运维操作都是以数据的安全性为第一准则,尽可能做到扩容迁移等操作透明化,保证用户至上。

83180A0C_4852_4255_9769_0BF20C7D5ABA

二、单机多实例时期

Redis 使用早期(2016 年以前),Redis 服务器上线后,由 DBA 在物理服务器上部署多个实例,然后这些实例在 CRedis 中注册,提供给应用访问。资源优化和性能的平衡由 DBA 管理,主要是基于服务器的内存、CPU、网络带宽等指标来手动调节,和 MySQL 的单机多实例类似。DBA 在集群上下线和部署时需要自行在 pool 中寻找合适的机器,当然我们也总结出了一套比较合理的优化算法和方式来管理 Redis 的部署。

在 Redis 的使用爆发增长时期(2016-2018),我们上线了一套 Redis 自动化治理系统 RAT(Redis Administration tools)。Redis 的上下线和扩容从手工时代来到了自动化部署和自动扩容时期,Redis 运维管理难度,随着实例大规模增加而增加。
由于前期对 Redis 的申请和扩容没有做太多限制,物理服务器的使用率一直不太理想,维持在 40%+ 左右。加上频繁扩容导致的超大实例(20GB+),稍不注意在全量同步时就容易引起实例 OOM,影响业务。为了提高内存效率和降低手动操作风险,DBA 迫切需要一种更先进的部署治理系统来管理 Redis。

三、容器化时期

2018 年开始,随着容器化的流行以及 Kubernetes 成为容器编排的事实标准,我们也开始逐步探索有状态应用容器化。
Redis 的部署由 Kubernetes 根据设定的规则自动化调度部署,DBA 再也不需要操心资源的问题,操作效率提高了几十倍。Redis 的上线和无状态的应用一样接入到了 PaaS 系统中,Redis 的分配也划分了多个可用域(Region),每个 Region 划分多个可用 Pool 满足相关性强的 Redis 集中部署。

Redis 作为一种典型的有状态应用,很多的落地经验可以参考前面的文章《携程 Redis 容器化实践》。但在当时容器化的规模只有几千个,而目前已经增加了十倍多,还在不断增长,对于如此大规模的实例数,治理策略的调整势在必行。

3.1 二次调度
很多情况下,业务方在申请 Redis 时,并不特别清楚该 Redis 会用到多大,随着业务量的增长或调整,Redis 的使用量可能会远超或远小于原始的分配额度。对于远超额度的,为避免 Key 剔除,以及由此带来的主从切换,影响业务的持续可用性等问题,我们会在 Redis 的使用率(UsedMemory/MaxMemory)达到 90% 的时候进行自动扩容处理(修改 Redis 的 MaxMemory),也就是内存超分,但支持内存超分带来的负面效果也很明显:
1)Kubernetes 的 Request 会失去它原先占位作用,因为真实的用量无法感知。
2)因为 Request 无法感知,所以为了防止实际的宿主机内存被打爆,我们必须限制宿主机实例的个数。
3)限制实例个数是根据每个实例分配的平均期望内存来估算,实际分配中会导致资源的利用率并不平衡。
而对于远小于原始分配额度的,缺点也很明显:
1)实例占用了大量的 Request 配额,而实际用的很小。
2)大量的配额无法释放,导致新的实例无法部署到某些很空的宿主机上。
3)某些很空的宿主机无法部署新实例,利用率低下。
由于 Kubernetes 对于这种有状态应用支持的不够完善,而我们的 Redis 集群又有几千台的规模,在处理这些问题时必须分而治之,各个击破。

因为迁移实例需要迁移一组,一组一般是 2 个或更多实例,对于运维来说是个非常重的操作,但迁移实例可以修正 Request 配额,让其适配 UsedMemory。而漂移只需要迁移一个实例,是个相对轻量级的操作。对于 Request 和 UsedMemory 严重不匹配的(一般是 2 倍以上或 1/3 以下的关系),我们通过迁移来修正。而对于 Request 和 UsedMemory 相差不是很大的,我们必须在外围进行二次调度。
二次调度可以认为是宿主机资源 Rebalance 的过程,而在二次调度之前,我们必须要厘清二次调度的目标:
1)对于一个标准的宿主机,为了支持自动扩容,我们认为 Request 100%,内存使用率为 60%-65% 为最优状态。
2)每个宿主机上内存使用率尽可能地平均,也就说方差尽可能地小。
3)Node 可用内存小于 35%,禁止调度,大于 45%,开放调度。
对于第三点,在外围有专门的 Job 来检测 Node 的可用内存,来 cordon/uncordon 符合条件的 Node。
对于上面第一第二点的两个目标,我们设计了支持 2 种模式的局部最优平衡算法:
1)预留制
由于 Redis 实例在使用过程中内存使用量不断增长,且增长趋势无序无规律,使得某些宿主机上的内存可用率很低(如图 2 所示宿主机可用率约为 24%-26%)。因此,可以通过将内存不足的宿主机上的实例漂移到闲置或内存充足的宿主机上来缓解源宿主机的内存压力。

A71FB16A_6DF2_4c7b_B72A_3A207E72DD64

首先,通过选择需要被二次调度的源宿主机以及指定目标内存可用率(如图 2 为 50%),预留制算法可将源宿主机上需要被调度的实例漂移到闲置宿主机上,或是调用已经实现的 bestnode 接口,自动为源宿主机上需要被调度的实例选择符合调度条件的目标宿主机,且二次调度后目标宿主机以及闲置宿主机的内存可用率不低于指定的目标内存可用率。

bestnode 是已经实现的一种二次调度的策略,可为实例选择相同 label、相同 zone 且可用内存、可分配内存及宿主机可容纳实例个数充足的最优目标宿主机,从而保证实例可以成功地漂移到目标宿主机上。

2)完全平衡制
由于集群中宿主机内存使用率的差距非常大(如图 3 所示),为了使每个宿主机上内存使用率尽可能平均,即方差尽可能小,可以通过将内存紧张的宿主机上的实例漂移到内存充足的宿主机上,从而缩小宿主机内存使用率的差距。

4BAA8243_5DCF_48b1_9D9A_8EB2DD3225A1

如图 3 所示,通过手动选择需要平衡的宿主机实例,完全平衡制算法将计算宿主机群内存使用率的最小方差(图 3 中宿主机群内存使用率达到最小方差约为 61%),并在宿主机群间做实例的调度。

在实例漂移前 Redis 集群中还存在如图 4 所示的情况。由于某些宿主机上实例的 UsedMemory 很小,导致宿主机的内存可用率很高却由于实例个数已满无法再将实例漂移到宿主机上,使得宿主机的内存利用率不高。相反,也存在实例的 UsedMemory 很大导致的宿主机内存可用率低但还可将实例漂移到宿主机上的情况。

对于这种情况,我们首先将内存可用率高的宿主机上 UsedMemory 最小的几个实例漂移到内存使用率低的宿主机上,从而为宿主机腾出实例个数配额,接着将内存可用率低的宿主机上 UsedMemory 较大的几个实例漂移过来,从而平均宿主机的内存使用率。

53501BBC_6D37_4d87_8A2B_676F6D922B5A

用户可配置二次调度的参数,如指定可漂移实例的 Max/Min UsedMemory,Rebalance 次数,最后生成一个 config 文件。
预留制算法与完全平衡制算法在选择源宿主机上需要被调度的实例时都使用的是 First Fit Decreasing(FFD)算法,即首先计算达到目标内存可用率需从源宿主机上释放内存(ReleaseMemory)的大小,接着遍历由大到小排序后源宿主机上实例的 RequestMemory 以及 UsedMemory,将符合条件(RequestMemory在先对宿主机进行预处理后(如上图 4),根据二次调度 config 文件,综合考虑黑白名单、实例优先级,并且排除不能被调度的实例类型(如 XPipe 以及多 slave 实例)后,即可确定最终需要被二次调度的实例名单。最后即可生成如图 5 所示的二次调度方案。
Redis 主要瓶颈在内存,因此我们暂时也只考虑内存。但这种二次调度的模式同样可以应用于其他有状态应用的场景,如 Mysql/Mongodb/Es 等,只是考虑的维度更全面(Cpu/ 内存 / 磁盘)。

F4EEC1C0_4AAB_4e55_B79F_8BC4AC588E29

3.2 自动化漂移
有了上面的二次调度,我们可以手工或者自动生成二次调度的计划或任务,在指定时间触发,此外我们上线了容器自动化漂移系统,漂移操作支持下面几种类型:
1)指定实例漂移到指定宿主机。
2)宿主机完全 down 掉,无法恢复,一键漂移。
3)宿主机有故障需要维修,一键漂移。
4)二次调度计划自动或手动漂移。
根据之前的描述,我们所有的运维操作需要在 CRedis 中注册,我们也针对 Redis 实例在 CRedis 中的不同角色,在逻辑和物理层面进行了无缝衔接,在漂移过程中自动修改 CRedis 的访问策略,数据同步,Xpipe DR 系统自动化注册(Xpipe 是携程 Redis 跨 IDC 容灾的方案),自动添加删除哨兵等。

B07B3D2D_6F1B_4696_82B2_377B9564A6AC

3.3 Cilium
由于我们漂移过程中,整个 IP 是不变的,IP 不变的逻辑由 OVS 来保证,这样好处是对客户端和中间件透明。但随着携程单个 IDC 内容器部署密度的越来越大,大二层网络的交换机表项无法承受这么多 IP 在整个 IDC 内可漂移。
Cilium 是下一代云原生的网络解决方案,之前有其他同事的文章有所描述,这里不再展开。我们将 Redis 容器跑在了 Cilium 上,漂移过程中 Redis 换宿主机后 IP 会变,这样会涉及多个系统的数据变更,如哨兵记录了老 IP,当前实例却变成新 IP,这时候正好分配一个老的 IP 给了新的实例,导致复制关系错乱,稍有不慎便会导致生产事故。也对漂移流程的可靠性提出了更高的要求,我们细化每一步漂移流程,设计合适的状态机,保证每一步的可重试和幂等性。
3.4 傲腾落地
大规模的 Redis 用量以及增长速度迫切需要我们调研性价比更高的解决方案,这时候 Intel 傲腾技术进入了我们的视线。目前为止,傲腾宿主机大约为整个 Redis 宿主机的 10% 左右。
傲腾 SSD
率先被我们引入的是傲腾 SSD, 通过 2 块 SSD 组合,可以提供操作系统约 700 多 G 的内存。

68FE4822_683E_4e5f_B2C4_9DA1C85EAF99

从实际效果上看(图 7)傲腾 SSD 的平均延迟相比内存还是相对明显,从 0.3ms 上升到了 0.9ms,与测试的结果吻合,但某些业务能接受这种延迟的上升,并且能节约 60% 成本,因此我们首先在携程内部小范围的部署了傲腾 SSD。
傲腾 AEP
尽管傲腾 SSD 可以符合我们的部分要求,但缺点还是比较多。
1)驱动支持不完善,基本所有的厂商需要对应的 RAID 卡驱动,并且每家还不太一样,现实状况是需要编译多个内核版本来适配不同厂商的驱动。
2)装机繁琐,需要注册码,还要开关机多次,并且还有失败的概率,是否能最终进入系统有点凭运气。
3)700G+ 作为 Redis 宿主机来说相对太大了,因为我们需要考虑 Redis 宿主机万一挂了恢复的时间。

14BDF407_1FE9_4dc2_B363_C6BA4F356D1E

从线上结果来看,傲腾 AEP 和纯内存的耗时比较接近,业务的监控状态如上图。傲腾与普通物理内存实际运行区别已经非常小,可以满足绝大部分业务场景。并且相对于傲腾 SSD,傲腾 AEP 还有以下的优点:
1)装机方便,不需要注册码,也无需额外的驱动程序
2)可以选择的规格比较多,可以方便组合 CPU/ 内存 / 傲腾 AEP 的配比
我们经过数量和大小的权衡,选择的规格是 32C CPU+4 个 128G 的傲腾 AEP,傲腾 AEP 与内存配比是 1:4。单 GB 成本约为纯内存的 50%。

四、混合云时期

2019 年开始,随着集团业务的全球化,业务海外访问量的增加,Redis 出海的需求也不断上升,由于 Redis 的快速响应要求,我们也需要在公有云上部署 Redis 群集,以提供给当地的应用访问,减少延迟。

然而公有云上无法提供裸金属服务器,实际提供的宿主机都是虚拟机,出于容灾方面的考虑,我们将 Redis 的宿主机严格按 zone 打散,让 master/slave 部署在不同的 zone,这样即使一个 zone 全部 down 掉也完全不影响 Redis 的业务。此外对申请部署透明,对于用户来说,只需要选择想要部署的 Region 即可将 Redis 部署到海外,与私有云完全一致的体验,至此携程的 Redis 走到了混合云时期。
Redis On SSD
公有云上的 Redis 价格非常昂贵,约为私有云的 10 倍左右,并且私有云还有类似傲腾这种方案来进一步降低成本,但 Redis 需要出海的规模巨大,而携程大部分 Redis 需要通过内部的 Xpipe 系统同步到海外,因此迫切需要我们寻找一种廉价的方案来代替 Redis 跑在公有云上。
我们调研一段时间,最终选择开源的 kvrocks 作为二次开发的方案,自己开发实现支持 Redis 的 SYNC 和 PSYNC 协议,无缝对接公司的 Xpipe,来作为出海 Redis 的替代方案,实现方案和详细的数据后续会有专门文章介绍。
这里简单说下结论:下图为线上生产环境同一 Group 的 2 个 Redis/2 个 kvrocks 的访问平均延迟,表现较好的那两条线是 kvrocks 实例,从线上结果来看,与 Redis 表现没有什么差距。而这种替代方案可以节约 60%-80% 的成本。

9AE470D3_DCB2_43a6_A5CF_64E8DF38E348

五、总结

Redis 作为有状态应用,容器化和治理都需要很大的精力投入,而携程 Redis 治理既不盲从社区,为了支持内存超分,也难以照搬公有云厂商的相应的 Redis PaaS 服务,从实际业务需求出发,走出了一条既可行又现实的路。

作者介绍:
向晨,携程资深数据库工程师;布莱德,携程技术专家;皓月,携程技术培训生。

【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/zhibo

立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

原文发布时间:2020-05-14
本文作者:向晨
本文来自:“InfoQ”,了解相关信息可以关注“InfoQ

网友评论

登录后评论
0/500
评论
云栖号资讯小哥
+ 关注
所属团队号: 云栖号资讯