如何实现K8s Pod自定义指标弹性伸缩

弹性伸缩介绍

自动弹性伸缩(AutoScaling),是Kubernetes的一大功能和亮点。在OpenStack IaaS云计算中也有类似的服务,即Senlin。即基于资源使用情况自动弹性缩容和扩容工作负载。Kubernetes的自动弹性伸缩有两个维度:

  • Cluster Autoscaler:处理K8s集群Node节点伸缩,该功能依赖于IaaS云提供商云主机服务和资源监控服务。
  • Horizontal Pod Autoscaler(HPA):处理Pod自动弹性伸缩副本集,该功能依赖于监控服务采集到的资源指标数据。

简言之,Cluster Autoscaling和Horizontal Pod Autoscaler(HPA)可用于动态调整计算能力以满足系统SLA的要求。

通常,扩/缩容都是根据内存或者CPU资源的使用率实现,但是现实中,很多时候扩/缩容的依据通常是业务监控指标。如何根据业务监控指标来进行扩/缩容,将是本文探讨的内容。

自Kubernetes 1.11版本起,K8s资源采集指标由Resource Metrics API(Metrics Server 实现)和Custom metrics api(Prometheus实现)两种API实现,传统Heapster监控被废弃。前者主要负责采集Node、Pod的核心资源数据,如内存、CPU等;而后者则主要负责自定义指标数据采集,如网卡流量,磁盘IOPS、HTTP请求数、数据库连接数等。

Custom Metrics Server介绍

前面已提过,自heapster被废弃以后,所有的指标数据都从API接口中获取,由此kubernetes将资源指标分为了两种:

  • Core metrics(核心指标):由metrics-server提供API,即 metrics.k8s.io,仅提供Node和Pod的CPU和内存使用情况。
  • Custom Metrics(自定义指标):由Prometheus Adapter提供API,即 custom.metrics.k8s.io,由此可支持任意Prometheus采集到的自定义指标。

想让K8s的HPA,获取核心指标以外的其它自定义指标,则必须部署一套prometheus监控系统,让prometheus采集其它各种指标,但是prometheus采集到的metrics并不能直接给k8s用,因为两者数据格式不兼容,还需要另外一个组件(kube-state-metrics),将prometheus的metrics数据格式转换成k8s API接口能识别的格式,转换以后,因为是自定义API,所以还需要用Kubernetes aggregator在Master节点上的kube-apiserver中注册,以便直接通过/apis/来访问。

image

Custom Metrics组件介绍

  • node-exporter:prometheus的agent端,收集Node级别的监控数据。
  • prometheus:监控服务端,从node-exporter拉取数据并存储为时序数据。
  • kube-state-metrics: 将prometheus中可以用PromQL查询到的指标数据转换成k8s对应的数据格式,即转换成Custerom Metrics API接口格式的数据,但是它不能聚合进apiserver中的功能。
  • k8s-prometheus-adpater:聚合apiserver,即提供了一个apiserver(cuester-metrics-api),自定义APIServer通常都要通过Kubernetes aggregator聚合到apiserver。

Custom Metrics部署

获取部署文件

1
2
# git clone https://github.com/stefanprodan/k8s-prom-hpa
# cd k8s-prom-hpa/

创建monitoring命名空间

1
# kubectl create -f ./namespaces.yaml

编辑prometheus部署文件

1
2
3
# vim ./prometheus/prometheus-dep.yaml
image: prom/prometheus:v2.1.0
//修改为image: prom/prometheus:v2.5.0

将 Prometheus部署到monitoring命名空间

1
# kubectl create -f ./prometheus

生成由Prometheus adapter所需的TLS证书

1
# make certs

编辑custom-metrics-apiserve部署文件

1
2
3
4
5
6
# vim ./custom-metrics-api/custom-metrics-apiserver-deployment.yaml
- --prometheus-url=http://prometheus.monitoring.svc.cluster.local:9090/
//将该参数修改为Prometheus的 service域名地址

image: quay.io/coreos/k8s-prometheus-adapter-amd64:v0.2.0
//修改为image: quay.io/coreos/k8s-prometheus-adapter-amd64:v0.4.1

部署Prometheus自定义api适配器

1
# kubectl create -f ./custom-metrics-api

列出由prometheus提供的自定义指标

1
# kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1" | jq .

查看创建的namespace

1
2
3
4
5
6
# kubectl get namespace
NAME STATUS AGE
default Active 10d
kube-public Active 10d
kube-system Active 10d
monitoring Active 6d23h

查看创建的pod

1
2
3
4
# kubectl get pods -n monitoring
NAME READY STATUS RESTARTS AGE
custom-metrics-apiserver-855cbf8644-6qhmv 1/1 Running 0 2d21h
prometheus-788f78d959-xs74p 1/1 Running 0 2d21h

