Rancher 生態中K3S集群的三種HPA配置試用
本文主要講述了 K3S 集群下的 HPA 的 3 種詳細類型配置,閱讀本文你可以了解內容如下:

HPA 概念介紹
HPA 是英文 Horizontal Pod Autoscaler 的縮寫,就是我們說的「水平 POD 自動擴縮容」,也就是橫向擴縮容。它可以根據監控指標,自動擴縮 ReplicationController、Deployment、ReplicaSet 和 StatefulSet 中的 Pod 數量。HPA 可以很好的幫助服務來應對突發的流量,和普通服務中的針對 vm 的擴縮容有異曲同工之處。
當然除了 HPA,還有集群、縱向等類型的自動擴縮容,本文不做討論,有興趣的可參考官方倉庫和文檔[1]。
K3S 和 K8S 的 HPA 概念和使用基本一致,以下統稱容器平臺為 K8S。
HPA 工作機制
K8S 中 HPA 的工作機制大致如下:

HPA 控制器會周期性的查詢 HPA 定義的監控指標,當達到觸發條件時,觸發擴縮容動作。這個周期時間默認是 15s,可通過 Controller 的參數 --horizontal-pod-autoscaler-sync-period 來自定義。
HPA 指標類型
HPA 控制器查詢監控指標是通過 APIServer 查詢的,這些監控指標是通過 APIServer 的 聚合 (Aggregator[2] )插件機制集成到 APIServer 的,結構大致如下:

