您的位置:首页 > 其它

实现基于内存的HPA

2017-03-15 16:15 141 查看
K8S HPA(Horizontal Pod Autoscaler)资源实现了基于CPU利用率的弹性伸缩功能,但并不支持基于内存的弹性伸缩功能。我们自己实现了该功能,在此与各位分享。

实现原理

我之前有一篇文章分析了其源码,参考此处。我的实现也基本参考了K8S HPA的思路,源码可以参考此处

MemHpa资源

首先需要一个类似HPA的MemHpa资源来定义弹性伸缩的相关规则:replicas的上下限、利用率阈值和引用的pod controller。可参考这里

MemHpa是自定义资源,如何让K8S将其管理起来呢?这里就涉及到K8S ThirdPartyResource的概念,可参考这里。原理是:

通过ThirdPartyResource类型的资源可以将自定义的资源注册到K8S中,注册后K8S API便会为该自定义资源暴露对应的endpoint,URL为/apis/<group>/v1/namespaces/<namespace>/<kind>/

ThirdPartyResource只需指定.metadata.name和.versions,name格式为kind.group。K8S会将kind中的‘-’号转为驼峰式,如mem-hpa转为MemHpa。

自定义资源除了apiVersion、kind和metadata之外的其他字段都是自定义的。

以我的实现为例,ThirdPartyResource name为mem-hpa.xinhuang.com,version添加了v1。注册后endpoint为/apis/xinhuang.com/v1/namespaces/<namespace>/memhpas。访问该endpoint返回资源的apiVersion为xinhuang.com/v1,kind为MemHpa和MemHpaList。

有了MemHpa资源后,还需要封装对应的API来访问。可以参考源码,这里我只封装了Create、Update、Delete、Get、List和Watch操作。

metrics获取

我的实现中使用了Prometheus来获取metrics,主要是因为我们团队准备使用Prometheus作为K8S的监控组件,其能通过Alertmanager实现用户级别的监控告警功能。部署方法可以参考我之前的一篇文章,查看这里

Prometheus client可以参考源码。Prometheus的query语法可以参考这里

K8S API访问

我的思路是将MemHpa controller运行在kube-system下的Pod中,这样可以使用service account便可以访问K8S API,还带来几点好处:

可将Pod交由RC、Deployment等资源管理起来

可通过Service访问Prometheus,解决了服务发现问题

使用k8s.io/client-go/1.4/rest的InClusterConfig()方法就可以获得我们需要的配置,从而创建client。

scale算法

我基本上参考了HPA的实现逻辑。每隔30秒或MemHpa资源有变动时,便会判断是否需要进行scale。

内存使用率计算

公式是利用率=所有pod的所有容器的metrics总和/内存limit总和。

metrics是通过MemHpa的.spec.scaleTargetRef.name查询Prometheus得到的,这里使用了模糊查询,因此可能存在多余的pod,因此在计算总和时会进行过滤检查。

limit是用过.spec.scaleTargetRef查询到pod controller的scale子资源,并通过其selector来查询出所有Pod,再统计limit总和。

查询出来的Pod也会存在干扰项:尚未运行的Pod以及未查到metrics的Pod。对于干扰项的处理可以参考replica-calculator.go的GetReplicas方法。

scale条件判断

得到当前内存利用率之后,再根据其与MemHpa的.spec.targetUtilizationPercentage的比值来决定是否触发scale,比值小于0.9则缩容,比值大于1.1则扩容。伸缩后的replicas=Ceil(比值*当前replicas)。此外,为避免频繁的伸缩还添加了时间窗口机制,距离上次伸缩的一段时间内是无法再次触发伸缩的。

满足条件后,通过修改scale子资源的replicas便可以完成伸缩操作。

踩过的坑

基本实现原理大概就是这些内容了,接下来说说实现期间踩到的一些坑。

这里有两个坑。在封装MemHpa client时,底层使用了client-go的RESTClient对象,这样可以很方便的封装API。但其内部还存在一些校验机制,我在使用封装好的API访问MemHpa资源时,在解码时会报错。这里需要我们先将自定义的MemHpa资源注册,可参考源码

另一个坑是在注册之后,我可以获取到MemHpa对象,其字段均被初始化为零值。这里有个bug,详情参考这里。解决办法是将ObjectMeta对象组合到MemHpa对象而不是嵌入,并添加相关接口函数,具体参考源码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  kubernetes memory HPA