大家好,我是藍胖子,都知道,k8s擁有自動擴縮容機制HPA,我們能夠透過配置針對不同的擴縮容場景進行自動擴縮容,往往初學者在面對其中繁多配置的時候會學了又忘記,今天我將會以一種不同的視角,結合api server 請求 來探索這部分的配置,看完本篇,應該會對擴縮容這部分配置會有更深的理解。
自動擴縮容架構圖
我們先來看一下自動擴縮容的原理,在k8s中HPA這個模組的邏輯會定時請求api server 獲取相應的pod或者CRD或者其他資源的指標資訊,這些指標資訊是使用者建立HPA的yaml配置檔案時指定的。
api server收到請求後,根據請求的api group,api version 轉發給內部的api service服務進行處理,當我們想讓k8s借用prometheus的相關指標進行擴縮容時,就需要在叢集裡用api service的方式安裝prometheus adapter,它會將發往api server的請求經過包裝,轉發到prometheus伺服器獲取對應指標資訊,然後將結果經過封裝返回給客戶端即HPA模組。HPA模組收到指標後,在根據自身配置檔案中的target值判斷是否需要進行自動擴縮容。
api server 處理請求的方式
既然提到了prometheus adapter是以api service 方式安裝到k8s叢集中的,我再對api server的架構已經處理請求的方式再闡述下。
api server 處理請求的方式是鏈式的,你可以簡單的理解為api server裡有多個http server ,當某個請求的路徑不屬於某個http server處理範疇內的話,會將這個請求委託給下一個http server進行處理。同時,k8s允許使用者自定義api service作為http server,prometheus adapter 就是一個自定義的api service。
api server 請求路徑格式
向api server傳送http請求,請求格式是按一定規則進行組裝的,我主要檢視了HPA模組原始碼,所以拿這塊去舉例,hpa發往api server的請求是將api version和api group 以及要請求資源的名稱空間,資源名拼接到一起組成的路徑。如下:
不同的HPA指標型別這個路徑的拼接會有所不同(下面會詳細講到),但是整體的api風格是和這個一致的。
當HPA在向api server傳送請求的時候則是根據不同的擴縮容指標型別選擇了不同的api group 去傳送請求。
❗️❗️? 注意,這裡HPA選擇的api group是k8s這部分程式碼已經固定好了的,所以prometheus adapter在以api service安裝時指定的api group 需要和這裡吻合。目前針對指標型別,HPA會從metrics.k8s.io, customer.metrics.k8s.io, external.metrics.k8s.io這3個api group種選取對應的group。
HPA 擴縮容的4種指標型別
接下來,我們來詳細看下,HPA擴縮容的4種指標型別。
Pods
先看第一種pods型別,它表示的是由pod產生的指標, 其在HPA宣告的配置yaml檔案裡寫法如下,
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: sample-app
namespace: default
spec:
maxReplicas: 10
minReplicas: 2
metrics:
- pods:
metric:
name: http_requests
selector:
matchLabels:
<label-key>: <label-value>
target:
averageValue: 500m
type: AverageValue
type: Pods
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: sample-app
可以看到spec.metrics.type 值為pods型別,HPA的pods 指標型別 是指pod這個資源物件產生的指標,其中定義了指標名為http_requests,最終發往api server的url path如下所示,
/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/http_requests?labelSelector=<label-key>=<label-value>
api server 收到這個請求後會將請求轉發給prometheus adapter,那麼prometheus adapter 又是如何將http_requests 與具體的prometheus中的指標對應起來的呢?
prometheus adapter在啟動的時候我們會配置一個規則配置檔案,在這個檔案定義了這個對映關係,下面是這針這種型別的指標配置規則部分,
rules:
- seriesQuery: 'http_requests_total{}'
resources:
overrides:
kubernetes_namespace: {resource: "namespace"}
kubernetes_pod_name: {resource: "pods"}
name:
matches: "http_requests_total"
as: "http_requests"
metricsQuery: 'sum(rate(<<.Series>>{<<.LabelMatchers>>}[2m])) by (<<.GroupBy>>)'
我們將hpa配置檔案和發往api server的請求以及prometheus adapter的規則檔案結合起來,看看prometheus adapter 規則檔案裡那些模板變數的含義。
首先是hpa的配置檔案中指定了metric.name是http_requests,http_requests在prometheus adapter的配置檔案裡是將prometheus的http_requests_total與之對應了起來,並且從規則配置檔案的resources.overrides 配置中可以發現namespace和pods資源在指標http_requests_total中會有kubernetes_namespace和kubernetes_pod_name標籤與之對應,這層關係其實主要是為了metricsQuery 中模板變數的替換。
metricsQuery 中 <<.Series>> 其實就是seriesQuery這部分。
<<.LabelMatchers>> 是篩選指標時的標籤,在hpa裡面我們指定了metric.selector,發往api server的請求裡的引數labelSelector就會替代<<.LabelMatchers>>模板變數,同時發往api server請求中的namespace的值也會寫到<<.LabelMatchers>>中,並且namespace的對應標籤名就是resources.overrides中定義的kubernetes_namespace。
<<.GroupBy>> 變數在這裡會將k8s的resource資源型別作為分組的維度,並且在這個場景下,在發往api server的請求中,k8s的資源是pods型別,而pods型別在指標中的標籤名是kubernetes_pod_name。
所以最終,在prometheus 中進行查詢時執行的promql語句為,
sum(rate(http_requests_total{"kubernetes_namespace":"default","<label-key>":"<label-value>"}[2m])) by (kubernetes_pod_name)
Object
在看了pods型別的hpa指標後,我們再來看看Object型別的指標是如何配置的,因為在k8s裡資源型別除了pod型別,還有其他型別,所以如果由其他資源型別產生的指標,則由Object來表示。
先看下hpa的yaml配置檔案是如何寫的。
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: sample-app
namespace: default
spec:
maxReplicas: 10
minReplicas: 2
metrics:
- object:
metric:
name: requests-per-second
describedObject:
apiVersion: extensions/v1beta1
kind: Ingress
name: main-route
target:
type: Value
value: 2k
type: Object
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: sample-app
發往api server的請求格式如下
/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/ingress/main-route/requests-per-second
prometheus adapter配置此類規則和Pods型別類似,模板變數解析方式也是類似,唯一有點不一樣的是此時的<<.GroupBy>> 模板變數會被ingress/main-route 也就是資源名加上資源示例名替代。
Resource, ContainerResource
接著看一下hpa中的Resource型別的指標配置,它表示對pod的cpu或者記憶體值來進行擴縮容,本質上可以用Pods 型別的配置來代替這部分配置,那為什麼還有Resource型別呢?因為Resource出來的時候還沒有Pods 型別。
Kubernetes 1.20 在 HorizontalPodAutoscaler (HPA) 中引入了 ContainerResource
型別指標,不論是Resource還是ContainerResource都只能對cpu和記憶體這兩個維度進行監控,它們的區別如下,
Resource 計算pod的資源使用率是
sum{每個容器的資源使用量} / sum{每個容器的資源請求}
但是一個pod有多個容器,可能會出現單個容器資源使用率高,但是平均下來每個容器資源使用率低的情況,而ContainerResource 則能夠指定以pod中的哪個容器拿來計算擴容指標,能夠提供更準確的擴容機制。
ContainerResource在hpa的yaml配置檔案中配置如下,其中container標籤指明瞭容器名稱。
type: ContainerResource
containerResource:
name: cpu
container: application
target:
type: Utilization
averageUtilization: 60
而Resource型別的擴容指標則是針對pod中所有容器計算指標,
type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
它們發往api server 的請求格式如下
/apis/metrics.k8s.io/v1beta1/namespaces/default/pods
注意,這裡的配置檔案沒有加上selector,實際上我們平時寫配置檔案的時候肯定是有selector的,所以發往api server的請求也會有selector的引數
prometheus adapter 在收到這個請求後,會將對應的pod的cpu和記憶體資訊全部返回,然後k8s的hpa模組篩選其需要用到的部分,像ContainerResource就會篩選返回結果中和container標籤值代表的容器名稱一樣的指標進行計算。
prometheus adapter針對此型別的指標規則配置如下, 其中的containerLabel 表明了指標中容器名稱是用哪個標籤表示的,此時的<<.GroupBy>>模板變數 會由pod資源名稱和容器名標籤兩個維度替代。
以下是prometheus adapter官方給出的配置模版,自己配置的時候需要改掉實際查詢的指標名已經標籤名等。
"resourceRules":
"cpu":
"containerLabel": "container"
"containerQuery": |
sum by (<<.GroupBy>>) (
irate (
container_cpu_usage_seconds_total{<<.LabelMatchers>>,container!="",pod!=""}[4m]
)
)
"nodeQuery": |
sum by (<<.GroupBy>>) (
irate(
node_cpu_usage_seconds_total{<<.LabelMatchers>>}[4m]
)
)
"resources":
"overrides":
"namespace":
"resource": "namespace"
"node":
"resource": "node"
"pod":
"resource": "pod"
"memory":
"containerLabel": "container"
"containerQuery": |
sum by (<<.GroupBy>>) (
container_memory_working_set_bytes{<<.LabelMatchers>>,container!="",pod!=""}
)
"nodeQuery": |
sum by (<<.GroupBy>>) (
node_memory_working_set_bytes{<<.LabelMatchers>>}
)
"resources":
"overrides":
"node":
"resource": "node"
"namespace":
"resource": "namespace"
"pod":
"resource": "pod"
"window": "5m"
External
最後,我們來看下external 型別的擴容指標如何配置,上面講到的hpa 擴縮容指標型別都是在k8s叢集裡產生的指標,它們都限定在了一個namespace裡面,除此以外,hpa模組還允許配置第三方的指標型別,比如叢集外部的訊息佇列產生的指標,這型別的指標被稱作External型別。 在hpa裡配置案例如下,
type: External
external:
metric:
name: queue_messages_cnt
selector:
matchLabels:
app: "lanpangzi"
# External指標型別下只支援Value和AverageValue型別的目標值
target:
type: AverageValue
averageValue: 30
發往api server的請求格式如下
/apis/external.metrics.k8s.io/v1beta1/namespaces/default/queue_messages_cnt
由於外部指標和namespace無關,所以在配置prometheus adapter的規則配置檔案的時候,指定下指標是namespace無關的。
externalRules:
- seriesQuery: 'queue_messages_cnt'
resources:
namespaced: false
name:
matches: 'queue_messages_cnt'
as: 'queue_messages_cnt'
metricsQuery: avg(<<.Series>>{<<.LabelMatchers>>})
總結
探索HPA配置的含義過程中,其實可以發現k8s在針對HPA擴容依據的擴充方式上,就是規定了3組api group(metrics.k8s.io,external.metrics.k8s.io,custom.metrics.k8s.io),並且用基本一致的http請求,讓第三方(prometheus adapter)在宣告為api service 的時候指定為對應的api group,然後解析請求路徑和引數來進而對prometheus查詢 即完成了對HPA擴容指標的查詢。
關於prometheus adapter更多的配置案例建議直接看prometheus adapter的doc目錄下的示例。