查看创建的service

1
2
3
4
# kubectl get svc -n monitoring
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
custom-metrics-apiserver ClusterIP 10.233.7.116 <none> 443/TCP 2d21h
prometheus NodePort 10.233.49.23 <none> 9090:30090/TCP 2d21h

到了这里,我们便可以通过 http://节点IP:30090方式访问Prometheus页面。

image

查看新创建的api群组

1
2
3
# kubectl api-versions  | grep metrics
custom.metrics.k8s.io/v1beta1
metrics.k8s.io/v1beta1

列出由prometheus提供的自定义指标

1
2
3
4
5
6
7
8
9
# yum -y install jq

# kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1" | jq .
{
"kind": "APIResourceList",
"apiVersion": "v1",
"groupVersion": "custom.metrics.k8s.io/v1beta1",
"resources": [.............] //输出信息太多,此处省略
}

列示Pod 上的Prometheus 适配器所提供的缺省定制指标。

1
#  kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1" | jq .  |grep "pods/"

获取monitoring命名空间中所有pod的fs_usage_bytes信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#  kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/monitoring/pods/*/fs_usage_bytes" | jq .
{
"kind": "MetricValueList",
"apiVersion": "custom.metrics.k8s.io/v1beta1",
"metadata": {
"selfLink": "/apis/custom.metrics.k8s.io/v1beta1/namespaces/monitoring/pods/%2A/fs_usage_bytes"
},
"items": [
{
"describedObject": {
"kind": "Pod",
"namespace": "monitoring",
"name": "custom-metrics-apiserver-855cbf8644-6qhmv",
"apiVersion": "/__internal"
},
"metricName": "fs_usage_bytes",
"timestamp": "2019-01-15T14:14:22Z",
"value": "233570304"
},
{
"describedObject": {
"kind": "Pod",
"namespace": "monitoring",
"name": "prometheus-788f78d959-xs74p",
"apiVersion": "/__internal"
},
"metricName": "fs_usage_bytes",
"timestamp": "2019-01-15T14:14:22Z",
"value": "36864"
}
]
}

验证

创建一个使用Custom Metrics APIs的Pod,来验证自定义指标弹性伸缩功能。

1
# cd k8s-prom-hpa/

podinfo 应用暴露了一个自定义的度量指标:http_requests_total。Prometheus adapter(即 custom-metrics-apiserver)删除了 _total 后缀并将该指标标记为 counter metric。

在default命名空间中创建podinfo服务,podinfo应用程序暴露了一个自定义的指标,即http_requests。

1
# kubectl create -f ./podinfo/podinfo-svc.yaml,./podinfo/podinfo-dep.yaml

从自定义指标API获取每秒的总请求数:

1
# kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/http_requests" | jq .

m代表milli-units,例如,901m 意味着901 milli-requests (就是大约0.9个请求)。

创建一个HPA,如果请求数超过每秒10,将扩容podinfo这个Pod的副本数。

1
# kubectl create -f ./podinfo/podinfo-hpa-custom.yaml

过几秒钟HPA从自定义指标API取得http_requests的值:

1
2
3
# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
podinfo Deployment/podinfo 899m / 10 2 10 2 1m

用每秒25次请求数给podinfo服务加压
安装hey

1
2
# go get -u github.com/rakyll/hey
# hey -n 10000 -q 5 -c 5 http://<K8S-IP>:31198/healthz

几分钟后,HPA开始扩容该Pod副本数。

1
2
3
4
5
6
7
8
9
10
11
12
13
# kubectl describe hpa
Name: podinfo
Namespace: default
Reference: Deployment/podinfo
Metrics: ( current / target )
"http_requests" on pods: 9059m / 10
Min replicas: 2
Max replicas: 10

Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulRescale 2m horizontal-pod-autoscaler New size: 3; reason: pods metric http_requests above target

可看到,在当前压力测试下,Pod副本将自动扩容到三副本,但不会超过10副本这个最大值。同时三副本Pod已经满足当前负载。

负载测试结束后,HPA向下自动缩容到1个副本Pod。

1
2
3
4
5
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulRescale 5m horizontal-pod-autoscaler New size: 3; reason: pods metric http_requests above target
Normal SuccessfulRescale 21s horizontal-pod-autoscaler New size: 2; reason: All metrics below target

后续

custom metrics大大丰富了K8s pod弹性伸缩的能力,使K8s Pod AutoScaling从资源伸缩向业务应用伸缩方向转变成为现实。限于篇幅,后面将介绍部署Grafana+Influxdb集成Prometheus可视化查看K8s集群资源使用情况。

此外,在生产环境中,应当使用NFS、hostPath等方式持久化存储Prometheus监控数据。

参考资料:
https://github.com/stefanprodan/k8s-prom-hpa

0%