实现基于内存的HPA
2017-03-15 16:15
141 查看
K8S HPA(Horizontal Pod Autoscaler)资源实现了基于CPU利用率的弹性伸缩功能,但并不支持基于内存的弹性伸缩功能。我们自己实现了该功能,在此与各位分享。
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操作。
Prometheus client可以参考源码。Prometheus的query语法可以参考这里。
可将Pod交由RC、Deployment等资源管理起来
可通过Service访问Prometheus,解决了服务发现问题
使用k8s.io/client-go/1.4/rest的InClusterConfig()方法就可以获得我们需要的配置,从而创建client。
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子资源的replicas便可以完成伸缩操作。
这里有两个坑。在封装MemHpa client时,底层使用了client-go的RESTClient对象,这样可以很方便的封装API。但其内部还存在一些校验机制,我在使用封装好的API访问MemHpa资源时,在解码时会报错。这里需要我们先将自定义的MemHpa资源注册,可参考源码。
另一个坑是在注册之后,我可以获取到MemHpa对象,其字段均被初始化为零值。这里有个bug,详情参考这里。解决办法是将ObjectMeta对象组合到MemHpa对象而不是嵌入,并添加相关接口函数,具体参考源码
实现原理
我之前有一篇文章分析了其源码,参考此处。我的实现也基本参考了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对象而不是嵌入,并添加相关接口函数,具体参考源码
相关文章推荐
- 基于NETLINK的内核与用户空间共享内存的实现
- 基于NETLINK的内核与用户空间共享内存的实现[转]
- h.264并行解码算法2D-Wave实现(基于多核非共享内存系统)
- Nginx slab的实现 --- 第二篇“基于页的内存分配”
- 基于C语言的内存文件实现
- 基于NETLINK的内核与用户空间共享内存的实现
- 使用Scikit-learn实现基于内存的协同过滤算法(使用movieLens数据集)
- #内存管理的艺术# 之 Nginx slab的实现 --- 第二篇“基于页的内存分配”
- Linux 基于IPC机制实现进程间的共享内存处理
- 撸代码--类QQ聊天实现(基于linux 管道 信号 共享内存)
- 基于allocator实现的内存共享
- linux基于信号量同步的共享内存IPC实现
- 撸代码--类QQ聊天实现(基于linux 管道 信号 共享内存)
- #内存管理的艺术# 之 Nginx slab的实现 --- 第三篇“基于块的内存分配”
- 基于Android实现桌面悬浮清内存app概述
- 基于ring0层的内存搜索,实现枚举线程.
- C#实现基于链表的内存记事本实例
- 内存管理器(八)基于Glibc malloc 实现内存管理程序
- h.264并行解码算法3D-Wave实现(基于多核共享内存系统)
- RSA - 基于内存代码实现