深入掌握Pod
12、Pod的扩缩容
在实际生产系统中,我们经常会遇到某个服务需要扩容的场景,也可能会遇到由于资源紧张或者工作负载降低而需要减少服务实例数量的场景。此时可以利用Deployment/RC的Scale机制来完成这些工作。
Kubernetes对Pod的扩缩容操作提供了手动和自动两种模式,手动模式通过执行kubectl scale命令或通过RESTful API对一个Deployment/RC进行Pod副本数量的设置,即可一键完成。自动模式则需要用户根据某个性能指标或者自定义业务指标,并指定Pod副本数量的范围,系统将自动在这个范围内根据性能指标的变化进行调整。
12.1、手动扩缩容机制
以Deployment nginx为例:
apiVersion: apps/v1
kind: Deployment # 资源类型:Kubernetes Deployment(工作负载控制器)
metadata:
name: nginx-deployment # Deployment 的名称
spec:
replicas: 3 # 期望运行的 Pod 副本数量(这里是 3 个)
selector: # 标签选择器:Deployment 通过此规则来关联和管理哪些 Pod
matchLabels:
app: nginx # 必须与下方 template 中的标签保持一致
template:
metadata:
labels:
app: nginx # 为该 Deployment 创建的 Pod 打上标签
spec:
containers:
- name: nginx # 容器名称
image: nginx:1.7.9 # 使用的 Nginx 镜像及指定版本
ports:
- containerPort: 80 # 容器监听的端口
已运行的Pod副本数量为3个:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-3973253433-scz37 1/1 Running 0 5s
nginx-deployment-3973253433-x8fsq 1/1 Running 0 5s
nginx-deployment-3973253433-x9z8z 1/1 Running 0 5s
通过kubectl scale命令可以将Pod副本数量从初始的3个更新为5个:
$ kubectl scale deployment nginx-deployment --replicas 5
deployment "nginx-deployment" scaled
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-3973253433-3gt27 1/1 Running 0 4s
nginx-deployment-3973253433-7jls2 1/1 Running 0 4s
nginx-deployment-3973253433-scz37 1/1 Running 0 4m
nginx-deployment-3973253433-x8fsq 1/1 Running 0 4m
nginx-deployment-3973253433-x9z8z 1/1 Running 0 4m
将–replicas设置为比当前Pod副本数量更小的数字,系统将会“杀掉”一些运行中的Pod,以实现应用集群缩容:
$ kubectl scale deployment nginx-deployment --replicas=1
deployment "nginx-deployment" scaled
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-3973253433-x9z8z 1/1 Running 0 6m
12.2、自动扩缩容机制
Kubernetes从1.1版本开始,新增了名为Horizontal PodAutoscaler(HPA)的控制器,用于实现基于CPU使用率进行自动Pod扩缩容的功能。HPA控制器基于Master的kube-controller-manager服务启动参数–horizontal-pod-autoscaler-sync-period定义的探测周期(默认值为15s),周期性地监测目标Pod的资源性能指标,并与 HPA资源对象中的扩缩容条件进行对比,在满足条件时对Pod副本数量进行调整。
Kubernetes在早期版本中,只能基于Pod的CPU使用率进行自动扩缩容操作,关于CPU使用率的数据来源于Heapster组件。Kubernetes从1.6版本开始,引入了基于应用自定义性能指标的HPA机制,并在1.9版本之后逐步成熟。本节对Kubernetes的HPA的原理和实践进行详细说明。
12.2.1、HPA的工作原理
Kubernetes中的某个Metrics Server(Heapster或自定义Metrics Server)持续采集所有Pod副本的指标数据。HPA控制器通过Metrics Server的API(Heapster的API或聚合API)获取这些数据,基于用户定义的扩缩容规则进行计算,得到目标Pod副本数量。当目标Pod副本数量与当前副本数量不同时,HPA控制器就向Pod的副本控制器(Deployment、RC或ReplicaSet)发起scale操作,调整Pod的副本数量,完成扩缩容操作。下图描述了HPA体系中的关键组件和工作流程。

