弹性伸缩工程优化探秘

本文将带大家一起探索,关于腾讯云弹性计算产品的技术设计要点。

在各行各业都一定程度上适用这句话:Those who talk don’t know, and those who know don’t talk.

—— 而我相信,你终将成为那个懂得原理、能做成事还乐于分享的高手。

0x00 弹性计算相关背景介绍

云计算底层离不开虚拟化技术,虚拟化让人们有安全感和幸福感,它解决了资源的安全隔离和高效利用两大问题。操作虚拟机,就像在泳道里游泳,因为有挡波阻浪的泳道线,我们无需关心旁边泳道里的人是何种泳姿翻腾出多大水花,而我们总是自由自在,仿佛拥有了整个泳池。

到多终端、多后台服务的动静拆分:

再到更复杂的微服务化:

腾讯云的云服务器(CVM)和弹性伸缩(AutoScaling)将陪伴用户业务发展的每一个阶段,一起见证用户的业务和架构的日新月异。当然,弹性伸缩(AutoScaling)服务本身也是持续续高速增长的业务:

弹性 (Elasticity)—— As the extension, so the force. 弹性就是物体受到外力时变形,并且当该外力解除时恢复其初始形状的能力。固体物体受到外力时将变形。如果材料是弹性的,当这些力被移除时,物体将返回到其初始形状。按广义胡克定律,应力(stress)大小和应变(strain)成正比。

弹性伸缩——甚至整个云服务——解决的最关键问题是:让业务稳稳地活下去,持续产生社会价值。实现时两个核心思想贯彻始终:

第一,有备无患 —— Design for failure, and nothing will fail.

第二,统一决策,灵活执行 —— 顶层指令在各个执行层级间“行政发包”。重点在于广义的机制(machanism)策略(strategy)分离,strategy可以是决策/策略/设计/调度/算法,mechanism可以是机制/执行/计算/IO/任务实现/服务等。

0x01 弹性伸缩瓶颈及方案分析

伸缩活动——保障业务架构弹力满满的“胶原蛋白”。弹性伸缩的伸缩活动包括扩容、缩容、不健康实例替换等等。弹性伸缩的核心就是伸缩活动的设计实现及其生命周期的管理。

做任何需要保证质量的工作都需要须形成一定惯例(Routine)流程,惯例一般是步骤(Step)的简单线性叠加,不过更复杂的需要工作流Workflow,串/并行甚至分支判断。

其实生活中,你或许每天都感受着流程,如简单的护肤流程:洁面乳cleanser、爽肤水toner、精华液serum、润肤霜moisturizer和防晒霜SPF,像这样“线性”的五步,每一步都依赖前一步的结束。不过,当你想更进一步提升魅力时,事情逐渐复杂起来:

第一,步骤会更多,如除了基本护肤还要粉底、遮瑕以及最后的定妆粉等;

第二,步骤间不完全依赖彼此的顺序执行,如睫毛、眼线、唇彩和轮廓修容等步骤的顺序,可自由发挥,按兴趣统筹调整,化妆师多的时候还可以并行;

第三,步骤间或许有些循环,如补粉底等。如下图:

化妆“程序”步骤的串行与并行

等等,这看起来感觉像不像一个程序呢?的确。弹性伸缩的业务抽象完全可以类比化妆的流程,使用弹性伸缩也同样地可以让我们的架构更有活力和魅力,让我们的业务永葆青春。

伸缩活动的计算步骤复杂度如何?可以认为每个伸缩活动是由若干个子步骤(Step)组成的,往往每个Step都涉及至少一次的外部微服务或API调用,可以简单地理解为一次异步I/O任务,其中既有串行的,也有必须并行的,彼此的调用链路也由运行时的结果动态调整。

伸缩活动的步骤配置(缩减示意)

