HPA通过监控POD的CPU和MEM使用率,实现了默认的弹性伸缩功能。
然而,在实际环境中,伸缩的判断依据通常不单单是CPU/MEM,还可能是:
无法一一列举。
HPA支持我们扩展自定义指标,只要我们让prometheus采集到POD的这些指标,那么HPA就可以通过一定的机制查询prometheus来得到这些指标值,进而做出伸缩依据。
为了最终使用自定义指标,需要使用如下版本的HPA:
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
HPA实现在kube-controller-manager中,是一个控制器。
该控制器周期性扫描所有配置的HPA对象,每个HPA对象关联到1个Deployment,进而关联到N个POD。
控制器将获取这些POD的CPU和MEM资源使用量,求这些POD的平均值,然后做出是否伸缩的决策。
问题来了,HPA是从哪里取到POD的CPU和MEM使用率的呢?
我们也知道,通过下面的命令同样可以查看POD的资源使用量:
[root@10-42-49-142 ~]# kubectl top pods -n abtest11
NAME CPU(cores) MEMORY(bytes)
abtest-bgm-smzdm-com-5d4899774b-6lh5b 20m 79Mi
abtest-test-job-5487cfbf7-gxp44 1m 23Mi
其实top命令和HPA都采用了同一种方法来获取CPU/MEM指标,下面是调用流程:
metrics-server是一个开源项目,它采集集群所有kubelet中的容器CPU/MEM指标数据,保存到内存里。
当HPA调用apiserver的特定URL时,apiserver转发请求给metrics-server得到应答,也就是我们上面kubectl top看到的数据了。
我们完全可以直接请求apiserver的特定URL,方法如下:
kubectl get --raw '/apis/metrics.k8s.io/v1beta1/namespaces/abtest11/pods'| jq .
{
"kind": "PodMetricsList",
"apiVersion": "metrics.k8s.io/v1beta1",
"metadata": {
"selfLink": "/apis/metrics.k8s.io/v1beta1/namespaces/abtest11/pods"
},
"items": [
{
"metadata": {
"name": "abtest-bgm-smzdm-com-5d4899774b-6lh5b",
"namespace": "abtest11",
"selfLink": "/apis/metrics.k8s.io/v1beta1/namespaces/abtest11/pods/abtest-bgm-smzdm-com-5d4899774b-6lh5b",
"creationTimestamp": "2020-06-04T02:52:54Z"
},
"timestamp": "2020-06-04T02:52:12Z",
"window": "30s",
"containers": [
{
"name": "abtest-bgm-smzdm-com",
"usage": {
"cpu": "18782113n",
"memory": "81008Ki"
}
}
]
},
{
"metadata": {
"name": "abtest-test-job-5487cfbf7-gxp44",
"namespace": "abtest11",
"selfLink": "/apis/metrics.k8s.io/v1beta1/namespaces/abtest11/pods/abtest-test-job-5487cfbf7-gxp44",
"creationTimestamp": "2020-06-04T02:52:54Z"
},
"timestamp": "2020-06-04T02:52:12Z",
"window": "30s",
"containers": [
{
"name": "abtest-test-job",
"usage": {
"cpu": "401002n",
"memory": "23668Ki"
}
}
]
}
]
}
这个命令调用apiserver,获取abtest11这个namespace下面所有pod的cpu/mem指标,将得到metrics-server的JSON应答。
这个转发关系是通过配置apiservice资源类型实现的:
kubectl describe apiservice v1beta1.metrics.k8s.io 也就是说,apiserver收到/apis/metrics.k8s.io/v1beta1/前缀的调用,就会转发流量给metrics-server这个service。 当然,metrics-server是需要我们自己部署配置的,具体方法大家去github主页参考即可。 其实呢,HPA Controller逻辑上已经预埋了自定义指标的扩展能力,自定义指标的获取与上述基础指标的获取,原理上是一模一样的。 HPA控制器会通过调用apiserver的另外一个URL前缀,经过代理后调用到k8s-prometheus-adaptor服务,该服务会从prometheus采集到自定义指标,并转换成JSON格式作为返回。 可见, 因为我们有大量指标存储在prometheus中,因此有了k8s-prometheus-adaptor的加持,我们就可以把他们利用到HPA中。 在了解这个开源项目的过程中,我发现最难理解的还是它的工作原理,所以我还是阅读了一下它的代码。 该程序由”定时拉取”和”实时查询”两部分组成,既然有一个实时查询prometheus指标的功能,为什么还要定时拉取?拉取什么?这是我在接触该项目过程中最大的困惑,直接影响到我们对其规则配置的理解。 要解释这个问题,很难离开实际的配置例子,所以我们直接进入本篇博客的主题,根据PHP-FPM进程使用率的HPA。(PHP是依靠启动多个PHP-FPM进程来实现并发的,如果POD里面的FPM进程使用率太高的话,我们认为应该扩容) 给HPA自定义指标叫做fpm_active_rate,代表1个POD的FPM进程使用率。 在prometheus中并没有fpm_active_rate这个东西,只有2类原始数据: pm_status{container_name="abtest-bgm-smzdm-com",instance="10.42.58.17:808",job="kubernetes-php-pods",namespace="abtest",pod_ip="10.42.58.17",pod_name="abtest-bgm-smzdm-com-b56679fdf-2fhlv",pod_template_hash="b56679fdf",state="total processes",zdm_app_env="test",zdm_app_green_blue="green",zdm_app_key="abtest-bgm-smzdm-com",zdm_app_language="php",zdm_app_owner="abtest-bgm-smzdm-com",zdm_app_type="deployment"} pm_status是prometheus采集的原始序列数据。 标签state分为”active processes”和”total processes”,分别代表活跃进程数和总进程数。 标签namespace和pod_name则标识了所归属的POD。 因此,每个POD的fpm_active_rate的计算思路就是用active除以total即可。 现在,我们必须看一下配置文件了: apiVersion: v1 adapter支持配置多条规则,为了搞定fpm_active_rate指标,我只需要一个rule。 adapter会定时执行seriesQuery,这是一个promql。 它过滤出最新采集到的所有pm_status,并且还需要满足state是active processes的,这是干啥呢? 假设prom中只有这两条记录: pm_status{container_name="abtest-bgm-smzdm-com",instance="10.42.58.17:808",job="kubernetes-php-pods",namespace="abtest",pod_ip="10.42.58.17",pod_name="abtest-bgm-smzdm-com-b56679fdf-2fhlv",pod_template_hash="b56679fdf",state="total processes",zdm_app_env="test",zdm_app_green_blue="green",zdm_app_key="abtest-bgm-smzdm-com",zdm_app_language="php",zdm_app_owner="abtest-bgm-smzdm-com",zdm_app_type="deployment"} 那么,上述语句将得到结果: pm_status{container_name="abtest-bgm-smzdm-com",instance="10.42.58.17:808",job="kubernetes-php-pods",namespace="abtest",pod_ip="10.42.58.17",pod_name="abtest-bgm-smzdm-com-b56679fdf-2fhlv",pod_template_hash="b56679fdf",state="active processes",zdm_app_env="test",zdm_app_green_blue="green",zdm_app_key="abtest-bgm-smzdm-com",zdm_app_language="php",zdm_app_owner="abtest-bgm-smzdm-com",zdm_app_type="deployment"} 然后adapter根据resources.overwrites配置,从这条记录中进行提取+映射,得到如下信息: 同时,adapter根据name.as配置,将pm_status名字改为fpm_active_rate,也就是HPA自定义指标的名字。 此后,adapter在内存中记录一个这样的映射关系: fpm_active_rate, abtest11, abtest-bgm-smzdm-com-b56679fdf-2fhlv —-> rule 也就是这个POD到这条配置规则的关系。 可见,定时任务的目的就是『发现』: 发现所有的POD,记录它们与所属rule之间的关系。 之所以要定时构建上述索引关系,其目的是为了满足实时查询。
Name: v1beta1.metrics.k8s.io
Namespace:
Labels:
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"apiregistration.k8s.io/v1beta1","kind":"APIService","metadata":{"annotations":{},"name":"v1beta1.metrics.k8s.io"},"spec":{"...
API Version: apiregistration.k8s.io/v1
Kind: APIService
Metadata:
Creation Timestamp: 2019-11-20T03:58:48Z
Resource Version: 49406253
Self Link: /apis/apiregistration.k8s.io/v1/apiservices/v1beta1.metrics.k8s.io
UID: 6d633213-1d1f-499f-ab16-d6673b9a0ce0
Spec:
Group: metrics.k8s.io
Group Priority Minimum: 100
Insecure Skip TLS Verify: true
Service:
Name: metrics-server
Namespace: kube-system
Port: 443
Version: v1beta1
Version Priority: 100
Status:
Conditions:
Last Transition Time: 2020-05-11T18:47:59Z
Message: all checks passed
Reason: Passed
Status: True
Type: Available
Events:
HPA自定义指标架构
k8s-prometheus-adaptor架构
明确指标计算
pm_status{container_name="abtest-bgm-smzdm-com",instance="10.42.58.17:808",job="kubernetes-php-pods",namespace="abtest",pod_ip="10.42.58.17",pod_name="abtest-bgm-smzdm-com-b56679fdf-2fhlv",pod_template_hash="b56679fdf",state="active processes",zdm_app_env="test",zdm_app_green_blue="green",zdm_app_key="abtest-bgm-smzdm-com",zdm_app_language="php",zdm_app_owner="abtest-bgm-smzdm-com",zdm_app_type="deployment"}
配置文件
kind: ConfigMap
metadata:
name: adapter-config
namespace: custom-metrics
data:
config.yaml: |
rules:
- seriesQuery: '{__name__="pm_status",namespace!="",pod_name!="",state="active processes"}'
resources:
overrides:
namespace:
resource: namespace
pod_name:
resource: pod
name:
as: "fpm_active_rate"
metricsQuery: sum(max_over_time(<<.Series>>{<<.LabelMatchers>>,state="active processes"}[2m])) by (<<.GroupBy>>) / sum(max_over_time(<<.Series>>{<<.LabelMatchers>>,state="total processes"}[2m])) by (<<.GroupBy>>)
定时拉取
pm_status{container_name="abtest-bgm-smzdm-com",instance="10.42.58.17:808",job="kubernetes-php-pods",namespace="abtest",pod_ip="10.42.58.17",pod_name="abtest-bgm-smzdm-com-b56679fdf-2fhlv",pod_template_hash="b56679fdf",state="active processes",zdm_app_env="test",zdm_app_green_blue="green",zdm_app_key="abtest-bgm-smzdm-com",zdm_app_language="php",zdm_app_owner="abtest-bgm-smzdm-com",zdm_app_type="deployment"}
实时查询
你必须付费加入我的知识星球,为有效知识付费是对作者最好的回报。
二维码见下方 或者 右侧边栏。