一.HPA說明
HPA(Horizontal Pod Autoscaler)是kubernetes的一種資源對象,能夠根據某些指標對在statefulset、replicacontroller、replicaset等集合中的pod數量進行動態伸縮,使運行在上面的服務對指標的變化有一定的自適應能力。
HPA目前支持四種類型的指標,分別是Resource、Object、External、Pods。其中在穩定版本autoscaling/v1只支持對CPU指標的動態伸縮,在測試版本autoscaling/v2beta2中支持memory和自定義指標的動態伸縮,並以annotation的方式工作在autoscaling/v1版本中。
注意:Pod的自動縮放不適用於無法縮放的對象。
HPA的API有三個版本:
[root@k8s001 ~]# kubectl api-versions | grep autoscal
autoscaling/v1
autoscaling/v2beta1
autoscaling/v2beta2
- autoscaling/v1:只支持基於CPU指標的縮放。
- autoscaling/v2beta1:支持Resource Metrics(資源指標,如pod的CPU)和Custom Metrics(自定義指標)的縮放。
- autoscaling/v2beta2:支持Resource Metrics(資源指標,如pod的CPU)和Custom Metrics(自定義指標)和ExternalMetrics(額外指標)的縮放。
參考鏈接:https://v1-17.docs.kubernetes.io/zh/docs/tasks/run-application/horizontal-pod-autoscale/
二.度量指標詳細說明
下面,基於具體的示例進行說明:
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
namespace: default
spec:
# HPA的伸縮對象描述,HPA會動態修改該對象的pod數量
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-apache
# HPA的最小pod數量和最大pod數量
minReplicas: 1
maxReplicas: 10
# 監控的指標數組,支持多種類型的指標共存
metrics:
# Object類型的指標
- type: Object
object:
metric:
# 指標名稱
name: requests-per-second
# 監控指標的對象描述,指標數據來源於該對象
describedObject:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
name: main-route
# Value類型的目標值,Object類型的指標只支持Value和AverageValue類型的目標值
target:
type: Value
value: 10k
# Resource類型的指標
- type: Resource
resource:
name: cpu
# Utilization類型的目標值,Resource類型的指標只支持Utilization和AverageValue類型的目標值
target:
type: Utilization
averageUtilization: 50
# Pods類型的指標
- type: Pods
pods:
metric:
name: packets-per-second
# AverageValue類型的目標值,Pods指標類型下只支持AverageValue類型的目標值
target:
type: AverageValue
averageValue: 1k
# External類型的指標
- type: External
external:
metric:
name: queue_messages_ready
# 該字段與第三方的指標標簽相關聯
selector:
matchLabels:
env: "stage"
app: "myapp"
# External指標類型下只支持Value和AverageValue類型的目標值
target:
type: AverageValue
averageValue: 30
- metrics中target字段:target總共有3種類型:Utilization、Value、AverageValue。
- Utilization:表示平均使用率
- Value:表示實際值
- AverageValue:表示平均值
- metrics中type字段:type字段總共有4種類型:Object、Pods、Resource、External。
- Object:這里指的是指定kubernetes內部對象的指標,數據需要第三方adapter提供,只提供Value和AverageValue類型的目標值。
- Pods:這里指的是伸縮對象(stagefulSet、replicaController、replicaSet)底下的Pods的指標,數據需要第三方的adapter提供,並且只運行AverageValue類型的目標值。
- Resource:這里指的是當前伸縮對象下的pod的cpu和memory指標,只支持Utilization和AverageValue類型的目標值。
- External:這里指的是kubernetes外部的指標,數據需要第三方的adapter提供,只支持Value和AverageValue類型的目標值。
三.HPA動態伸縮原理