当然,真实的步骤已经是图中的若干倍,达到上百步,复杂度也更上了一个级别。步骤分为活动级别的步骤(下图蓝圈)和实例级别(下图绿圈)的步骤,如图所示。简单算下,如果100个伸缩活动,每个扩容100台云服务器的话,那么同一时刻(或极短时间内),按图中“最天真的”情况下,也要上万个I/O请求,实际中的步骤数量其实还要大两个数量级。所以,伸缩活动,即弹性伸缩语境下的“执行任务”,其实现难度和复杂度是具备一定通用性的:即后台异步任务流程、多组件/微服务间彼此调用、业务相关的复杂分支逻辑判断、任务的多状态(异常/重试/取消)、相关涉及的元数据量大、用户任务间的上下文隔离,以及并发性能和稳定性都要求级高。

如果你做过后台的业务开发,上述情景和需求是否感觉亲切熟悉呢?

单个伸缩活动的子步骤类似项链的棱角

可以思考下:这类任务是计算密集型(Compute Bound)还是I/O密集型(I/O Bound)呢?

0x02 框架设计及细节介绍

为了实现统一的决策设计,通常需要任务流WorkFlow的步骤定义来完成。那么我们观察下真正的Flow是什么样子的呢?照片中,壮观的Joekulgilkvísl蜿蜒穿过冰岛高地,成辫状蛇行向前,流过两侧被积雪覆盖的Rhyolite流纹岩山脉:

冰岛峡谷中的Joekulgilkvísl河,真实世界的Flow

我们不难发现:

1、现实的Flow不会逆流,永远向前,没有rollback(回滚);

2、现实的Flow不是一条线,必有若干支流,而所谓的主流不过是最大(概率流经)的一支;

3、现实的Flow中的路径中的每一个点,都是独一无二的,有自己的上下文。不可能两次踏入同一条河流。

所以总结:用带Context的DAG(有向无环图)抽象Flow。不要用类似rollback/cleanup、retry、exception这些概念来实现底层框架,这些复杂的概念可以有,不过最好放在更上层抽象。

另外,要摒弃朴素耿直的线性思维,相比成功(Success)或失败(Failure),关注“下一步去哪”(Next)以及“干净地完成”(Done)更加重要。因为前二者可能都有很多情况,比如失败的原因和结果一定有多种,任务的成功或失败都只是DAG里的一条Path而已,没有本质差异。非黑即白的二元决策往往是极不成熟且适用范围狭窄的。

当然,如果之前熟悉线性的回滚思维方式,通过简单的转换,可以将流程归约到DAG形式的描述,如图:S2是S1的回滚清理步骤,S4是S3的回滚清理步骤,左侧是线性回滚模式,右侧是归约后的同构DAG。

DAG描述业务流程

完成了统一的决策设计,再来看看灵活高效的执行。以下场景是人与人之间比较优雅的组织方式,管弦交响乐队的模式:

symphony orchestra

一个指挥,若干个乐器(弦乐、木管、铜管、打击)组,大家一起通力合作完成一次和谐的演奏流程。

对于整个乐团,指挥是大脑(CPU Bound),各个组的乐师是手脚(I/O Bound);对于指挥来说,他也有自己的大脑和挥舞指挥棒的双手。他们都是Actor,且互相有优雅的通讯方式。

指挥一个看起来是演奏得情绪节奏大脑,但他并没有直接下达命令到每个乐师,那么背后真正高效的组织力量又是什么呢?

谁是真正的控制:指挥 vs. 曲谱

下图是弹性伸缩(AutoScaling)的后台架构图简单示意:从API到任务调度器、定时任务触发器以及周边组件,都至关重要。对于伸缩活动的实际执行,其引擎组件在最后方,即图中红色的Activator服务组件,注意它同时也是一个MQ任务消费者,和你的业务中的通用消费者组件无太大差异。

弹性伸缩后台服务架构示意

放大上述的Activator,我们看到大致如下的类似交响乐队的内部实现。其中红色的就是策略核心:伸缩活动步骤定义,即WorkFlow的定义,也是全部活动的“乐谱”。Activator核心引擎负责伸缩活动的高效执行及生命周期的管理。可类比为:

