本文尝试以通俗的方式向读者介绍K8s的Pod的自动化横向扩缩容的领域模型。其实是以领域驱动设计(DDD)的思考方式来学习一项技术。希望能对读者帮助。
问题是什么
当要理解一个解决方案时,我们从问题域开始理解,会更容易。
比如存在一个场景:基于Pod的CPU使用率进行自动化扩容。当一个Pod的CPU使用率大于60%,并持续15秒时,我们就希望Pod的数量从10个扩到13个。
要实现这个场景,我们推断K8s应该存在一种机制方便我们实现这个场景。这种机制就是HPA(Horizontal Pod Autoscaler)。
换位思考一下,如果你是HPA机制的使用者,你会如何使用HPA呢?
你可能会配置如下:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-apache
minReplicas: 1
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
(如果看不懂,也没有关系,你只需要知道,你可以通过代码来定义HPA的行为)
作为用户,你只需要通过YAML文件定义清楚你的期望就可以了。至于如何实现,是由HPA机制的实现者实现的。
就好比司机开车过程中是不需要考虑动力系统是由电机实现的,还是由柴油引擎实现的。这就是“接口是用户的”的含义了。当然,作为汽车的设计者,你就必须考虑动力系统的设计与实现。
同样的,作为K8s的设计者,就必须考虑HPA机制的设计与实现。
当用户通过kubectl apply -f hpa.yaml命令部署HPA到K8s中时,我们的K8s该做什么呢?它需要考虑以下问题:
- 我该如何拿到用户Pod的某一个指标的值呢?
- 多久拿一次指标呢?
- 当这个指标不是CPU,内存等资源指标,而是应用本身的自定义指标呢?如果指标数据甚至不在K8s内部呢?
- Pod本来已经是由控制器控制的了,我们是该控制控制器去完成工作,还是直接控制Pod去完成?
- 当用户需要根据多个指标的值共同决定扩容时,我们该如何权衡其中的策略?
- 如果Pod中有多个容器,如何根据其中一个容器的指标进行扩容?
- 以上说的都是扩容,如何缩容呢?
- 缩容太快了,怎么办?即如何保证缩容时的稳定性?
笔者对以上相对口语的表述进行抽象。当我们作为K8s的HPA的设计者与实现者时,我们需要考虑以下问题:
- 指标来源问题;
- 指标的数量问题:即不仅只支持针对一个指标的扩缩容;
- 指标类型支持问题:资源指标、自定义指标、外部指标;
- Pod扩缩的控制器实现;
- 自定义Pod扩缩行为:扩容行为应该可以由用户自定义。
接下来我们分别看下K8s是如何解决以上问题的。
指标来源与指标类型
HPA控制器会从Metrics API中获取指标。而Metrics API根据不同的指标类型去不同的Metrics API的实现中去获取指标。
Metrics API支持三类指标:
- 资源指标:CPU和内存的使用率指标,由Metrics Server提供。Metrics Server需要单独安装;
- 自定义指标:比如应用的连接数大小。最终由你的Custom Metrics API的实现者提供;
- 外部自定义指标:与K8s 对象无关的自定义指标。
自定义指标与外部自定义指标的区别是该自定义指标是否来自于与应用同处于同一个K8s集群。
虽然定义是这样,这个界线也可以被打破,如下图:
Pod扩缩的控制器实现及自定义Pod扩缩行为
当HPA拿到指标后,在哪里,又该如何实现对Pod的数量的控制呢?
既然Pod已经存在Deployment 及其 Replicate Controller了,没有必要再重新设计一个新的控制器,在现有的控制器之上进行操作即可。
这部分逻辑是HPA的核心逻辑。具体实现在kube-controller-manager。网络上已经有很多源码分析。此处不再赘述。
小结
本文虽说的是K8s的HPA的机制的领域模型,但是,你发现这个领域模型也适用于非K8s的部署方式。
总的来说,关于HPA你只需要记住两个问题:
- 指标从何而来
- 如何根据指标进行扩缩容
然后找到这两个问题答案。当我们想通这两个问题后,即使不在K8s中实现HPA,我们也会有思路实现。