1、Pod 水平自动扩缩
Pod 水平自动扩缩(Horizontal Pod Autoscaler) 可以基于 CPU 利用率自动扩缩 ReplicationController、Deployment、ReplicaSet 和 StatefulSet 中的 Pod 数量。 除了 CPU 利用率,也可以基于其他应程序提供的 自定义度量指标 来执行自动扩缩。 Pod 自动扩缩不适用于无法扩缩的对象,比如 DaemonSet。
Pod 水平自动扩缩特性由 Kubernetes API 资源和控制器实现。资源决定了控制器的行为。 控制器会周期性地调整副本控制器或 Deployment 中的副本数量,以使得类似 Pod 平均 CPU 利用率、平均内存利用率这类观测到的度量值与用户所设定的目标值匹配。
2、Horizontal Pod Autoscaler 如何工作
Pod 水平自动扩缩器的实现是一个控制回路,由控制器管理器的 --horizontal-pod-autoscaler-sync-period
参数指定周期(默认值为 15 秒)。
每个周期内,控制器管理器根据每个 HorizontalPodAutoscaler 定义中指定的指标查询资源利用率。 控制器管理器可以从资源度量指标 API(按 Pod 统计的资源用量)和自定义度量指标 API(其他指标)获取度量值。
-
对于按 Pod 统计的资源指标(如 CPU),控制器从资源指标 API 中获取每一个 HorizontalPodAutoscaler 指定的 Pod 的度量值,如果设置了目标使用率, 控制器获取每个 Pod 中的容器资源使用情况,并计算资源使用率。 如果设置了 target 值,将直接使用原始数据(不再计算百分比)。 接下来,控制器根据平均的资源使用率或原始值计算出扩缩的比例,进而计算出目标副本数。
需要注意的是,如果 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 服务器(需要额外启动)提供。 可以从 metrics-server 获取更多信息。 另外,控制器也可以直接从 Heapster 获取指标。
说明: FEATURE STATE: Kubernetes 1.11 [deprecated] 自 Kubernetes 1.11 起,从 Heapster 获取指标特性已废弃。
自动扩缩控制器使用 scale 子资源访问相应可支持扩缩的控制器(如副本控制器、 Deployment 和 ReplicaSet)。 scale
是一个可以动态设定副本数量和检查当前状态的接口。
3、HPA的基本算法
pod 水平自动扩缩控制器根据当前指标和期望指标来计算扩缩比例。
期望副本数 = ceil[当前副本数 * (当前指标 / 期望指标)]
例如,当前度量值为 200m
,目标设定值为 100m
,那么由于 200.0/100.0 == 2.0
, 副本数量将会翻倍。 如果当前指标为 50m
,副本数量将会减半,因为50.0/100.0 == 0.5
。 如果计算出的扩缩比例接近 1.0 (根据--horizontal-pod-autoscaler-tolerance
参数全局配置的容忍值,默认为 0.1), 将会放弃本次扩缩。
如果 HorizontalPodAutoscaler 指定的是 targetAverageValue
或 targetAverageUtilization
, 那么将会把指定 Pod 度量值的平均值做为 currentMetricValue
。 然而,在检查容忍度和决定最终扩缩值前,我们仍然会把那些无法获取指标的 Pod 统计进去。
所有被标记了删除时间戳(Pod 正在关闭过程中)的 Pod 和失败的 Pod 都会被忽略。
如果某个 Pod 缺失度量值,它将会被搁置,只在最终确定扩缩数量时再考虑。
当使用 CPU 指标来扩缩时,任何还未就绪(例如还在初始化)状态的 Pod 或 最近的指标 度量值采集于就绪状态前的 Pod,该 Pod 也会被搁置。
由于受技术限制,Pod 水平扩缩控制器无法准确的知道 Pod 什么时候就绪, 也就无法决定是否暂时搁置该 Pod。 --horizontal-pod-autoscaler-initial-readiness-delay
参数(默认为 30s)用于设置 Pod 准备时间, 在此时间内的 Pod 统统被认为未就绪。 --horizontal-pod-autoscaler-cpu-initialization-period
参数(默认为5分钟) 用于设置 Pod 的初始化时间, 在此时间内的 Pod,CPU 资源度量值将不会被采纳。
在排除掉被搁置的 Pod 后,扩缩比例就会根据 currentMetricValue/desiredMetricValue
计算出来。
如果缺失任何的度量值,我们会更保守地重新计算平均值, 在需要缩小时假设这些 Pod 消耗了目标值的 100%, 在需要放大时假设这些 Pod 消耗了 0% 目标值。 这可以在一定程度上抑制扩缩的幅度。
此外,如果存在任何尚未就绪的 Pod,我们可以在不考虑遗漏指标或尚未就绪的 Pod 的情况下进行扩缩, 我们保守地假设尚未就绪的 Pod 消耗了期望指标的 0%,从而进一步降低了扩缩的幅度。
在扩缩方向(缩小或放大)确定后,我们会把未就绪的 Pod 和缺少指标的 Pod 考虑进来再次计算使用率。 如果新的比率与扩缩方向相反,或者在容忍范围内,则跳过扩缩。 否则,我们使用新的扩缩比例。
注意,平均利用率的原始值会通过 HorizontalPodAutoscaler 的状态体现( 即使使用了新的使用率,也不考虑未就绪 Pod 和 缺少指标的 Pod)。
如果创建 HorizontalPodAutoscaler 时指定了多个指标, 那么会按照每个指标分别计算扩缩副本数,取最大值进行扩缩。 如果任何一个指标无法顺利地计算出扩缩副本数(比如,通过 API 获取指标时出错), 并且可获取的指标建议缩容,那么本次扩缩会被跳过。 这表示,如果一个或多个指标给出的 desiredReplicas
值大于当前值,HPA 仍然能实现扩容。
最后,在 HPA 控制器执行扩缩操作之前,会记录扩缩建议信息。 控制器会在操作时间窗口中考虑所有的建议信息,并从中选择得分最高的建议。 这个值可通过 kube-controller-manager
服务的启动参数 --horizontal-pod-autoscaler-downscale-stabilization
进行配置, 默认值为 5 分钟。 这个配置可以让系统更为平滑地进行缩容操作,从而消除短时间内指标值快速波动产生的影响。
4、API 对象
HorizontalPodAutoscaler 是 Kubernetes autoscaling
API 组的资源。 在当前稳定版本(autoscaling/v1
)中只支持基于 CPU 指标的扩缩。
API 的 beta 版本(autoscaling/v2beta2
)引入了基于内存和自定义指标的扩缩。 在 autoscaling/v2beta2
版本中新引入的字段在 autoscaling/v1
版本中以注解 的形式得以保留。
5、HPA的部署测试
5.1部署metrics-server
官方代码仓库地址:https://github.com/kubernetes-sigs/metrics-server
wget https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.4.4/components.yaml
mv components.yaml metrics-server.yaml
metrics-server.yaml
apiVersion: v1 kind: ServiceAccount metadata: labels: k8s-app: metrics-server name: metrics-server namespace: kube-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: labels: k8s-app: metrics-server rbac.authorization.k8s.io/aggregate-to-admin: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-view: "true" name: system:aggregated-metrics-reader rules: - apiGroups: - metrics.k8s.io resources: - pods - nodes verbs: - get - list - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: labels: k8s-app: metrics-server name: system:metrics-server rules: - apiGroups: - "" resources: - pods - nodes - nodes/stats - namespaces - configmaps verbs: - get - list - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: labels: k8s-app: metrics-server name: metrics-server-auth-reader namespace: kube-system roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: extension-apiserver-authentication-reader subjects: - kind: ServiceAccount name: metrics-server namespace: kube-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: labels: k8s-app: metrics-server name: metrics-server:system:auth-delegator roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:auth-delegator subjects: - kind: ServiceAccount name: metrics-server namespace: kube-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: labels: k8s-app: metrics-server name: system:metrics-server roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:metrics-server subjects: - kind: ServiceAccount name: metrics-server namespace: kube-system --- apiVersion: v1 kind: Service metadata: labels: k8s-app: metrics-server name: metrics-server namespace: kube-system spec: ports: - name: https port: 443 protocol: TCP targetPort: https selector: k8s-app: metrics-server --- apiVersion: apps/v1 kind: Deployment metadata: labels: k8s-app: metrics-server name: metrics-server namespace: kube-system spec: selector: matchLabels: k8s-app: metrics-server strategy: rollingUpdate: maxUnavailable: 0 template: metadata: labels: k8s-app: metrics-server spec: containers: - args: - --cert-dir=/tmp - --secure-port=4443 - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname - --kubelet-use-node-status-port #image: k8s.gcr.io/metrics-server/metrics-server:v0.4.4 image: harbor.ywx.net/k8s-baseimages/metrics-server:v0.4.4 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 3 httpGet: path: /livez port: https scheme: HTTPS periodSeconds: 10 name: metrics-server ports: - containerPort: 4443 name: https protocol: TCP readinessProbe: failureThreshold: 3 httpGet: path: /readyz port: https scheme: HTTPS periodSeconds: 10 securityContext: readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 1000 volumeMounts: - mountPath: /tmp name: tmp-dir nodeSelector: kubernetes.io/os: linux priorityClassName: system-cluster-critical serviceAccountName: metrics-server volumes: - emptyDir: {} name: tmp-dir --- apiVersion: apiregistration.k8s.io/v1 kind: APIService metadata: labels: k8s-app: metrics-server name: v1beta1.metrics.k8s.io spec: group: metrics.k8s.io groupPriorityMinimum: 100 insecureSkipTLSVerify: true service: name: metrics-server namespace: kube-system version: v1beta1 versionPriority: 100
运行metrics-server.yaml清单
#运行metrics-server前 root@k8s-master01:/apps/k8s-yaml/hpa# kubectl top nodes W1028 22:12:32.344418 107255 top_node.go:119] Using json format to get metrics. Next release will switch to protocol-buffers, switch early by passing --use-protocol-buffers flag error: Metrics API not available #运行metrics-server后 root@k8s-master01:/apps/k8s-yaml/hpa# kubectl apply -f metrics-server.yaml serviceaccount/metrics-server created clusterrole.rbac.authorization.k8s.io/system:aggregated-metrics-reader created clusterrole.rbac.authorization.k8s.io/system:metrics-server created rolebinding.rbac.authorization.k8s.io/metrics-server-auth-reader created clusterrolebinding.rbac.authorization.k8s.io/metrics-server:system:auth-delegator created clusterrolebinding.rbac.authorization.k8s.io/system:metrics-server created service/metrics-server created deployment.apps/metrics-server created apiservice.apiregistration.k8s.io/v1beta1.metrics.k8s.io created #使用kubectl top nodes命令可以显示所有节点的CPU和MEM使用状态 root@k8s-master01:/apps/k8s-yaml/hpa# kubectl top nodes W1028 22:12:55.126877 107834 top_node.go:119] Using json format to get metrics. Next release will switch to protocol-buffers, switch early by passing --use-protocol-buffers flag NAME CPU(cores) CPU% MEMORY(bytes) MEMORY% 172.168.33.207 310m 31% 1047Mi 81% 172.168.33.208 92m 9% 994Mi 77% 172.168.33.209 97m 9% 944Mi 73% 172.168.33.210 121m 3% 684Mi 21% 172.168.33.211 341m 8% 730Mi 22% 172.168.33.212 135m 3% 692Mi 21%
5.2部署hpa
注意:Deployment对象必须配置requests的参数,不然无法获取监控数据,也无法通过HPA进行动态伸缩
nginx-deploy.yaml
apiVersion: apps/v1 kind: Deployment #类型为deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 1 #副本数 selector: #匹配标签,必须与template中定义的标签一样 matchLabels: app: nginx template: #定义pod模板 metadata: labels: app: nginx #pod的标签与deployment选择的标签一致 spec: #定义容器 containers: - name: nginx image: nginx:1.16.0 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 #不配置资源限制requests,deployment无法获取监控数据,也无法通过HPA进行动态伸缩 resources: limits: cpu: 1 memory: "512Mi" requests: cpu: 500m memory: "512Mi"
运行清单
root@k8s-master01:/apps/k8s-yaml/hpa# kubectl apply -f nginx-deploy.yaml deployment.apps/nginx-deployment created root@k8s-master01:/apps/k8s-yaml/hpa# kubectl get pods NAME READY STATUS RESTARTS AGE nginx-deployment-777d596565-nnbtb 1/1 Running 0 3s root@k8s-master01:/apps/k8s-yaml/hpa# kubectl get deployments.apps NAME READY UP-TO-DATE AVAILABLE AGE nginx-deployment 1/1 1 1 78s
5.2.1手动命令部署
#nginx-deployment副本数,在cpu使用率超过10%时,最多扩大到4个;在cpu使用率不超过10%时,最少保障2个 root@k8s-master01:/apps/k8s-yaml/hpa# kubectl autoscale deployment nginx-deployment --cpu-percent=10 --min=2 --max=4 -n default horizontalpodautoscaler.autoscaling/nginx-deployment autoscaled #部署的期望副本为2个,原deployment为1个,不满足期望副本,会自动增加1个来满足期望值。 root@k8s-master01:/apps/k8s-yaml/hpa# kubectl get pod NAME READY STATUS RESTARTS AGE nginx-deployment-777d596565-j72n9 1/1 Running 0 4s nginx-deployment-777d596565-nnbtb 1/1 Running 0 4m55s #查看deployment的hpa值 root@k8s-master01:/apps/k8s-yaml/hpa# kubectl get hpa nginx-deployment NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE nginx-deployment Deployment/nginx-deployment 0%/10% 2 4 2 7m31s root@k8s-master01:/apps/k8s-yaml/hpa# kubectl describe hpa nginx-deployment Name: nginx-deployment Namespace: default Labels: <none> Annotations: <none> CreationTimestamp: Thu, 28 Oct 2021 22:38:22 +0800 Reference: Deployment/nginx-deployment Metrics: ( current / target ) resource cpu on pods (as a percentage of request): 0% (0) / 10% Min replicas: 2 Max replicas: 4 Deployment pods: 2 current / 2 desired Conditions: Type Status Reason Message ---- ------ ------ ------- AbleToScale True ScaleDownStabilized recent recommendations were higher than current one, applying the highest recent recommendation ScalingActive True ValidMetricFound the HPA was able to successfully calculate a replica count from cpu resource utilization (percentage of request) ScalingLimited False DesiredWithinRange the desired count is within the acceptable range Events: <none> #current:为当前值 #target:为配置的目标值
5.2.2 yaml清单部署
#apiVersion: autoscaling/v2beta1 apiVersion: autoscaling/v1 kind: HorizontalPodAutoscaler metadata: namespace: default name: pha-nginx-deployment labels: app: hpa-nginx-deployment version: v2beta1 spec: #定义deployment/nginx-deployment中部署hpa scaleTargetRef: apiVersion: apps/v1 #apiVersion: extensions/v1beta1 kind: Deployment name: nginx-deployment minReplicas: 2 #最少2个副本 maxReplicas: 20 #最大20个副本 targetCPUUtilizationPercentage: 60 #metrics: #- type: Resource # resource: # name: cpu # targetAverageUtilization: 60 #- type: Resource # resource: # name: memory
运行清单并测试
root@k8s-master01:/apps/k8s-yaml/hpa# kubectl apply -f hpa-nginx-deployment.yaml horizontalpodautoscaler.autoscaling/pha-nginx-deployment created root@k8s-master01:/apps/k8s-yaml/hpa# kubectl get hpa pha-nginx-deployment NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE pha-nginx-deployment Deployment/nginx-deployment 0%/60% 2 20 2 34s root@k8s-master01:/apps/k8s-yaml/hpa# kubectl get hpa pha-nginx-deployment NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE pha-nginx-deployment Deployment/nginx-deployment 0%/60% 2 20 2 34s root@k8s-master01:/apps/k8s-yaml/hpa# kubectl describe hpa pha-nginx-deployment Name: pha-nginx-deployment Namespace: default Labels: app=hpa-nginx-deployment version=v2beta1 Annotations: <none> CreationTimestamp: Thu, 28 Oct 2021 22:44:27 +0800 Reference: Deployment/nginx-deployment Metrics: ( current / target ) resource cpu on pods (as a percentage of request): 0% (0) / 60% Min replicas: 2 Max replicas: 20 Deployment pods: 2 current / 2 desired Conditions: Type Status Reason Message ---- ------ ------ ------- AbleToScale True ScaleDownStabilized recent recommendations were higher than current one, applying the highest recent recommendation ScalingActive True ValidMetricFound the HPA was able to successfully calculate a replica count from cpu resource utilization (percentage of request) ScalingLimited False DesiredWithinRange the desired count is within the acceptable range Events: <none>