进程 = 程序 + 虚拟机 (解释器 + 运行时 + 库函数)

伸缩活动 = 步骤配置 + 执行框架(核心引擎 + 处理函数)

步骤编排核心计算引擎设计示意

每个执行单元都是Reactor模型的异步事件处理器:上一层的“指挥”执行单元Actor查询工作流步骤表,并根据下一层的执行返回结果计算下一步的任务并分发给下层;下一层中的Actor通过总线和上层“指挥”执行单元沟通。这里面有一个分形(Fractal)的设计考量,类似递归的感觉,这种分形树状结构是最自然也最高效的组织方式,尽管理论上可以无限层次的扩展,实践应用中一般四层以内足够了。实践上,可以考虑从协程/线程/进程/节点到微服务各级别来分别递进实现。上图只是单线程内的二层分型执行引擎的设计示例。

设计灵感源于:有限自动机、Actor模型以及分形理论

系统设计实践先介绍到这里。关于性能,再补充两个让其质变的实现细节。

细节1:最小的执行单元(原子Actor)通过eventfd来进行彼此的消息通知,高效利用内核接口/资源,保证高性能。eventfd-with-epoll 可以保证单节点百万级的事件并发,极其适合这类高事件吞吐率的场景。

最小的分形Actor单元,基于Linux eventfd&epoll高效实现

细节2:事务消息Copy-on-Write实现,通过Immutability节省空间同时保证通讯安全。注意,这里我们推荐用消息传递(Massage Passing)来实现内存共享(Memory Sharing),而不是相反。

事务消息CoW引用实现,用消息传递实现内存共享

0x03 技术思考及方法论总结

重点无疑是策略机制分离(Separation mechanism from policy),它是处理统一策略和灵活执行之间的矛盾的最大前提和行之有效的方法。它也是操作系统里最重要的概念之一,如果你是技术人员应该不觉得太陌生。对于业务型后台计算逻辑,核心问题往往一般属于两大类:

1、资源管理:特点在于大规模,静态,可以化为后者解决

2、任务管理:难点在于执行调度、生命周期复杂状态、高并发、且动态决策

对于决策统一的诉求,在于可灵活设计组合、可靠可控,我们通过策略层采用DAG任务流张量编排来实现;

对于执行灵活的诉求,在于高性能和定制化,我们通过执行层采用分形Reactor逐层建模实现。

流程策略与执行机制分离

系统设计上,可以沿着这个思路进行优化:将需求问题转换为计算问题,再转换为I/O问题,再转换为组合设计问题,最终化为策略问题,我们的系统将因满足人的需求而愈发增值。

0x04 小结

本文所述的设计点及相关方法论已在腾讯云弹性伸缩(AutoScaling)、轻量应用服务器(Lighthouse)等多个产品中实践应用,不仅有效地支持了这些核心业务的快速迭代、改善了开发效率,更重要的是大家日常的代码撸得更开心了。如果你也有兴趣,欢迎一起来讨论吧~

0x05 参考资料

本文内容源于第十三届中国系统架构师大会SACC2021的直播分享《 腾讯云弹性伸缩工程优化探秘技术》

可扩展任务流框架实现

Linux eventfd原理应用

Incredible images reveal the stunning beauty of braided rivers

关于作者:溪歪歪,2015年加入腾讯,专注云服务产品相关领域的技术探索。负责云服务器、弹性伸缩、轻量应用服务器、GPU等产品的研发及运营工作。 长期关注高性能集群管理、分布式任务调度系统、Web全栈开发相关方向。

未完待续,敬请期待...

本站文章资源均来源自网络,除非特别声明,否则均不代表站方观点,并仅供查阅,不作为任何参考依据!
如有侵权请及时跟我们联系,本站将及时删除!
如遇版权问题,请查看 本站版权声明
THE END
分享
二维码
海报
弹性伸缩工程优化探秘
在各行各业都一定程度上适用这句话:Those who talk don’t know, and those who know don’t talk.
<<上一篇
下一篇>>