接下来首先对HPA能够管理的指标类型、扩缩容算法、HPA对象的配置进行详细说明,然后通过一个完整的示例对如何搭建和使用基于自定义指标的HPA体系进行说明。
12.2.2、指标的类型
Master的kube-controller-manager服务持续监测目标Pod的某种性能指标,以计算是否需要调整副本数量。目前Kubernetes支持的指标类型如下。
- Pod资源使用率:Pod级别的性能指标,通常是一个比率值,例如CPU使用率。
- Pod自定义指标:Pod级别的性能指标,通常是一个数值,例如接收的请求数量。
- Object自定义指标或外部自定义指标:通常是一个数值,需要容器应用以某种方式提供,例如通过HTTP URL“/metrics”提供,或者使用外部服务提供的指标采集URL。
Kubernetes从1.11版本开始,弃用基于Heapster组件完成Pod的CPU使用率采集的机制,全面转向基于MetricsServer完成数据采集。Metrics Server将采集到的Pod性 能指标数据通过聚合API(Aggregated API)如metrics.k8s.io 、 custom.metrics.k8s.io和external.metrics.k8s.io提供给HPA控制器进行查询。关于聚合API和API聚合器(API Aggregator)的概念详见9.4节的说明。
12.2.3、扩缩容算法详解
Autoscaler控制器从聚合API获取到Pod性能指标数据之后,基于下面的算法计算出目标Pod副本数量,与当前运行的Pod副本数量进行对比,决定是否需要进行扩缩容操作:
desiredReplicas = ceil[currentReplicas * ( currentMetricValue /
desiredMetricValue )]
即当前副本数×(当前指标值/期望的指标值),将结果向上取整。
以CPU请求数量为例,如果用户设置的期望指标值为100m,当前实际使用的指标值为200m,则计算得到期望的Pod副本数量应为两个(200/100=2)。如果设置的期望指标值为50m,计算结果为0.5,则向上取整值为1,得到目标Pod副本数量应为1个。
当计算结果与1非常接近时,可以设置一个容忍度让系统不做扩缩容操作。容忍度通过kube-controller-manager服务的启动参数–horizontal-pod-autoscaler-tolerance进行设置,默认值为0.1(即10%),表示基于上述算法得到的结果在[-10%-+10%]区间内,即[0.9-1.1],控制器都不会进行扩缩容操作。
也可以将期望指标值(desiredMetricValue)设置为指标的平均值类型,例如targetAverageValue或targetAverageUtilization,此时当前指标值(currentMetricValue)的算法为所有Pod副本当前指标值的总和除以Pod副本数量得到的平均值。
此外,存在几种Pod异常的情况,如下所述。
- Pod正在被删除(设置了删除时间戳):将不会计入目标Pod副本数量。
- Pod的当前指标值无法获得:本次探测不会将这个Pod纳入目标Pod副本数量,后续的探测会被重新纳入计算范围。
- 如果指标类型是CPU使用率,则对于正在启动但是还未达到Ready状态的Pod,也暂时不会纳入目标副本数量范围。可以通过kube-controller-manager服务的启动参数–horizontal-pod-autoscaler-initial-readiness-delay设置首次探测Pod是否Ready的延时时间,默认值为30s。另一个启动参数–horizontal-pod-autoscaler-cpu-initialization-period设置首次采集Pod的CPU使用率的延时时间。
在计算“当前指标值/期望的指标值”(currentMetricValue / desiredMetricValue)时将不会包括上述这些异常Pod。
当存在缺失指标的Pod时,系统将更保守地重新计算平均值。系统会假设这些Pod在需要缩容(Scale Down)时消耗了期望指标值的100%,在需要扩容(Scale Up)时消耗了期望指标值的0%,这样可以抑制潜在的扩缩容操作。
此外,如果存在未达到Ready状态的Pod,并且系统原本会在不考虑缺失指标或NotReady的Pod情况下进行扩展,则系统仍然会保守地假设这些Pod消耗期望指标值的0%,从而进一步抑制扩容操作。
如果在HorizontalPodAutoscaler中设置了多个指标,系统就会对每个指标都执行上面的算法,在全部结果中以期望副本数的最大值为最终结果。如果这些指标中的任意一个都无法转换为期望的副本数(例如无法获取指标的值),系统就会跳过扩缩容操作。
最后,在HPA控制器执行扩缩容操作之前,系统会记录扩缩容建议信息(Scale Recommendation)。控制器会在操作时间窗口(时间范围可以配置)中考虑所有的建议信息,并从中选择得分最高的建议。这个值可通过kube-controller-manager服务的启动参数–horizontal-pod-autoscaler-downscale-stabilization-window进行配置,默认值为5min。这个配置可以让系统更为平滑地进行缩容操作,从而消除短时间内指标值快速波动产生的影响。
12.2.4、HorizontalPodAutoscaler配置详解
Kubernetes将HorizontalPodAutoscaler资源对象提供给用户来定义扩缩容的规则。
HorizontalPodAutoscaler资源对象处于Kubernetes的API组“autoscaling”中,目前包括v1和v2两个版本。其中autoscaling/v1仅支持基于CPU使用率的自动扩缩容,autoscaling/v2则用于支持基于任意指标的自动扩缩容配置,包括基于资源使用率、Pod指标、其他指标等类型的指标数据,当前版本为autoscaling/v2beta2。
下面对HorizontalPodAutoscaler的配置和用法进行说明。
-
(1)基于autoscaling/v1版本的HorizontalPodAutoscaler配置,仅可以设置CPU使用率:
apiVersion: autoscaling/v1 kind: HorizontalPodAutoscaler # 资源类型:水平 Pod 自动扩缩容控制器 (HPA) metadata: name: php-apache # HPA 的名称 spec: scaleTargetRef: apiVersion: apps/v1 # 目标控制器的 API 版本 kind: Deployment # 目标控制器类型(这里是 Deployment) name: php-apache # 目标 Deployment 的名称 minReplicas: 1 # 最小 Pod 副本数:当负载很低时,缩容到的最少副本数 maxReplicas: 10 # 最大 Pod 副本数:当负载很高时,扩容到的最多副本数 targetCPUUtilizationPercentage: 50 # 目标 CPU 平均利用率:当所有 Pod 的平均 CPU 利用率超过 50% 时,HPA 会触发扩容主要参数如下。
scaleTargetRef:目标作用对象,可以是Deployment 、 ReplicationController或ReplicaSet。targetCPUUtilizationPercentage:期望每个Pod的CPU使用率都为50%,该使用率基于Pod设置的CPU Request值进行计算,例如该值为200m,那么系统将维持Pod的实际CPU使用值为100m。minReplicas和maxReplicas:Pod副本数量的最小值和最大值,系统将在这个范围内进行自动扩缩容操作,并维持每个Pod的CPU使用率为50%。
为了使用autoscaling/v1版本的HorizontalPodAutoscaler,需要预先安装Heapster组件或Metrics Server,用于采集Pod的CPU使用率。Heapster从Kubernetes 1.11版本开始进入弃用阶段,本节不再对Heapster进行详细说明。关于Metrics Server的说明请参考9.4节的介绍,本节主要对基于自定义指标进行自动扩缩容的设置进行说明。
-
(2)基于autoscaling/v2beta2的HorizontalPodAutoscaler配置:
apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler # 资源类型:水平 Pod 自动扩缩容控制器 (HPA) metadata: name: php-apache # HPA 的名称 spec: scaleTargetRef: apiVersion: apps/v1 # 目标控制器的 API 版本 kind: Deployment # 目标控制器类型(这里是 Deployment) name: php-apache # 目标 Deployment 的名称 minReplicas: 1 # 最小 Pod 副本数:当负载很低时,缩容到的最少副本数 maxReplicas: 10 # 最大 Pod 副本数:当负载很高时,扩容到的最多副本数 metrics: # 扩缩容的监控指标配置 - type: Resource # 指标类型:基于资源(如 CPU、内存) resource: name: cpu # 监控的目标资源类型为 CPU target: type: Utilization # 目标指标类型为资源利用率 averageUtilization: 50 # 目标 CPU 平均利用率:当所有 Pod 的平均 CPU 利用率超过 50% 时触发扩容主要参数如下。
scaleTargetRef:目标作用对象,可以是Deployment 、 ReplicationController或ReplicaSet。minReplicas和maxReplicas:Pod副本数量的最小值和最大值,系统将在这个范围内进行自动扩缩容操作,并维持每个Pod的CPU使用率为50%。metrics:目标指标值。在metrics中通过参数type定义指标的类型;通过参数target定义相应的指标目标值,系统将在指标数据达到目标值时(考虑容忍度的区间,见前面算法部分的说明)触发扩缩容操作。
可以将metrics中的type(指标类型)设置为以下三种,可以设置一个或多个组合,如下所述。
Resource:基于资源的指标值,可以设置的资源为CPU和内存。Pods:基于Pod的指标,系统将对全部Pod副本的指标值进行平均值计算。Object:基于某种资源对象(如Ingress)的指标或应用系统的任意自定义指标。
Resource类型的指标可以设置CPU和内存。对于CPU使用率,在target参数中设置averageUtilization定义目标平均CPU使用率。对于内存资源,在target参数中设置AverageValue定义目标平均内存使用值。指标数据可以通过API“metrics.k8s.io”进行查询,要求预先启动Metrics Server服务。
Pods类型和Object类型都属于自定义指标类型,指标的数据通常需要搭建自定义Metrics Server和监控工具进行采集和处理。指标数据可以通过API“custom.metrics.k8s.io”进行查询,要求预先启动自定义Metrics Server服务。
类型为Pods的指标数据来源于Pod对象本身,其target指标类型只能使用AverageValue,示例如下:
metrics:
- type: Pods # 指标类型:Pods 指标,针对所有 Pod 的某个自定义指标计算平均值
pods:
metric:
name: packets-per-second # 自定义指标的名称(通常由 Prometheus Adapter 等指标适配器收集并提供给 K8s API)
target:
type: AverageValue # 目标类型:平均值类型(将所有 Pod 的该指标总和除以当前 Pod 副本数)
averageValue: 1k # 目标阈值:当每个 Pod 的平均每秒数据包数(packets-per-second)达到 1k(即 1000)时,HPA 将触发扩容
其中,设置Pod的指标名为packets-per-second,在目标指标平均值为1000时触发扩缩容操作。
类型为Object的指标数据来源于其他资源对象或任意自定义指标,其target指标类型可以使用Value或AverageValue(根据Pod副本数计算平均值)进行设置。下面对几种常见的自定义指标给出示例和说明。
例1,设置指标的名称为requests-per-second,其值来源于Ingress“main-route”,将目标值(value)设置为2000,即在Ingress的每秒请求数量达到2000个时触发扩缩容操作:
metrics:
- type: Object # 指标类型:Object 指标,用于监控非 Pod 的其他 Kubernetes 资源(如 Ingress、Service 等)
object:
metric:
name: requests-per-second # 自定义指标名称(例如通过 Prometheus 收集的每秒请求数)
describedObject:
apiVersion: networking.k8s.io/v1 # 目标资源的 API 版本(旧版本常写为 extensions/v1beta1)
kind: Ingress # 目标资源类型(这里是 Ingress)
name: main-route # 目标 Ingress 的名称
target:
type: Value # 目标类型:绝对值类型(针对整个对象实例的总体指标值)
value: 2k # 目标阈值:当该 Ingress 的总每秒请求数达到 2k(即 2000)时,HPA 将触发扩容
例2,设置指标的名称为http_requests,并且该资源对象具有标签“verb=GET”,在指标平均值达到500时触发扩缩容操作:
metrics:
- type: Object # 指标类型:Object 指标,用于监控非 Pod 资源的自定义指标
object:
metric:
name: http_requests # 监控的指标名称
selector:
matchLabels:
verb: GET # 标签选择器:使用标准的 matchLabels 来过滤特定的请求(例如只统计 GET 请求)
describedObject:
apiVersion: networking.k8s.io/v1
kind: Service # 目标资源类型(这里以 Service 为例)
name: my-service # 目标资源的名称
target:
type: AverageValue # 目标类型:平均值类型
averageValue: 500 # 目标阈值:当该对象的指定请求平均值达到 500 时触发扩容
还可以在同一个HorizontalPodAutoscaler资源对象中定义多个类型的指标,系统将针对每种类型的指标都计算Pod副本的目标数量,以最大值为准进行扩缩容操作。例如:
apiVersion: autoscaling/v2 # 推荐使用现代的 autoscaling/v2 API 版本
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
namespace: default
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-apache
minReplicas: 1
maxReplicas: 10
metrics: # 组合了多种类型的自动扩缩容指标
- type: Resource # 1. 资源指标:监控 CPU 利用率
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50 # 当平均 CPU 利用率超过 50% 时触发扩容
- type: Pods # 2. Pods 指标:针对所有 Pod 的自定义指标
pods:
metric:
name: packets-per-second
target:
type: AverageValue # v2 版本标准语法:使用 target 嵌套块定义类型和阈值
averageValue: 1k
- type: Object # 3. Object 指标:监控非 Pod 资源(如 Ingress)的指标
object:
metric:
name: requests-per-second
describedObject:
apiVersion: networking.k8s.io/v1 # 推荐将旧的 extensions/v1beta1 更新为 networking.k8s.io/v1
kind: Ingress
name: main-route
target:
type: Value # v2 版本标准语法:使用 target 嵌套块定义类型和阈值
value: 10k
从1.10版本开始,Kubernetes引入了对外部系统指标的支持。例如,用户使用了公有云服务商提供的消息服务或外部负载均衡器,希望基于这些外部服务的性能指标(如消息服务的队列长度、负载均衡器的QPS)对自己部署在Kubernetes中的服务进行自动扩缩容操作。这时,就可以在metrics参数部分设置type为External来设置自定义指标,然后就可以通过API“external.metrics.k8s.io”查询指标数据了。当然,这同样要求自定义Metrics Server服务已正常工作。
例3,设置指标的名称为queue_messages_ready,具有queue=worker_tasks标签在目标指标平均值为30时触发自动扩缩容操作:
metrics:
- type: External # 指标类型:External 指标,用于监控 Kubernetes 集群外部的系统指标(如云服务或外部消息队列)
external:
metric:
name: queue_messages_ready # 外部监控系统中的指标名称(例如 RabbitMQ 队列中准备就绪的消息数)
selector:
matchLabels:
queue: worker_tasks # 标签选择器:使用标准的 matchLabels 来过滤特定的队列实例
target:
type: AverageValue # 目标类型:平均值类型
averageValue: 30 # 目标阈值:当外部队列中每个实例的平均就绪消息数达到 30 时,HPA 将触发扩容
在使用外部服务的指标时,要安装、部署能够对接到Kubernetes HPA模型的监控系统,并且完全了解监控系统采集这些指标的机制,后续的自动扩缩容操作才能完成。
Kubernetes推荐尽量使用type为Object的HPA配置方式,这可以通过使用Operator模式,将外部指标通过CRD(自定义资源)定义为API资源对象来实现。
2.12.5、基于自定义指标的HPA实践
下面通过一个完整的示例,对如何搭建和使用基于自定义指标的HPA体系进行说明。
基于自定义指标进行自动扩缩容时,需要预先部署自定义Metrics Server,目前可以使用基于Prometheus、Microsoft Azure、Datadog Cluster等系统的Adapter实现自定义Metrics Server,未来还将提供基于GoogleStackdriver的实现自定义Metrics Server。读者可以参考官网https://github.com/kubernetes/metrics/blob/master/IMPLEMENTATIONS.md#custom-metrics-api的说明。
本节基于Prometheus监控系统对HPA的基础组件部署和HPA配置进行详细说明。
基于Prometheus的HPA架构如图所示。