HPA在kubernetes中也由一個controller控制,controller會間隔循環HPA,檢查每個HPA中監控的指標是否觸發伸縮條件,默認的間隔時間為15s。一旦觸發伸縮條件,controller會向kubernetes發送請求,修改伸縮對象(statefulSet、replicaController、replicaSet)子對象scale中控制pod數量的字段。kubernetess響應請求,修改scale結構體,然后會刷新一次伸縮對象的pod數量。伸縮對象被修改后,自然會通過list/watch機制增加或減少pod數量,達到動態伸縮的目的。
- 對於每個pod的資源指標(如CPU),控制器從資源指標API中獲取每一個HorizontalPodAutoscaler指定的pod的指標,如果設置了目標使用率,控制器會獲取每個Pod中的容器資源使用情況,並計算資源使用率。如果使用原始值,將直接使用原始數據,進而計算出目標副本數。這里注意的是,如果Pod某些容器不支持資源采集,那么該控制器將不會使用該pod的CPU使用率。
- 如果pod使用自定義指標,控制器機制與資源指標類型,區別在於自定義的指標只適用原始值,而不是利用率。
- 如果pod使用的對象指標和外部指標(每個指標描述一個對象信息),這個指標將直接跟目標指標設定值相比較,並生成一個上述的縮放比例。在最新的autoscaling/v2beta2版本API中,這個指標也可以根據pod數量平分后再進行計算。通常情況,控制器從一系列的聚合API(metrics.k8s.io,custom.metrics.k8s.io和external.metrics.k8s.io)中獲取指標數據。metrics.k8s.io API通常由metrics-server(這里需要額外啟動)提供。
四.HPA伸縮流程
HPA的主要伸縮流程如下:
1)判斷當前Pod數量是否在HPA設定的Pod數量空間中,如果不在,過小返回最小值,過大返回最大值,結束伸縮。
2)判斷指標的類型,並向api server發送對應的請求,拿到設定的監控指標。一般來說指標會從下面系列聚合API中獲取(metrics.k8s.io,custom.metrics.k8s.io和external.metrics.k8s.io)。其中metrics.k8s.io一般由kubernetes自帶的metrics-server來提供,主要是cpu、memory使用率指標。另外兩種需要第三方的adapter來提供。custom.metrics.k8s.io提供的自定義指標數據,一般與kubernetes集群有關,比如跟特定的pod相關。external.metrics.k8s.io同樣提供自定義指標數據,但一般與kubernetes集群無關,許多知名的第三方監控平台提供了adapter實現上述api(如prometheus),可以將監控和adapter一同部署在kubenetes集群中提供服務。甚至能夠替換原來的metrics-server來提供上述三類api指標,達到深度定制監控數據的目標。
3)根據獲取的指標,使用相關的算法計算出一個伸縮系數,並乘以當前pod數量以獲得期望的pod數量。這里系數是指標的期望值與目前值的比值,如果大於1表示擴容,小於1表示縮容。指數數值有平均值(AverageValue)、平均使用率(Utilization)、裸值(Value)三種類型* * 每種類型的數值都有對應的算法。注意下面事項:如果系數有小數點,統一進一;系數如果未達到某個容忍值,HPA認為變化太小,會忽略這次變化,容忍值默認為0.1。
- 這里HPA擴容算法比較保守,如果出現獲取不到指標的情況,擴容時算最小值,縮容時算最大值。如果需要計算平均值,出現pod沒准備好的情況,我們保守地假設尚未就緒的pods消耗了試題指標的0%,從而進一步降低了伸縮的幅度。
- 一個HPA支持多個指標的監控,HPA會循環獲取所有的指標,並計算期望的pod數量,並從期望結果中獲得最大的pod數量作為最終的伸縮的pod數量。一個伸縮對象在k8s中允許對應多個HPA,但是只是k8s不會報錯而已,事實上HPA彼此不知道自己監控的是同一個伸縮對象,在這個伸縮對象中的pod會被多個HPA無意義地來回修改pod數量,給系統增加消耗,如果想要指定多個監控指標,可以如上述所說,在一個HPA中添加多個監控指標。
4)檢查最終pod數量是否在HPA設定的pod數量范圍的區間,如果超過最大值或不足最小值都會修改為最大值或者最小值。然后會向kubernetes發出請求,修改伸縮對象的子對象scale的pod數量,結束一個HPA的檢查,獲取下一個HPA,完成一個伸縮流程。
五.HPA適用場景
HPA適用流量波動較大,機器資源緊張,服務數量多的業務場景。
六.部署測試
這里為了演示HPA,使用一個基於php-apache鏡像來定制Docker鏡像,Dockerfile內容如下:
cat Dockerfile.php
FROM php:5-apache
ADD index.php /var/www/html/index.php
RUN chmod a+rx index.php
其中,index.php頁面執行一些CPU密集型計算
cat index.php
<?php
$x = 0.0001;
for ($i = 0; $i <= 1000000; $i++) {
$x += sqrt($x);
}
echo "OK!";
?>
6.1 構建Docker
# 構建docker鏡像
docker build -t hpa-php:test -f Dockerfile.php .
# 保存docker鏡像成tar
docker save hpa-php:test -o php.tar
# 導入docker鏡像到各個節點
docker load -i php.tar
6.2 創建php的yaml文件
[root@k8s001 php]# cat php.yaml
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: php
spec:
selector:
matchLabels:
run: php
replicas: 1
template:
metadata:
labels:
run: php
spec:
containers:
- name: php
image: hpa-php:test
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
resources:
limits:
cpu: 400m
requests:
cpu: 200m
---
apiVersion: v1
kind: Service
metadata:
name: php
labels:
run: php
spec:
ports:
- port: 80
selector:
run: php
# 部署php服務
[root@k8s001 php]# kubectl apply -f php.yaml
# 查看php服務是否部署成功
[root@k8s001 php]# kubectl get pod
NAME READY STATUS RESTARTS AGE
php-58b79cd964-gxcnc 1/1 Running 0 56s
6.3 創建HPA
由上面可見,php服務正在運行。我們使用kubectl autoscale創建自動縮放器。實現對php的deployment創建的pod的自動擴縮容。下面的命令會創建一個HPA,HPA將會根據CPU資源指標進行增加或者減少副本數:
# 給deployment創建HPA
[root@k8s001 php]# kubectl autoscale deployment php --cpu-percent=50 --min=1 --max=10
說明:這里我們讓副本數維持到1-10個,所有的Pod的平均CPU使用率維持在50%(這里我們運行時pod請求的cpu為200m核,說明平均CPU使用為100m核)
# 查看HPA創建的詳情
[root@k8s001 php]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php Deployment/php 0%/50% 1 10 1 29s
說明:這里由於我們沒有給服務器增加負載,CPU當前消耗為0(TARGET列顯示了由相應deployment控制的所有Pod的平均值)。
6.4 對php進行壓測
說明:這里是針對CPU進行壓測。
# 這里啟動一個容器,並將無限查詢循環發送php服務
kubectl run v1 -it --image=busybox /bin/sh
# 登錄到容器,執行以下操作
/ # while true; do wget -q -O- http://php.default.svc.cluster.local; done
等待一分鍾左右,我們查看HPA的負載情況:
[root@k8s001 php]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php Deployment/php 264%/50% 1 10 1 16m
上面可以看到,CPU消耗已經達到264%,每個pod的目標cpu使用率是50%,因此php這個deployment創建的pod副本數將調整為6個副本,為什么是6個副本,因為264/50=5.28
[root@k8s001 php]# kubectl get pod
NAME READY STATUS RESTARTS AGE
php-58b79cd964-7p9nj 1/1 Running 0 41s
php-58b79cd964-gxcnc 1/1 Running 0 24m
php-58b79cd964-hcnnv 1/1 Running 0 56s
php-58b79cd964-krmgd 1/1 Running 0 56s
php-58b79cd964-pngqw 1/1 Running 0 56s
php-58b79cd964-s86h5 1/1 Running 0 41s
[root@k8s001 php]# kubectl get deployment php
NAME READY UP-TO-DATE AVAILABLE AGE
php 6/6 6 6 25m
說明: 這里需要幾分鍾來穩定副本數。因為負載情況不同,因此最終副本數可能會有差異。
6.5 停止對php服務壓測,查看HPA是否會對php縮容
這里我們停止向php這個服務發送查詢請求,在busybox鏡像創建容器的終端中,通過Ctrl+C中斷while進程,幾分鍾后查看結果:
# 此時CPU利用率已經降到0,所以HPA將自動縮減副本數量至1。
[root@k8s001 php]# kubectl get hpa -w
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php Deployment/php 0%/50% 1 10 6 25m
php Deployment/php 0%/50% 1 10 6 25m
php Deployment/php 0%/50% 1 10 3 26m
php Deployment/php 0%/50% 1 10 3 26m
php Deployment/php 0%/50% 1 10 1 27m
# 查看deployment數
[root@k8s001 php]# kubectl get deployment php
NAME READY UP-TO-DATE AVAILABLE AGE
php 1/1 1 1 36m
注意:這里自動縮容需要幾分鍾。
同理,基於內存的縮容也可以參考此示例進行。這里我們就不進行演示說明了。
