探索Kubernetes HPA
HPA簡介
HPA(Horizontal Pod Autoscaler)是kubernetes(以下簡稱k8s)的一種資源對(duì)象,能夠根據(jù)某些指標(biāo)對(duì)在statefulSet、replicaController、replicaSet等集合中的pod數(shù)量進(jìn)行動(dòng)態(tài)伸縮,使運(yùn)行在上面的服務(wù)對(duì)指標(biāo)的變化有一定的自適應(yīng)能力。
HPA目前支持四種類型的指標(biāo),分別是Resource、Object、External、Pods。其中在穩(wěn)定版本autoscaling/v1中只支持對(duì)CPU指標(biāo)的動(dòng)態(tài)伸縮,在測試版本autoscaling/v2beta2中支持memory和自定義指標(biāo)的動(dòng)態(tài)伸縮,并以annotation的方式工作在autoscaling/v1版本中。
HPA在k8s中的結(jié)構(gòu)
首先可以看一下HPA在k8s中的結(jié)構(gòu),這里找了一個(gè)k8s官方給出的HPA例子,我在關(guān)鍵字段上給出一些注釋方便理解。
- type: Podspods:metric:name: packets-per-second# AverageValue類型的目標(biāo)值,Pods指標(biāo)類型下只支持AverageValue類型的目標(biāo)值target:type: AverageValueaverageValue: 1k# External類型的指標(biāo)- type: Externalexternal:metric:name: queue_messages_ready# 該字段與第三方的指標(biāo)標(biāo)簽相關(guān)聯(lián),(此處官方文檔有問題,正確的寫法如下)selector:matchLabels:env: "stage"app: "myapp"# External指標(biāo)類型下只支持Value和AverageValue類型的目標(biāo)值target:type: AverageValueaverageValue: 30autoscaling/v1版本將metrics字段放在了annotation中進(jìn)行處理。
target共有3種類型:Utilization、Value、AverageValue。Utilization表示平均使用率;Value表示裸值;AverageValue表示平均值。
metrics中的type字段有四種類型的值:Object、Pods、Resource、External。
Resource指的是當(dāng)前伸縮對(duì)象下的pod的cpu和memory指標(biāo),只支持Utilization和AverageValue類型的目標(biāo)值。
Object指的是指定k8s內(nèi)部對(duì)象的指標(biāo),數(shù)據(jù)需要第三方adapter提供,只支持Value和AverageValue類型的目標(biāo)值。
Pods指的是伸縮對(duì)象(statefulSet、replicaController、replicaSet)底下的Pods的指標(biāo),數(shù)據(jù)需要第三方的adapter提供,并且只允許AverageValue類型的目標(biāo)值。
External指的是k8s外部的指標(biāo),數(shù)據(jù)同樣需要第三方的adapter提供,只支持Value和AverageValue類型的目標(biāo)值。
HPA動(dòng)態(tài)伸縮的原理
HPA在k8s中也由一個(gè)controller控制,controller會(huì)間隔循環(huán)HPA,檢查每個(gè)HPA中監(jiān)控的指標(biāo)是否觸發(fā)伸縮條件,默認(rèn)的間隔時(shí)間為15s。一旦觸發(fā)伸縮條件,controller會(huì)向k8s發(fā)送請(qǐng)求,修改伸縮對(duì)象(statefulSet、replicaController、replicaSet)子對(duì)象scale中控制pod數(shù)量的字段。k8s響應(yīng)請(qǐng)求,修改scale結(jié)構(gòu)體,然后會(huì)刷新一次伸縮對(duì)象的pod數(shù)量。伸縮對(duì)象被修改后,自然會(huì)通過list/watch機(jī)制增加或減少pod數(shù)量,達(dá)到動(dòng)態(tài)伸縮的目的。
HPA伸縮過程敘述
HPA的伸縮主要流程如下:
1.判斷當(dāng)前pod數(shù)量是否在HPA設(shè)定的pod數(shù)量區(qū)間中,如果不在,過小返回最小值,過大返回最大值,結(jié)束伸縮。
2.判斷指標(biāo)的類型,并向api server發(fā)送對(duì)應(yīng)的請(qǐng)求,拿到設(shè)定的監(jiān)控指標(biāo)。一般來說指標(biāo)會(huì)根據(jù)預(yù)先設(shè)定的指標(biāo)從以下三個(gè)aggregated APIs中獲取:metrics.k8s.io、custom.metrics.k8s.io、 external.metrics.k8s.io。其中metrics.k8s.io一般由k8s自帶的metrics-server來提供,主要是cpu,memory使用率指標(biāo),另外兩種需要第三方的adapter來提供。custom.metrics.k8s.io提供自定義指標(biāo)數(shù)據(jù),一般跟k8s集群有關(guān),比如跟特定的pod相關(guān)。external.metrics.k8s.io同樣提供自定義指標(biāo)數(shù)據(jù),但一般跟k8s集群無關(guān)。許多知名的第三方監(jiān)控平臺(tái)提供了adapter實(shí)現(xiàn)了上述api(如prometheus),可以將監(jiān)控和adapter一同部署在k8s集群中提供服務(wù),甚至能夠替換原來的metrics-server來提供上述三類api指標(biāo),達(dá)到深度定制監(jiān)控?cái)?shù)據(jù)的目的。
3.根據(jù)獲得的指標(biāo),應(yīng)用相應(yīng)的算法算出一個(gè)伸縮系數(shù),并乘以目前pod數(shù)量獲得期望pod數(shù)量。系數(shù)是指標(biāo)的期望值與目前值的比值,如果大于1表示擴(kuò)容,小于1表示縮容。指標(biāo)數(shù)值有平均值(AverageValue)、平均使用率(Utilization)、裸值(Value)三種類型,每種類型的數(shù)值都有對(duì)應(yīng)的算法。以下幾點(diǎn)值得注意:如果系數(shù)有小數(shù)點(diǎn),統(tǒng)一進(jìn)一;系數(shù)如果未達(dá)到某個(gè)容忍值,HPA認(rèn)為變化太小,會(huì)忽略這次變化,容忍值默認(rèn)為0.1。
HPA擴(kuò)容算法是一個(gè)非常保守的算法。如果出現(xiàn)獲取不到指標(biāo)的情況,擴(kuò)容時(shí)算最小值,縮容時(shí)算最大值;如果需要計(jì)算平均值,出現(xiàn)pod沒準(zhǔn)備好的情況,平均數(shù)的分母不計(jì)入該pod。
一個(gè)HPA支持多個(gè)指標(biāo)的監(jiān)控,HPA會(huì)循環(huán)獲取所有的指標(biāo),并計(jì)算期望的pod數(shù)量,并從期望結(jié)果中獲得最大的pod數(shù)量作為最終的伸縮的pod數(shù)量。一個(gè)伸縮對(duì)象在k8s中允許對(duì)應(yīng)多個(gè)HPA,但是只是k8s不會(huì)報(bào)錯(cuò)而已,事實(shí)上HPA彼此不知道自己監(jiān)控的是同一個(gè)伸縮對(duì)象,在這個(gè)伸縮對(duì)象中的pod會(huì)被多個(gè)HPA無意義地來回修改pod數(shù)量,給系統(tǒng)增加消耗,如果想要指定多個(gè)監(jiān)控指標(biāo),可以如上述所說,在一個(gè)HPA中添加多個(gè)監(jiān)控指標(biāo)。
4.檢查最終的pod數(shù)量是否在HPA設(shè)定的pod數(shù)量范圍的區(qū)間,如果超過最大值或不足最小值都會(huì)修改為最大值或最小值。然后向k8s發(fā)出請(qǐng)求,修改伸縮對(duì)象的子對(duì)象scale的pod數(shù)量,結(jié)束一個(gè)HPA的檢查,獲取下一個(gè)HPA,完成一個(gè)伸縮流程。
HPA的應(yīng)用場景
HPA的特性結(jié)合第三方的監(jiān)控應(yīng)用,使得部署在HPA伸縮對(duì)象(statefulSet、replicaController、replicaSet)上的服務(wù)有了非常靈活的自適應(yīng)能力,能夠在一定限度內(nèi)復(fù)制多個(gè)副本來應(yīng)對(duì)某個(gè)指標(biāo)的急劇飆升,也可以在某個(gè)指標(biāo)較小的情況下刪除副本來讓出計(jì)算資源給其他更需要資源的應(yīng)用使用,維持整個(gè)系統(tǒng)的穩(wěn)定。非常適合于一些流量波動(dòng)大,機(jī)器資源吃緊,服務(wù)數(shù)量多的業(yè)務(wù)場景,如:電商服務(wù)、搶票服務(wù)、金融服務(wù)等。
k8s-prometheus-adapter
前文說到,許多監(jiān)控系統(tǒng)通過adapter實(shí)現(xiàn)了接口,給HPA提供指標(biāo)數(shù)據(jù)。在這里我們具體介紹一下prometheus監(jiān)控系統(tǒng)的adapter。
prometheus是一個(gè)知名開源監(jiān)控系統(tǒng),具有數(shù)據(jù)維度多,存儲(chǔ)高效,使用便捷等特點(diǎn)。用戶可以通過豐富的表達(dá)式和內(nèi)置函數(shù),定制自己所需要的監(jiān)控?cái)?shù)據(jù)。
prometheus-adapter在prometheus和api-server中起到了適配者的作用。prometheus-adapter接受從HPA中發(fā)來,通過apiserver aggregator中轉(zhuǎn)的指標(biāo)查詢請(qǐng)求,然后根據(jù)內(nèi)容發(fā)送相應(yīng)的請(qǐng)求給prometheus拿到指標(biāo)數(shù)據(jù),經(jīng)過處理后返回給HPA使用。prometheus可以同時(shí)實(shí)現(xiàn)metrics.k8s.io、custom.metrics.k8s.io、 external.metrics.k8s.io三種api接口,代替k8s自己的matrics-server,提供指標(biāo)數(shù)據(jù)服務(wù)。
prometheus-adapter部署能否成功的關(guān)鍵在于配置文件是否正確。配置文件中可以設(shè)定需要的指標(biāo)以及對(duì)指標(biāo)的處理方式,以下是一份簡單的配置文件,加上注釋以稍做解釋。
# 指標(biāo)規(guī)則,可以多個(gè)規(guī)則共存,上一個(gè)規(guī)則的結(jié)果會(huì)傳給下一個(gè)規(guī)則 rules:# 計(jì)算指標(biāo)數(shù)據(jù)的表達(dá)式- metricsQuery: sum(rate(<<.Series>>{<<.LabelMatchers>>}[5m])) by (<<.GroupBy>>)# 指標(biāo)重命名,支持正則表達(dá)式。這里表示刪除指標(biāo)名字中的"_seconds_total"name:as: ""matches: (.*)_seconds_total$# 指標(biāo)與k8s資源通過標(biāo)簽關(guān)聯(lián),這里將指標(biāo)通過標(biāo)簽與k8s的namspace和pod相互關(guān)聯(lián)resources:overrides:namespace:resource: namespacepod:resource: pod# 過濾指標(biāo)條件seriesFilters: []# 指標(biāo)查詢表達(dá)式,可以根據(jù)標(biāo)簽等條件,篩選特定的指標(biāo)seriesQuery: '{namespace!="",pod!=""}'metricsQuery字段會(huì)在k8s請(qǐng)求時(shí)執(zhí)行,其中,“<<” “>>”是go的模板語法,Series表示指標(biāo)名稱,LabelMatchers表示指標(biāo)與k8s對(duì)象名稱匹配的標(biāo)簽鍵值對(duì),GroupBy表示指標(biāo)數(shù)值歸并的標(biāo)簽名。
不太好理解,這里截取官方文檔中的一個(gè)例子來進(jìn)行說明,看了就應(yīng)該明白了:
For instance, suppose we had a series http_requests_total (exposed ashttp_requests_per_second in the API) with labels service, pod,ingress, namespace, and verb. The first four correspond to Kubernetes resources. Then, if someone requested the metric pods/http_request_per_second for the pods pod1 and pod2 in the somens namespace, we’d have:- Series: "http_requests_total"- LabelMatchers: "pod=~\"pod1|pod2",namespace="somens"- GroupBy: podresources字段是k8s資源對(duì)象與指標(biāo)關(guān)聯(lián)的重要字段,本質(zhì)上是根據(jù)指標(biāo)的標(biāo)簽值與k8s資源對(duì)象名稱進(jìn)行匹配,resources字段告訴系統(tǒng),應(yīng)該用指標(biāo)的哪個(gè)標(biāo)簽的標(biāo)簽值來匹配k8s資源對(duì)象名稱。有兩個(gè)方法關(guān)聯(lián)資源,一種是通過overrides,將特定的標(biāo)簽與k8s資源對(duì)象綁定起來,當(dāng)k8s請(qǐng)求指標(biāo)的時(shí)候,會(huì)將資源對(duì)象名稱與這個(gè)特定的標(biāo)簽值進(jìn)行比較,來分辨指標(biāo)具體是哪個(gè)對(duì)象的;另一種是template,通過go語言模板語法將k8s資源對(duì)象名轉(zhuǎn)化成標(biāo)簽名,進(jìn)行匹配。
第二種方法,不太好理解,同樣截取官方文檔中的一個(gè)例子來進(jìn)行說明:
# any label `kube_<group>_<resource>` becomes <group>.<resource> in Kubernetes resources: template: "kube_<<.Group>>_<<.Resource>>"部署一個(gè)監(jiān)控自定義指標(biāo)的HPA
1.部署prometheus應(yīng)用,使其正常工作。可以使用官方的helm包快捷部署,之后的應(yīng)用也基本以helm包的方式部署,不再贅述。
2.部署需要伸縮的應(yīng)用。這里我選擇了一個(gè)簡單的nginx服務(wù),部署為deployment作為伸縮對(duì)象。
3.在應(yīng)用的命名空間下,部署提供自定義指標(biāo)的應(yīng)用。這里我選擇了官方的prometheus-node-exporter,并以nodeport的方式暴露數(shù)據(jù)端口,作為自定義指標(biāo)數(shù)據(jù)的來源。這個(gè)應(yīng)用會(huì)以daemonSet的方式運(yùn)行在k8s集群的每個(gè)node上,并對(duì)外開放在自己node上獲取到的指標(biāo)。
在prometheus的界面上已經(jīng)可以看到node-exporter暴露出來的指標(biāo)了
4.部署prometheus-adapter應(yīng)用。在helm包的values中修改其配置文件,配置文件如下
上述配置文件將secondstotal和_total結(jié)尾的指標(biāo)用prometheus的內(nèi)置函數(shù)進(jìn)行了速率計(jì)算,并去掉對(duì)應(yīng)的后綴作為指標(biāo)名稱。
我們在k8s中利用kubectl查看指標(biāo)是否能夠獲得到指標(biāo)
kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/
你會(huì)看到所有的你能夠獲得的指標(biāo)名稱,但是名字和數(shù)據(jù)已經(jīng)和原來的指標(biāo)有所不同了,因?yàn)槲覀冊赼dapter的配置文件中做了一定程度的修改。
然后我們獲取一下node_cpu這個(gè)指標(biāo),看看是否能夠正確地顯示出來
kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/namespaces/my-nginx/pods/*/node_cpu | jq
這個(gè)命令能夠顯示my-nginx這個(gè)namspace下的所有pod的node_cpu指標(biāo)的數(shù)據(jù),結(jié)果如下
{"kind": "MetricValueList","apiVersion": "custom.metrics.k8s.io/v1beta1","metadata": {"selfLink": "/apis/custom.metrics.k8s.io/v1beta1/namespaces/my-nginx/pods/%2A/node_cpu"},"items": [{"describedObject": {"kind": "Pod","namespace": "my-nginx","name": "prometheus-node-exporter-b25zl","apiVersion": "/v1"},"metricName": "node_cpu","timestamp": "2019-10-29T03:33:47Z","value": "3822m"}] }ok,到這里,說明所有的組件工作正常,hpa能夠順利地得到這個(gè)指標(biāo)。需要注意的是HPA和監(jiān)控對(duì)象,伸縮對(duì)象一定要部署在同一個(gè)命名空間下,不然會(huì)獲取不到相應(yīng)的指標(biāo)。
5.部署hpa,其yaml文件如下
apiVersion: autoscaling/v2beta2 kind: HorizontalPodAutoscaler metadata:name: hpa-asdfvsnamespace: my-nginx spec:scaleTargetRef:apiVersion: apps/v1beta1kind: Deploymentname: my-nginxminReplicas: 1maxReplicas: 10metrics:- type: Objectobject:metric:name: node_cpudescribedObject:apiVersion: v1kind: Podname: prometheus-node-exporter-b25zltarget:type: Valuevalue: 9805m我們將根據(jù)prometheus-node-exporter這個(gè)pod的node_cpu這個(gè)指標(biāo)來動(dòng)態(tài)伸縮我們的nginx應(yīng)用。
我們來獲取一下這個(gè)hpa
kubectl get horizontalPodAutoscaler -n my-nginx hpa-asdfvs -oyaml
apiVersion: autoscaling/v1 kind: HorizontalPodAutoscaler metadata:annotations:autoscaling.alpha.kubernetes.io/conditions: '[{"type":"AbleToScale","status":"True","lastTransitionTime":"2019-10-29T02:54:50Z","reason":"ReadyForNewScale","message":"recommendedsize matches current size"},{"type":"ScalingActive","status":"True","lastTransitionTime":"2019-10-29T03:05:24Z","reason":"ValidMetricFound","message":"theHPA was able to successfully calculate a replica count from Pod metric node_cpu"},{"type":"ScalingLimited","status":"False","lastTransitionTime":"2019-10-29T02:54:50Z","reason":"DesiredWithinRange","message":"thedesired count is within the acceptable range"}]'autoscaling.alpha.kubernetes.io/current-metrics: '[{"type":"Object","object":{"target":{"kind":"Pod","name":"prometheus-node-exporter-b25zl","apiVersion":"v1"},"metricName":"node_cpu","currentValue":"3822m"}}]'autoscaling.alpha.kubernetes.io/metrics: '[{"type":"Object","object":{"target":{"kind":"Pod","name":"prometheus-node-exporter-b25zl","apiVersion":"v1"},"metricName":"node_cpu","targetValue":"9805m"}}]'kubectl.kubernetes.io/last-applied-configuration: |{"apiVersion":"autoscaling/v2beta2","kind":"HorizontalPodAutoscaler","metadata":{"annotations":{},"name":"hpa-asdfvs","namespace":"my-nginx"},"spec":{"maxReplicas":10,"metrics":[{"object":{"describedObject":{"apiVersion":"v1","kind":"Pod","name":"prometheus-node-exporter-b25zl"},"metric":{"name":"node_cpu"},"target":{"type":"Value","value":"9805m"}},"type":"Object"}],"minReplicas":1,"scaleTargetRef":{"apiVersion":"apps/v1beta1","kind":"Deployment","name":"my-nginx"}}}creationTimestamp: "2019-10-29T02:54:45Z"name: hpa-asdfvsnamespace: my-nginxresourceVersion: "164701"selfLink: "/apis/autoscaling/v1/namespaces/my-nginx/horizontalpodautoscalers/hpa-asdfvs"uid: 76fa6a19-f9f7-11e9-8930-0242c5ccd054 spec:maxReplicas: 10minReplicas: 1scaleTargetRef:apiVersion: apps/v1beta1kind: Deploymentname: my-nginx status:currentReplicas: 1desiredReplicas: 1lastScaleTime: "2019-10-29T03:06:10Z"可以看到hpa以annotation的方式在v1版本工作,并將matrics字段寫入了annotation中。在annotation的condition字段中,我們清楚地看到HPA已經(jīng)獲取到了這個(gè)指標(biāo)。之后我們可以嘗試降低target值來讓pod擴(kuò)展,或者增加target值來使pod縮減。到這里一個(gè)監(jiān)控自定義指標(biāo)的HPA就部署成功了。
總結(jié)
以上是生活随笔為你收集整理的探索Kubernetes HPA的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 动作捕捉
- 下一篇: 平板实现app2sd功能