关键组件包括如下。
- Prometheus:定期采集各Pod的性能指标数据。
- Custom Metrics Server:自定义MetricsServer,用Prometheus Adapter进行具体实现。它 从Prometheus服务采集性能指标数据,通过Kubernetes的Metrics Aggregation层将自定义指标API注册到Master的API Server中,以/apis/custom.metrics.k8s.io路径提供指标数据。
- HPA Controller : Kubernetes的HPA控制器,基于用户定义的HorizontalPodAutoscaler进行自动扩缩容操作。
接下来对整个系统的部署过程进行说明。
-
(1)在Master的API Server启动Aggregation层,通过设置kube-apiserver服务的下列启动参数进行开启。
--requestheader-client-ca-file=/etc/kubernetes/ssl_keys/ca.crt:客户端CA证书。--requestheader-allowed-names=:允许访问的客户端common names列表,通过header中由–requestheader- username-headers参数指定的字段获取。客户端common names的名称需要在client-ca-file中进行配置,将其设置为空值时,表示任意客户端都可以访问。--requestheader-extra-headers-prefix=X-Remote-Extra-:请求头中需要检查的前缀名。--requestheader-group-headers=X-Remote-Group:请求头中需要检查的组名。--requestheader-username-headers=X-Remote-User:请求头中需要检查的用户名。--proxy-client-cert-file=/etc/kubernetes/ssl_keys/kubelet_client.crt:在请求期间验证Aggregator的客户端CA证书。--proxy-client-key-file=/etc/kubernetes/ssl_keys/kubelet_client.key:在请求期间验证Aggregator的客户端私钥。
配置kube-controller-manager服务中HPA的相关启动参数(可选配置)如下。
--horizontal-pod-autoscaler-sync-period=10s:HPA控制器同步Pod副本数量的时间间隔,默认值为15s。--horizontal-pod-autoscaler-downscale-stabilization=1m0s:执行缩容操作的等待时长,默认值为5min。--horizontal-pod-autoscaler-initial-readiness-delay=30s:等待Pod达到Ready状态的时延,默认值为30min。--horizontal-pod-autoscaler-tolerance=0.1:扩缩容计算结果的容忍度,默认值为0.1,表示[-10%-+10%]。
-
(2)部署Prometheus,这里使用Operator模式进行部署。
首先,使用下面的YAML配置文件部署prometheus-operator:apiVersion: apps/v1 kind: Deployment # 资源类型:Deployment 工作负载控制器 metadata: labels: k8s-app: prometheus-operator name: prometheus-operator # Deployment 名称 spec: replicas: 1 # 副本数为 1 selector: matchLabels: k8s-app: prometheus-operator template: metadata: labels: k8s-app: prometheus-operator # Pod 模板标签,必须与 selector 匹配 spec: containers: - image: quay.io/coreos/prometheus-operator:v0.17.0 # 镜像地址及版本 imagePullPolicy: IfNotPresent # 镜像拉取策略:本地存在则优先使用本地镜像 name: prometheus-operator # 容器名称 ports: - containerPort: 8080 # 容器暴露的端口 name: http resources: # 容器资源配额限制 limits: cpu: 200m # CPU 上限:200 毫核 memory: 100Mi # 内存上限:100 MB requests: cpu: 100m # CPU 请求量:100 毫核 memory: 50Mi # 内存请求量:50 MB这个prometheus-operator会自动创建名为monitoring.coreos.com的CRD资源。
然后,通过Operator的配置部署Prometheus服务:
--- apiVersion: monitoring.coreos.com/v1 kind: Prometheus # 资源类型:Prometheus Operator 定义的自定义资源 (CRD) metadata: name: prometheus labels: app: prometheus prometheus: prometheus spec: replicas: 1 # 副本数:1 个 baseImage: prom/prometheus # 使用的 Prometheus 镜像名称 version: v2.8.0 # Prometheus 版本 serviceMonitorSelector: matchLabels: service-monitor: function # 选择带有此标签的 ServiceMonitor 来自动发现监控目标 resources: requests: memory: 300Mi # 资源请求:分配至少 300Mi 内存 --- apiVersion: v1 kind: Service # 资源类型:Kubernetes Service,用于暴露 Prometheus 服务 metadata: name: prometheus labels: app: prometheus prometheus: prometheus spec: selector: prometheus: prometheus # 标签选择器:将流量转发给带有该标签的 Prometheus Pod ports: - name: http # 端口名称 port: 9090 # Service 暴露的端口(同时也是容器监听的端口)确认Prometheus Operator和Prometheus服务正常运行:
# kubectl get pods NAME READY STATUS RESTARTS AGE prometheus-operator-7c976597bc-xzdf5 1/1 Running 0 51m prometheus-prometheus-0 2/2 Running 0 42m -
(3)部署自定义Metrics Server,这里以PrometheusAdapter的实现进行部署。下面的YAML配置文件主要包含Namespace、ConfigMap、Deployment、Service和自定义API资源custom.metrics.k8s.io/v1beta1,这里将它们部署在一个新的Namespace“custom-metrics”中。
--- kind: Namespace apiVersion: v1 metadata: name: custom-metrics --- apiVersion: v1 kind: ConfigMap metadata: name: adapter-config namespace: custom-metrics data: config.yaml: | rules: - seriesQuery: '{__name__=~"^container_.*",container_name!="POD",namespace!="",pod_name!=""}' seriesFilters: [] resources: overrides: namespace: resource: namespace pod_name: resource: pod name: matches: ^container_(.*)_seconds_total$ as: "" metricsQuery: sum(rate(<<.Series>>{<<.LabelMatchers>>,container_name!="POD"}[1m])) by (<<.GroupBy>>) - seriesQuery: '{__name__=~"^container_.*",container_name!="POD",namespace!="",pod_name!=""}' seriesFilters: - isNot: ^container_.*_seconds_total$ resources: overrides: namespace: resource: namespace pod_name: resource: pod name: matches: ^container_(.*)_total$ as: "" metricsQuery: sum(rate(<<.Series>>{<<.LabelMatchers>>,container_name!="POD"}[1m])) by (<<.GroupBy>>) - seriesQuery: '{__name__=~"^container_.*",container_name!="POD",namespace!="",pod_name!=""}' seriesFilters: - isNot: ^container_.*_total$ resources: overrides: namespace: resource: namespace pod_name: resource: pod name: matches: ^container_(.*)$ as: "" metricsQuery: sum(<<.Series>>{<<.LabelMatchers>>,container_name!="POD"}) by (<<.GroupBy>>) - seriesQuery: '{namespace!="",__name__!~"^container_.*"}' seriesFilters: - isNot: .*_total$ resources: template: <<.Resource>> name: matches: "" as: "" metricsQuery: sum(<<.Series>>{<<.LabelMatchers>>}) by (<<.GroupBy>>) - seriesQuery: '{namespace!="",__name__!~"^container_.*"}' seriesFilters: - isNot: .*_seconds_total resources: template: <<.Resource>> name: matches: ^(.*)_total$ as: "" metricsQuery: sum(rate(<<.Series>>{<<.LabelMatchers>>}[1m])) by (<<.GroupBy>>) - seriesQuery: '{namespace!="",__name__!~"^container_.*"}' seriesFilters: [] resources: template: <<.Resource>> name: matches: ^(.*)_seconds_total$ as: "" metricsQuery: sum(rate(<<.Series>>{<<.LabelMatchers>>}[1m])) by (<<.GroupBy>>) resourceRules: cpu: containerQuery: sum(rate(container_cpu_usage_seconds_total{<<.LabelMatchers>>}[1m])) by (<<.GroupBy>>) nodeQuery: sum(rate(container_cpu_usage_seconds_total{<<.LabelMatchers>>, id='/'}[1m])) by (<<.GroupBy>>) resources: overrides: instance: resource: node namespace: resource: namespace pod_name: resource: pod containerLabel: container_name memory: containerQuery: sum(container_memory_working_set_bytes{<<.LabelMatchers>>}) by (<<.GroupBy>>) nodeQuery: sum(container_memory_working_set_bytes{<<.LabelMatchers>>,id='/'}) by (<<.GroupBy>>) resources: overrides: instance: resource: node namespace: resource: namespace pod_name: resource: pod containerLabel: container_name window: 1m --- apiVersion: apps/v1 kind: Deployment metadata: name: custom-metrics-server namespace: custom-metrics labels: app: custom-metrics-server spec: replicas: 1 selector: matchLabels: app: custom-metrics-server template: metadata: labels: app: custom-metrics-server spec: containers: - name: custom-metrics-server image: directxman12/k8s-prometheus-adapter-amd64 imagePullPolicy: IfNotPresent args: - --prometheus-url=http://prometheus.default.svc:9090/ - --metrics-relist-interval=30s - --v=10 - --config=/etc/adapter/config.yaml - --logtostderr=true ports: - containerPort: 443 securityContext: runAsUser: 0 volumeMounts: - mountPath: /etc/adapter/ name: config readOnly: true volumes: - name: config configMap: name: adapter-config --- apiVersion: v1 kind: Service metadata: name: custom-metrics-server namespace: custom-metrics spec: ports: - port: 443 targetPort: 443 selector: app: custom-metrics-server --- apiVersion: apiregistration.k8s.io/v1 kind: APIService metadata: name: v1beta1.custom.metrics.k8s.io spec: service: name: custom-metrics-server namespace: custom-metrics group: custom.metrics.k8s.io version: v1beta1 insecureSkipTLSVerify: true groupPriorityMinimum: 100 versionPriority: 100确认custom-metrics-server正常运行:
-
(4)部署应用程序,它会在HTTP URL“/metrics”路径提供名为http_requests_total的指标值:
--- apiVersion: apps/v1 kind: Deployment # 资源类型:Deployment 工作负载控制器 metadata: name: sample-app labels: app: sample-app spec: replicas: 1 # 副本数:1 个 selector: matchLabels: app: sample-app template: metadata: labels: app: sample-app # Pod 模板标签,必须与 selector 匹配 spec: containers: - image: luxas/autoscale-demo:v0.1.2 # 示例应用镜像(常用于 HPA/扩缩容测试) imagePullPolicy: IfNotPresent name: metrics-provider ports: - name: http containerPort: 8080 # 容器内部监听的端口 --- apiVersion: v1 kind: Service # 资源类型:Kubernetes Service,用于将流量暴露给应用 metadata: name: sample-app labels: app: sample-app spec: ports: - name: http port: 80 # Service 暴露的对外端口 targetPort: 8080 # 转发到后端容器的端口 selector: app: sample-app # 将流量路由到带有此标签的 Pod部署成功之后,可以在应用的URL“/metrics”中查看指标http_requests_total的值:
# kubectl get service sample-app NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE sample-app ClusterIP 169.169.43.252 <none> 80/TCP 86m # curl 169.169.43.252/metrics # HELP http_requests_total The amount of requests served by the server in total # TYPE http_requests_total counter http_requests_total 1 -
(5)创建一个Prometheus的ServiceMonitor对象,用于监控应用程序提供的指标:
apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor # 资源类型:Prometheus Operator 定义的 ServiceMonitor,用于告诉 Prometheus 如何抓取 Service 的监控指标 metadata: name: sample-app # ServiceMonitor 的名称 labels: service-monitor: function # 标签:与前面 Prometheus 资源中的 serviceMonitorSelector 对应,使其能够被自动发现 spec: selector: matchLabels: app: sample-app # 选择器:通过标签去匹配需要被监控的 Service(例如我们刚才创建的名为 sample-app 的 Service) endpoints: - port: http # 抓取指标的目标端口名称(对应 Service 中定义的名为 http 的端口)关键配置参数如下。
Selector:设置为Pod的Label“app: sample-app”。Endpoints:设置为在Service中定义的端口名称“http”。
-
(6)创建一个HorizontalPodAutoscaler对象,用于为HPA控制器提供用户期望的自动扩缩容配置。
apiVersion: autoscaling/v2 # 推荐使用现代的 autoscaling/v2 API 版本 kind: HorizontalPodAutoscaler # 资源类型:水平 Pod 自动扩缩容控制器 metadata: name: sample-app # HPA 的名称 spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment # 目标控制器类型(这里是 Deployment) name: sample-app # 目标 Deployment 的名称 minReplicas: 1 # 最小 Pod 副本数 maxReplicas: 10 # 最大 Pod 副本数 metrics: # 扩缩容监控指标 - type: Pods # 指标类型:Pods 指标(针对所有 Pod 的自定义指标计算平均值) pods: metric: name: http_requests # 自定义指标名称(例如每个 Pod 的 HTTP 请求数) target: type: AverageValue # 目标类型:平均值类型 averageValue: 500m # 目标阈值:当每个 Pod 的平均请求数达到 500m(即 0.5,或每秒 0.5 个请求,视具体适配器定义而定)时触发扩容关键配置参数如下。
scaleTargetRef:设置HPA的作用对象为之前部署的Deployment“sample-app”。type=Pods:设置指标类型为Pods,表示从Pod获取指标数据。metric.name=http_requests:将指标的名称设置为“http_requests”,是自定义Metrics Server将应用程序提供的指标“http_requests_total”经过计算转换成的一个新比率值,即sum(rate(http_requests_total{namespace=“xx”,pod=“xx”}[1m])) by pod,指过去1min内全部Pod指标http_requests_total总和的每秒平均值。target:将指标http_requests的目标值设置为500m,类型为AverageValue,表示基于全部Pod副本数据计算平均值。目标Pod副本数量将使用公式“http_requests当前值/500m”进行计算。minReplicas和maxReplicas:将扩缩容区间设置为1~10(单位是Pod副本)。
此时可以通过查看自定义Metrics Server提供的URL“custom.metrics.k8s.io/v1beta1”查看Pod的指标是否已经被成功采集,并能够通过聚合API进行查询:
# kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/http_requests?sel ector=app%3Dsample-app" {"kind":"MetricValueList","apiVersion":"custom.metrics.k8s.io/v1beta1","meta data":{"selfLink":"/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/% 2A/http_requests"},"items":[{"describedObject":{"kind":"Pod","namespace":"defaul t","name":"sample-app-579f977995-jz98h","apiVersion":"/v1"},"metricName":"http_r equests","timestamp":"2019-03-16T17:54:38Z","value":"33m"}]}从结果中看到正确的value值,说明自定义Metrics Server工作正常。
查看HorizontalPodAutoscaler的详细信息,可以看到其成功从自定义Metrics Server处获取了应用的指标数据,可以进行扩缩容操作:
# kubectl describe hpa.v2beta2.autoscaling sample-app Name: sample-app Namespace: default Labels: <none> Annotations: <none> CreationTimestamp: Sun, 17 Mar 2019 01:05:33 +0800 Reference: Deployment/sample-app Metrics: ( current / target ) "http_requests" on pods: 33m / 500m Min replicas: 1 Max replicas: 10 Deployment pods: 1 current / 1 desired Conditions: Type Status Reason Message ---- ------ ------ ------- AbleToScale True ReadyForNewScale recommended size matches current size ScalingActive True ValidMetricFound the HPA was able to successfully calculate a replica count from pods metric http_requests ScalingLimited False DesiredWithinRange the desired count is within the acceptable range -
(7)对应用的服务地址发起HTTP访问请求,验证HPA自动扩容机制。例如,可以使用如下脚本对应用进行压力测试:
# for i in {1..100000}; do wget -q -O-169.169.43.252 > /dev/null; done一段时间之后,观察HorizontalPodAutoscaler和Pod数量的变化,可以看到自动扩容的过程:
# kubectl describe hpa.v2beta2.autoscaling sample-app Name: sample-app Namespace: default Labels: <none> Annotations: <none> CreationTimestamp: Sun, 17 Mar 2019 02:01:30 +0800 Reference: Deployment/sample-app Metrics: ( current / target ) "http_requests" on pods: 4296m / 500m Min replicas: 1 Max replicas: 10 Deployment pods: 10 current / 10 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 pods metric http_requests ScalingLimited True TooManyReplicas the desired replica count is more than the maximum replica count Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal SuccessfulRescale 67s horizontal-pod-autoscaler New size: 4; reason: pods metric http_requests above target Normal SuccessfulRescale 56s horizontal-pod-autoscaler New size: 8; reason: pods metric http_requests above target Normal SuccessfulRescale 45s horizontal-pod-autoscaler New size: 10; reason: pods metric http_requests above target发现Pod数量扩容到了10个(被maxReplicas参数限制的最大值):
# kubectl get pods -l app=sample-app NAME READY STATUS RESTARTS AGE sample-app-579f977995-dtgcw 1/1 Running 0 32s sample-app-579f977995-hn5bd 1/1 Running 0 70s sample-app-579f977995-jz98h 1/1 Running 0 75s sample-app-579f977995-kllhq 1/1 Running 0 90s sample-app-579f977995-p5d44 1/1 Running 0 85s sample-app-579f977995-q6rxb 1/1 Running 0 70s sample-app-579f977995-rhn5d 1/1 Running 0 70s sample-app-579f977995-tjc8q 1/1 Running 0 86s sample-app-579f977995-tzthf 1/1 Running 0 70s sample-app-579f977995-wswcx 1/1 Running 0 32s停止访问应用服务,等待一段时间后,观察HorizontalPodAutoscaler和Pod数量的变化,可以看到缩容操作:
# kubectl describe hpa.v2beta2.autoscaling sample-app Name: sample-app Namespace: default Labels: <none> Annotations: <none> CreationTimestamp: Sun, 17 Mar 2019 02:01:30 +0800 Reference: Deployment/sample-app Metrics: ( current / target ) "http_requests" on pods: 33m / 500m Min replicas: 1 Max replicas: 10 Deployment pods: 1 current / 1 desired Conditions: Type Status Reason Message ---- ------ ------ ------- AbleToScale True ReadyForNewScale recommended size matches current size ScalingActive True ValidMetricFound the HPA was able to successfully calculate a replica count from pods metric http_requests ScalingLimited False DesiredWithinRange the desired count is within the acceptable range Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal SuccessfulRescale 6m48s horizontal-pod-autoscaler New size: 4; reason: pods metric http_requests above target Normal SuccessfulRescale 6m37s horizontal-pod-autoscaler New size: 8; reason: pods metric http_requests above target Normal SuccessfulRescale 6m26s horizontal-pod-autoscaler New size: 10; reason: pods metric http_requests above target Normal SuccessfulRescale 47s horizontal-pod-autoscaler New size: 1; reason: All metrics below target发现Pod的数量已经缩容到最小值1个:
# kubectl get pods -l app=sample-app NAME READY STATUS RESTARTS AGE sample-app-579f977995-dtgcw 1/1 Running 0 10m
本文深入讲解Kubernetes中Pod的扩缩容机制,重点涵盖手动扩缩容(kubectl scale)与自动扩缩容(HPA)原理及实践。详细解析HorizontalPodAutoscaler工作流程、支持的三类指标(资源使用率、Pod自定义指标、Object/外部指标)、扩缩容核心算法(含容忍度与异常Pod处理)、autoscaling/v1与v2beta2配置差异,并基于Prometheus Adapter完成自定义指标HPA的端到端部署与验证。
1591

被折叠的 条评论
为什么被折叠?