目前 HPA 支持的聚合 API 主要有以下三種:
- metrics.k8s.io 資源類 API,由 K8S 自帶的 Metric server[3]提供,其中主要為 nodes 和 pods 的相關監控數據;
- custom.metrics.k8s.io 自定義類 API,由第三方監控方案提供的適配器(Adapter)提供,目前常用為 Prometheus-Adapter[4]。
- external.metrics.k8s.io 外部的 API,和 k8s 集群沒有關系的外部指標,由第三方監控方案適配器提供;
更多聚合 API 信息可參考官方文檔這里[5]。
指標類型可分為如下幾類:
- POD 資源使用率:POD 級別的性能指標,通常為一個比率值。
- POD 自定義指標:POD 級別的性能指標,通常為一個數值。
- 容器 資源使用率:容器級別的性能指標,K8S 1.20 引入,主要針對一個 POD 中多個容器的情況,可指定核心業務容器作為資源擴縮閾值。
- Object 自定義和外部指標:通常為一個數值,需要容器以某種方式提供,或外部服務提供的指標采集 URL。
HPA ApiVersion
K8S 中各種資源在定義時,使用 apiVsersion 來區分不同版本。HPA 目前主要有以下幾個常用版本:
- autoscaling/v1 只支持針對 CPU 指標的擴縮容;
- autoscaling/V2beta1 除了 cpu,新引入內存利用率指標來擴縮容;
- autoscaling/V2beta2 除了 cpu 和內存利用率,新引入了自定義和外部指標來擴縮容,支持同時指定多個指標;
v2 版本相較 v1 做了不少擴展,除了增加自義定和外部指標外,還支持多指標,當定義多個指標時,HPA 會根據每個指標進行計算,其中縮放幅度最大的指標會被采納。
在定義 HPA 時,需要合理選擇版本,以免造成不必要的錯誤。更多 APIVersion 信息,可參考官方文檔這里[6]。
HPA 實戰
接下來我們配置測試下上邊說的幾種類型的指標 HPA,自定義指標和外部指標需要第三方的適配器(Adapter)來提供,針對 Rancher 生態可使用其集成的 Monitoring (100.1.0+up19.0.3) charts 來解決,其中組件 Prometheus Adapter 便是適配器,提供了自定義和外部類型的 API。詳細安裝方法可參考我之前的文章k3s 中 helm 自定義安裝 traefik v2[7],Monitoring 安裝方法一樣。
安裝好適配器之后,我們準備一個 nginx 服務用來進行自動擴縮容使用,我們使用 cnych/nginx-vts:v1.0 鏡像,它集成了 nginx vts 模塊,可以查看 nginx 的各種請求數據。創建 deployments 和 service 如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-hpa-test
namespace: hd-ops
spec:
selector:
matchLabels:
app: nginx-server
template:
metadata:
labels:
app: nginx-server
spec:
containers:
- name: nginx-demo
image: cnych/nginx-vts:v1.0
resources:
limits:
cpu: 50m
requests:
cpu: 50m
ports:
- containerPort: 80
name: http
---
apiVersion: v1
kind: Service
metadata:
name: nginx-hpa-test
namespace: hd-ops
labels:
app: nginx-svc
spec:
ports:
- port: 80
targetPort: 80
name: http
selector:
app: nginx-server
# type: ClusterIP
type: NodePortv1 版本 Pod 資源類
首先,我們來測試 v1 版本的 HPA,僅支持 POD 資源 CPU 的指標判斷。我們可直接使用 kubectl 命令行來創建,命令如下:
# 創建
$ kubectl autoscale deployment nginx-hpa-test --cpu-percent=20 --min=1 --max=10
# 查看
$ kubectl get hpa -A
NAMESPACE NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
hd-ops nginx-hpa-test Deployment/nginx-hpa-test 2%/20% 1 10 1 1m其中關注下 TARGETS 列,前邊為 CPU 當前使用,后邊為觸發閾值。我們給 nginx 服務增加壓力,觀察 hpa 情況,可使用如下命令:
# 加壓 替換 node-ip 為 pod 部署的具體 node ip,node-port 為隨機映射暴露的節點端口
for i in {0..30000};do echo "start-------->:${i}";curl http://<node-ip>:<node-port>/status/format/prometheus;echo "=====================================:${i}";sleep 0.01;done
# 查看hpa情況
$ kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-hpa-test Deployment/nginx-hpa-test 26%/20% 1 10 2 3h48m我們可以看到 pod 的副本數由 1 個變為了 2 個,成功觸發 hpa,擴容 1 個 pod。
當我們停止壓力之后再觀察 HPA,并沒有馬上縮容,這是因為 k8s 的冷卻延遲機制??赏ㄟ^控制器參數--horizontal-pod-autoscaler-downscale-stabilization來自定義,默認為 5m。
# 五分鐘后查看,pod 成功縮容。
~ ? 19:16:08
$ kubectl get hpa -n hd-ops -o wide
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-hpa-test Deployment/nginx-hpa-test 0%/20% 1 10 2 3h54m
~ ? 19:16:11
$ kubectl get hpa -n hd-ops -o wide
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-hpa-test Deployment/nginx-hpa-test 0%/20% 1 10 1 4h1m至此,v1 版本的 HPA 行為符合預期,當 CPU 的使用率超過閾值后,會擴容目標 pod,當使用率低于閾值后,會在冷卻期結束后縮容。
除了使用 kubectl 命令行來定義 hpa,也可以通過 yaml 來定義,如上 hpa,與下邊的 yaml 文件等效。
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: nginx-hpa-demo
spec:
scaleTargetRef: # 目標作用對象
apiVersion: apps/v1
kind: Deployment
name: nginx-hpa-test
minReplicas: 1 # 最小副本數
maxReplicas: 10 # 最大副本數
metrics: # 目標指標值
- type: Resource # 資源類型指標
resource:
name: cpu
targetAverageUtilization: 20v2 版本自定義指標
接下來,我們看下 v2 版本的自定義指標,需要你 K8S 在 1.6 以上。我們可按如下步驟來配置:
1.確保自定義指標數據已推送到 Prometheus
理論上來說,只要在 Prometheus 中的數據,我們都可以作為觸發指標來配置 HPA。Rancher 平臺中,使用 Prometheus Operator 的 CRD 來安裝管理的監控組件,我們可通過自定義 ServiceMonitor 來給 Prometheus 添加 target 采集對象。針對上邊我們 Nginx 的數據,我們可以編寫如下的 ServiceMonitor:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: nginx-hpa-svc-monitor
namespace: cattle-monitoring-system
labels:
app: nginx-hpa-svc-monitor
spec:
jobLabel: app
selector:
matchLabels:
app: nginx-svc
namespaceSelector:
matchNames:
- hd-ops
endpoints:
- port: http
interval: 30s
path: /status/format/prometheus應用之后,我們可以在 prometheus 的 target 頁面看到如下的 target 列表項:
serviceMonitor/cattle-monitoring-system/nginx-hpa-svc-monitor/0 (1/1 up)2.選擇指標,注冊到自定義指標 API
Nginx vts 接口 /status/format/prometheus中,提供了很多 nginx 的請求數據指標,我們拿 nginx_vts_server_requests_total 指標來計算一個實時的請求數作為 HPA 的觸發指標。我們需要將該指標注冊到 HPA 請求的 custom.metrics.k8s.io 中。Rancher 平臺中,我們可以覆蓋 Monitoring (100.1.0+up19.0.3) charts 的 values 來注冊指標 ,配置如下:
prometheus-adapter:
enabled: true
prometheus:
# Change this if you change the namespaceOverride or nameOverride of prometheus-operator
url: http://rancher-monitoring-prometheus.cattle-monitoring-system.svc
port: 9090
# hpa metrics
rules:
default: false # 需要設置為 false,否則與下邊 custom 的指標沖突,會造成整個 custom 指標為空,不知道是不是bug;
custom:
# 具體詳細的參數配置可參考官方文檔:https://docs.rancher.cn/docs/rancher2/cluster-admin/tools/cluster-monitoring/cluster-metrics/custom-metrics/_index
- seriesQuery: "nginx_vts_server_requests_total" # '{__name__=~"^nginx_vts_.*_total",namespace!="",pod!=""}'
seriesFilters: []
resources:
overrides:
namespace: # 這里的namespace和pod是prometheus里面指標的標簽,會作為分組
resource: namespace
service:
resource: services
pod:
resource: pod
name:
matches: ^(.*)_total
as: "${1}_per_second"
metricsQuery: sum(rate(<<.Series>>{<<.LabelMatchers>>}[1m])) by (<<.GroupBy>>)運行如下命令,更新組件。
helm upgrade --install rancher-monitoring ./ -f k3s-values.yaml -n cattle-monitoring-system可我們通過 API 查看是否注冊成功:
# 查看 api 列表
$ kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/" |python3 -m json.tool
{
"kind": "APIResourceList",
"apiVersion": "v1",
"groupVersion": "custom.metrics.k8s.io/v1beta1",
"resources": [
{
"name": "namespaces/nginx_vts_server_requests_per_second",
"singularName": "",
"namespaced": false,
"kind": "MetricValueList",
"verbs": [
"get"
]
},
{
"name": "services/nginx_vts_server_requests_per_second",
"singularName": "",
"namespaced": true,
"kind": "MetricValueList",
"verbs": [
"get"
]
},
{
"name": "pods/nginx_vts_server_requests_per_second",
"singularName": "",
"namespaced": true,
"kind": "MetricValueList",
"verbs": [
"get"
]
}
]
}
# 查看具體某個指標的數值
$ kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/hd-ops/pods/*/nginx_vts_server_requests_per_second" |python3 -m json.tool
{
"kind": "MetricValueList",
"apiVersion": "custom.metrics.k8s.io/v1beta1",
"metadata": {
"selfLink": "/apis/custom.metrics.k8s.io/v1beta1/namespaces/hd-ops/pods/%2A/nginx_vts_server_requests_per_second"
},
"items": [
{
"describedObject": {
"kind": "Pod",
"namespace": "hd-ops",
"name": "nginx-hpa-test-7695f97455-mmpxz",
"apiVersion": "/v1"
},
"metricName": "nginx_vts_server_requests_per_second",
"timestamp": "2022-04-01T07:21:28Z",
"value": "133m", # 此處單位m 為千分之的意思
"selector": null
}
]
}看到如上返回,說明注冊成功了,之后我們就可以使用其中的指標名稱來配置 HPA 了。
3.配置 HPA
編寫 HPA yaml 如下:
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: nginx-hpa-custom
namespace: hd-ops
spec:
scaleTargetRef: # 應用目標服務 Deployment
apiVersion: apps/v1
kind: Deployment
name: nginx-hpa-test
minReplicas: 1 # 保留最小副本數
maxReplicas: 10 # 擴容最大副本數
metrics:
- type: Pods # 指標類型
pods:
metric:
name: nginx_vts_server_requests_per_second # 指標名稱
target:
type: AverageValue
averageValue: 5應用之后,我們可以看到數據已經采集到:
$ kubectl get hpa -A
NAMESPACE NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
hd-ops nginx-hpa-custom Deployment/nginx-hpa-test 133m/5 1 10 1 1m4.測試 HPA
運行如下命令加壓測試:
# 加壓
for i in {0..30000};do echo "start-------->:${i}";curl http://<node-ip>:<node-port>/status/format/prometheus;echo "=====================================:${i}";sleep 0.01;done
# 查看hpa情況
$ kubectl get hpa -A
NAMESPACE NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
hd-ops nginx-hpa-custom Deployment/nginx-hpa-test 11333m/5 1 10 3 72m請求數超過了閾值 5,可以看到 REPLICAS 副本數變成了 3。停止壓測后,5 分鐘后查看 hpa,又縮容回去,符合預期。
v2 版本的 HPA 還可以使用其他類型,且支持多個指標,如下示例:
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: nginx-hpa-custom
namespace: hd-ops
spec:
scaleTargetRef: # 應用目標服務 Deployment
apiVersion: apps/v1
kind: Deployment
name: nginx-hpa-test
minReplicas: 1 # 保留最小副本數
maxReplicas: 10 # 擴容最大副本數
metrics:
- type: Pods # 指標類型
pods:
metric:
name: nginx_vts_server_requests_per_second # 指標名稱
target:
type: AverageValue
averageValue: 5
- type: Resource # 資源類型和v1版本寫法略有區別,需要注意
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
- type: Object # 除pod外的其他資源對象類型
object:
metric:
name: requests-per-second
describedObject:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
name: main-route
target:
kind: Value
value: 10k # 數值,直接與獲取到的指標值比較指標類型 type 有下邊 4 種取值:
- Resource: 指當前伸縮對象 pod 的 cpu 和 內存 支持 utiliztion 和 averagevalue 類型的目標值。一般情況下,計算 CPU 使用率使用 averageUtilization 定義平均使用率。計算內存資源,使用 AverageValue 定義平均使用值。
- Pods: 指的是伸縮對象 Pod 的指標,數據有第三方的 Adapter 提供,只支持 AverageValue 類型的目標值。
- Object: K8S 內部除 pod 之外的其他對象的指標,數據有第三方的 Adapter 提供,支持 Value 和 AverageValue 類型的目標值。
- External: K8S 外部的指標,數據有第三方 Adapter 提供,支持 Value 和 AverageValue 類型的目標值。
更多 type 類型信息,可參考官方文檔這里[8]。
v2 版本外部指標
最后我們來測試下外部指標,要求 K8S 版本在 1.10 以上,配置步驟和自定義表一致,在注冊指標到 /apis/external.metrics.k8s.io/v1beta1/ 時配置略有區別,
我們這里隨便拿一個與 K8S 無關的外部主機的 cpu 使用率做下測試,注冊配置如下:
prometheus-adapter:
enabled: true
prometheus:
# Change this if you change the namespaceOverride or nameOverride of prometheus-operator
#url: http://rancher-monitoring-prometheus.cattle-monitoring-system:9090/
url: http://rancher-monitoring-prometheus.cattle-monitoring-system.svc
port: 9090
# hpa metrics
rules:
default: false
# 如下為外部配置
external:
- seriesQuery: 'cpu_usage_idle{cpu="cpu-total"}' # '{__name__=~"^nginx_vts_.*_total",namespace!="",pod!=""}'
seriesFilters: []
resources:
template: <<.Resource>>
name:
matches: "^(.*)"
as: "cpu_usage"
metricsQuery: (100-cpu_usage_idle{cpu="cpu-total",ip="192.168.10.10"})/100 #<<.Series>>{<<.LabelMatchers>>}這里 resources 我們直接使用 template 來模糊定義讓系統自動處理,因為外部指標和 K8S API 資源并不一定有關系,我們 metricsQery 這塊也使用明確寫出的方式來處理。
應用之后,我們同樣可以使用 API 來查看:
# 查看指標列表
$ kubectl get --raw "/apis/external.metrics.k8s.io/v1beta1/"|python3 -m json.tool
{
"kind": "APIResourceList",
"apiVersion": "v1",
"groupVersion": "external.metrics.k8s.io/v1beta1",
"resources": [
{
"name": "cpu_usage",
"singularName": "",
"namespaced": true,
"kind": "ExternalMetricValueList",
"verbs": [
"get"
]
}
]
}
# 查看具體的指標數據
$ kubectl get --raw "/apis/external.metrics.k8s.io/v1beta1/namespaces/hd-ops/cpu_usage"|python3 -m json.tool
{
"kind": "ExternalMetricValueList",
"apiVersion": "external.metrics.k8s.io/v1beta1",
"metadata": {},
"items": [
{
"metricName": "cpu_usage",
"metricLabels": {
"__name__": "cpu_usage",
"bg": "ops",
"cpu": "cpu-total",
"idc": "ali",
"ident": "xxxx-host",
"ip": "192.168.10.10",
"region": "BJ",
"svc": "demo-test"
},
"timestamp": "2022-04-02T03:28:50Z",
"value": "11m"
},
......
]
}有時候 API 會返回為空,大概率是我們注冊的 API rule 配置有問題,可修改配置明確查詢 PromQL 測試。
定義 HPA 和 custom 類型也一樣,如下:
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: nginx-hpa-custom
namespace: hd-ops
spec:
scaleTargetRef: # 應用目標服務 Deployment
apiVersion: apps/v1
kind: Deployment
name: nginx-hpa-test
minReplicas: 1 # 保留最小副本數
maxReplicas: 10 # 擴容最大副本數
metrics:
- type: External
external:
metric:
name: cpu_usage
selector:
matchLabels:
ip: "192.168.10.10"
target:
type: AverageValue
averageValue: 10m # 該數值為使用率 1% ,測試無實際意義應用后查看 hpa 情況:
$ kubectl get hpa -A
NAMESPACE NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
hd-ops nginx-hpa-custom-external Deployment/nginx-hpa-test 18m/10m (avg) 1 10 2 11m可以看到目標值 18m 超過了 閾值 10m,目標服務副本有 1 個變為 2 個,符合預期。
總結及問題回顧
至此,HPA 的三種大類型我們都測試完成。Rancher K3S 生態中,總體上來說和 K8S 并沒有什么不同。在 Rancher 平臺中有下邊幾個問題需要需要注意下:
- 1、Rancher Charts 商店中 Monitor 集成的 Prometheus Adapter 中,當定義 custom 和 external rule 規則時,需要關閉 default 默認的規則,否則會認證失敗,造成整個 API 請求為空。錯誤信息如下:
client.go:233: [debug] error updating the resource "rancher-monitoring-alertmanager.rules":
cannot patch "rancher-monitoring-alertmanager.rules" with kind PrometheusRule: Internal error occurred: failed calling webhook "prometheusrulemutate.monitoring.coreos.com": Post "https://rancher-monitoring-operator.cattle-monitoring-system.svc:443/admission-prometheusrules/validate?timeout=10s": context deadline exceeded2、Rule 配置問題,注意版本和配置的關系,有些配置格式不同版本不一致。
我是DeanWu,一個努力成為真正SRE的人。如果本文對您有幫助,還請三連支持(點贊、轉發、在看),您的支持將是我更新的最大動力。
關注公眾號「碼農吳先生」,回復關鍵字「小二」,加我wx拉你進技術交流群,聊技術聊人生。
擴展閱讀
- https://www.jetstack.io/blog/resource-and-custom-metrics-hpa-v2/
- https://kubernetes.io/zh/docs/tasks/run-application/horizontal-pod-autoscale/#support-for-metrics-apis
- https://kubernetes.io/zh/docs/tasks/run-application/horizontal-pod-autoscale/
參考資料
[1] 官方倉庫和文檔: https://github.com/kubernetes/autoscaler
[2] Aggregator: https://kubernetes.io/zh/docs/tasks/extend-kubernetes/configure-aggregation-layer/
[3] Metric server: https://github.com/kubernetes-sigs/metrics-server
[4] Prometheus-Adapter: https://github.com/kubernetes-sigs/prometheus-adapter/blob/master/docs/walkthrough.md
[5] 這里: https://kubernetes.io/zh/docs/tasks/run-application/horizontal-pod-autoscale/#support-for-metrics-apis
[6] 這里: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#horizontalpodautoscaler-v1-autoscaling
[7] k3s 中 helm 自定義安裝 traefik v2: https://pylixm.top/posts/2022-02-21-k3s-traefik-v2.html
[8] 這里: https://kubernetes.io/zh/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/#autoscaling-on-multiple-metrics-and-custom-metrics





























