微服务实例自动弹性伸缩实践


作者:陈乐吉

背景

弹性伸缩是根据用户的业务需求和策略,自动“调整”其“弹性资源”的管理服务。通过弹性伸缩功能,用户可设置定时、周期或监控策略,恰到好处地增加或减少“弹性资源”,并完成实例配置,保证业务平稳健康运行。
这里的调整是指:在满足业务需求高峰增长时无缝地增加“弹性资源”,并在业务需求下降时自动减少"弹性资源"以节约成本。
微信图片_20170614172644_meitu_2.jpg

在基于容器的PaaS平台中,弹性资源包含两种资源:微服务实例、宿主计算机资源
属于IaaS计算资源池的范畴,PaaS并不直接操作这些资源,只提供一个触发器,用来打通PaaS和IaaS。当PaaS感知底层计算资源使用情况满足配置规则时,触发IaaS对资源池扩缩容。
本文主要讨论第一种情况及“微服务实例”的自动弹性伸缩

产品需求

定义弹性伸缩功能,就是定义一下两块内容:
触发规则:涉及在什么时候(定时、周期)、按照什么方式(监控项、判断标准)。
弹性伸缩行为:定位目标资源、伸缩的流程控制。
2.1用户视图
从用户的角度,要指定某个微服务的弹性伸缩规则需要操作如下内容:
目标对象:微服务名称
全局参数:1.使能开关 2.单次伸缩操作可增减的实例数(步长) 3.最大实例数 4.最小实例数 5.两次操作之间的触发间隔 6.用于触发弹性伸缩行为的判断指标 (CPU/Memory/Calendar)
目标对象:微服务名称
CPU
扩容规则:门限值(>95%)持续时间(5 min)
缩容规则:门限值(<20%)持续时间(5 min)
Memory
扩容规则:门限值(>4GB)持续时间(5 min)
缩容规则: 门限值(<500MB)持续时间(5 min)
Calender
周期类型(月/星期)
起始时间(精确到秒,如:01日 08:00:00 - 03
18:00:00)
周期内弹性伸缩规则细节 (CPU/Memory/Calendar-Time)
Calendar-Time:(CPU/Memory同上)
伸缩方向:(Up/Down,进入该时 间段的起始时间执行扩缩容、偏离该时间段恢复原实例数)

288700033af6c87c72d2.jpg


UI界面
2.2数据模型

QQ截图20170615172913.png


方案-A

软件架构如下图所示:

image_1beld6u7q1c3g1e5n1nspons1t1p2a.png


orchestration 统一调度和管理弹性伸缩规则,比如当某一个stack中的微服务配置了弹性伸缩规则,orchestration会负责启动service对应的实例,然后调用弹性伸缩API gateway的API来下发配置。配置里面包含了具体某个service所属的租户、环境、stack(通过它们可以在整个系统中唯一索引到一个service);同时,该配置中好包含了每一个service对应的弹性伸缩规则(即上一节中提到的全局参数和规则细节)。

API gateway作为弹性伸缩对外的同一入口,一方面接受来自orchestration的用户配置并读取配置请求;另一方面,接收来自monitor的告警信息。
DB用于存储弹性伸缩需要持久化的配置数据,以及操作日志等信息。

image_1beld7jtr1h235hk1u2m11bs1jg72n.png


Routine负责弹性伸缩的处理逻辑;每一个 service都会通过golang启动一个routine。

image_1beld8575ffo1mh2ngl1bbmb8434.png


由于每一个service的弹性伸缩规则都包含了其作用时间;于是,我们按照这些时间片划分,可以将每一个时间片的弹性伸缩规则看做该service的一个状态。那么,弹性伸缩的状态机的变化,就是由timer和外部事件驱动的。

它们主要是以下三类事件:
服务弹性伸缩规则更新
接收到监控告警
当前strategy的作用时间终结

我们来分析这三种事件的响应方式

服务弹性伸缩规则更新: 当用户在UI上修改某个微服务的弹性伸缩规则时触发该事件,它要求立即结束当前所在的状态(比如,从calendar类型状态中恢复微服务实例的数量,或者从CPU、Memory类型状态中删除配置到监控模块的告警规则...)

接收到监控告警:需要判断当前是否处于CPU、Memory类型状态中,是否当前时间允许触发扩缩容操作。当这些条件都满足,然后确定伸缩的方向、步长、保障允许操作实例数的最大值最小值。然后对目标微服务做扩缩容操作。

当前strategy的作用时间终结:实现中,先对微服务的所有strategy按照时间片段来排序,然后通过与当前时间比较,从而确定当前时间有效的current strategy。但是,通过计算current strategy时间片的结束时间与当前时间的差值来确定何时结束 current strategy的作用范围。于是,只需要设置一个timer,其超时时间为 (end time of current strategy - now time)。

存在的问题与缺陷

这样的设计,存在一定的缺陷:
无法支持多实例部署:因为每一个 Routine本质是一个service的状态机,它的状态被外部事件驱动;意味着在多实例部署时,外部请求被哪个实例接收到,该实例就进入下一个状态;而其它实例并没有同步变化。
数据操作繁琐:orchestration与弹性伸缩各自维护了一套service作为Key的数据表;当用户通过orchestration对某个服务的弹性伸缩规则做增删查改时,数据访问流程复杂;由于各自的数据结构不一致,涉及到繁琐的数据转换。
协程间内部消息通信复杂:由于每一个service对应一个Routine,Routine与各种事件之间相互通信,需要引入较多的golang channel,一旦没用好,极容易导致阻塞。

方案—B

基于方案-A 设计存在的几种弊端,我们对 方案-A 做了一些改进,引入了 方案-B,具体如下

与方案A的差异在于:
在弹性伸缩服务内部,不设数据库。所有弹性伸缩的配置信息均存在orchestration的DB中,一旦有读取弹性伸缩配置的需求,orchestration会直接从自己的数据库中获取;数据结构可以完全基于orchestration的需求来定义,减少了数据转换流程,更加高效。

在orchestration与弹性伸缩之间引入Redis。Redis作为orchestration与弹性伸缩之间传递配置信息,同时作为多个弹性伸缩实例间共享运行时状态数据的介质。它解耦了orchestration与弹性伸缩之间的强依赖,从而使得两者能够基于Redis上的数据各司其职。

将之前基于service有状态的routine 更新为基于timer无状态的routine。新的Routine每秒钟被触发一次,
从Redis读取弹性伸缩服务列表,并按照列表中包含的服务,逐一为每一个服务启动一个service Routine。service Routine无状态,只check当前时刻应该生效的strategy,是否与Redis中已存的current strategy一致。如果不一致,切换stategy(执行相应操作,比如删除上一个strategy发送到监控模块的告警规则,重新下发新的告警规则等),然后终结该service Routine。

支持多个弹性伸缩服务实例。因为Routine内部不再维护一个多实例见无法同步的service的状态机,同时,redis作为多实例的一个共享数据介质,为支持多实例提供了条件。

文末福利:请大家关注'Wise2C'公众号并回复【进群】,睿云小助手会第一时间拉你进入
【Docker企业落地实践群】,我们分享的各个企业案例项目的技术专家与用户代表,正在敬候您的光临,期待大家就项目的更多细节与疑问与群里的大牛们进行咨询探讨。
若需要了解更多有关Wise系列PaaS产品的详情,请与我们的市场团队联系: contact@wise2c.com

睿云智合微信截图_20170926111623.png

0 个评论

要回复文章请先登录注册