Kubernetes HPA 原理与实践
自定义指标架构支持业务级别的指标监控,通过 Prometheus 生态系统提供丰富的自定义指标能力。
1.2.2 资源指标架构详解
核心组件:
1. Metrics Server:
• Kubernetes 官方提供的集群级别资源监控组件 • 从 kubelet 的 cAdvisor 收集 CPU 和内存使用数据 • 提供 metrics.k8s.io/v1beta1API 接口• 数据采集频率:默认 15 秒¹
2. kubelet & cAdvisor:
• kubelet:节点代理,负责 Pod 生命周期管理 • cAdvisor:容器监控组件,内置于 kubelet 中 • 实时收集容器的资源使用情况
3. HPA Controller:
• 查询 Metrics Server 获取资源指标 • 基于 CPU/内存利用率计算期望副本数 • 执行扩缩容决策
资源指标数据流:
cAdvisor → kubelet → Metrics Server → HPA Controller → Deployment1.2.3 自定义指标架构详解
核心组件:
1. Prometheus 监控栈:
• Prometheus Server:时序数据库,存储和查询指标数据 • ServiceMonitor/PodMonitor:定义监控目标的 CRD 资源 • 支持多维度标签和复杂查询语言 PromQL
2. Prometheus Adapter:
• 将 Prometheus 指标转换为 Kubernetes API 格式 • 提供 custom.metrics.k8s.io/v1beta1API 接口• 支持指标聚合和标签映射
3. Custom Metrics API:
• Kubernetes API 聚合层的扩展 • 为 HPA 提供统一的自定义指标查询接口 • 支持 Pod 级别和对象级别的指标
4. 应用程序:
• 通过 /metrics端点暴露 Prometheus 格式的指标• 包含业务相关的自定义指标(如请求速率、队列长度等)
自定义指标数据流:
应用 /metrics → Prometheus → Prometheus Adapter → Custom Metrics API → HPA Controller → Deployment1.2.4 两种架构的对比分析
| 部署复杂度 | ||
| 指标类型 | ||
| 扩展性 | ||
| 资源消耗 | ||
| 适用场景 | ||
| 学习成本 |
1.2.5 混合架构模式
在生产环境中,通常采用混合架构模式,同时使用资源指标和自定义指标:
apiVersion:autoscaling/v2
kind:HorizontalPodAutoscaler
metadata:
name:mixed-metrics-hpa
spec:
scaleTargetRef:
apiVersion:apps/v1
kind:Deployment
name:web-app
minReplicas:2
maxReplicas:20
metrics:
# 资源指标
-type:Resource
resource:
name:cpu
target:
type:Utilization
averageUtilization:70
# 自定义指标
-type:Pods
pods:
metric:
name:http_requests_per_second
target:
type:AverageValue
averageValue:"100"1.2.6 架构选择指导原则
选择资源指标架构的场景:
• ✅ 应用负载主要受 CPU/内存限制 • ✅ 需要快速部署和简单维护 • ✅ 团队对 Kubernetes 经验有限 • ✅ 成本敏感的环境
选择自定义指标架构的场景:
• ✅ 需要基于业务指标进行扩缩容 • ✅ 应用有复杂的性能特征 • ✅ 需要精细化的监控和告警 • ✅ 团队具备 Prometheus 运维能力
架构演进路径:
资源指标架构 → 混合架构 → 完全自定义指标架构参考资料:
1. Kubernetes Metrics Server 官方文档:默认采集间隔为 15 秒
1.3 HPA 工作原理
1.3.1 控制循环机制
HPA 的工作流程遵循经典的控制循环模式,每 15 秒执行一次:
1. 指标收集 → 2. 目标计算 → 3. 决策制定 → 4. 执行操作
↑ ↓
←←←←←←←←←←←← 5. 状态反馈 ←←←←←←←←←←←←←←←←←←详细步骤:
1. 指标收集:从 Metrics Server 或自定义指标源获取当前指标值 2. 目标计算:基于目标值计算期望的副本数 3. 决策制定:考虑扩缩容策略和稳定性窗口 4. 执行操作:更新目标资源的副本数 5. 状态反馈:监控扩缩容结果,为下次决策提供依据
1.3.2 核心算法详解
基础扩缩容算法:
期望副本数 = ceil(当前副本数 × (当前指标值 / 目标指标值))算法参数说明:
• 当前副本数:当前正在运行的 Pod 实例数量 • 当前指标值:从 Metrics API 获取的实时指标值 • 目标指标值:HPA 配置中设定的期望指标阈值 • ceil()函数:向上取整,确保副本数为正整数
实际计算案例:
场景:CPU 利用率扩容
- 当前副本数:3
- 当前 CPU 利用率:85%
- 目标 CPU 利用率:70%
计算过程:
期望副本数 = ceil(3 × (85 / 70)) = ceil(3 × 1.214) = ceil(3.642) = 4
结果:扩容 1 个副本(3 → 4)1.3.3 多指标决策算法
当配置多个指标时,HPA 采用最大值策略确保系统稳定性:
最终副本数 = max(指标1计算结果, 指标2计算结果, ..., 指标N计算结果)多指标计算示例:
场景:
- 当前副本数:4
- CPU 指标:当前 60%,目标 70% → 期望副本数 = ceil(4 × 60/70) = 4
- 请求指标:当前 450 req/s,目标 100 req/s per pod → 期望副本数 = ceil(4 × 450/400) = 5
最终结果:max(4, 5) = 5 副本1.3.4 安全机制与边界控制
1. 容忍度机制:
HPA 使用容忍度避免微小波动引起的频繁扩缩容:
默认容忍度:10%
扩容条件:当前指标值 > 目标指标值 × (1 + 容忍度)
缩容条件:当前指标值 < 目标指标值 × (1 - 容忍度)
示例:
- 目标 CPU 利用率:70%
- 扩容触发点:70% × 1.1 = 77%
- 缩容触发点:70% × 0.9 = 63%2. 扩缩容速率限制:
扩容限制:
- 单次扩容不超过当前副本数的 2 倍
- 扩容冷却期:3 分钟内不再扩容
缩容限制:
- 单次缩容不超过当前副本数的 50%
- 缩容冷却期:5 分钟内不再缩容3. 边界条件控制:
spec:
minReplicas:2# 最小副本数
maxReplicas:10# 最大副本数1.4 指标体系分类
1.4.1 资源指标 (Resource Metrics)
资源指标是 HPA 最基础的扩缩容依据,通过 Metrics Server 收集 Pod 的 CPU 和内存使用情况。
基于 Pod 的 CPU 和内存使用率:
metrics:
-type:Resource
resource:
name:cpu
target:
type:Utilization
averageUtilization:70# 70% CPU 利用率阈值
-type:Resource
resource:
name:memory
target:
type:Utilization
averageUtilization:80# 80% 内存利用率阈值适用场景:
• 计算密集型应用 • 内存密集型服务 • 通用 Web 应用
1.4.2 Pod 级别自定义指标 (Pods Metrics)
基于每个 Pod 的业务指标:
metrics:
-type:Pods
pods:
metric:
name:http_requests_per_second
target:
type:AverageValue
averageValue:"100"# 每个 Pod 100 req/s适用场景:
• 高并发 API 服务 • 消息队列处理 • 实时数据处理
1.4.3 对象级别自定义指标 (Object Metrics)
基于 Kubernetes 对象的指标:
metrics:
-type:Object
object:
metric:
name:http_requests_per_second_by_app
describedObject:
apiVersion:v1
kind:Namespace
name:production
target:
type:Value
value:"1000"# 命名空间级别 1000 req/s适用场景:
• 多租户应用 • 服务网格环境 • 跨服务协调扩容
第二部分:安装与配置
2.1 环境准备与兼容性检查
2.1.1 系统要求
• Kubernetes 版本:1.23+ (推荐 1.25+) • Metrics Server:v0.6.4+ • Prometheus:v2.30.0+ • Prometheus Adapter:v0.12.0+
2.1.2 验证集群状态
# 检查 Kubernetes 版本
kubectl version --short
# 验证 Metrics Server 状态
kubectl get deployment metrics-server -n kube-system
# 检查 API 服务可用性
kubectl get apiservice v1beta1.metrics.k8s.io2.2 Metrics Server 部署
2.2.1 快速部署
# 部署 Metrics Server
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
# 验证部署状态
kubectl get pods -n kube-system -l k8s-app=metrics-server2.2.2 配置调整(如需要)
# metrics-server-config.yaml
apiVersion:apps/v1
kind:Deployment
metadata:
name:metrics-server
namespace:kube-system
spec:
template:
spec:
containers:
-name:metrics-server
args:
---cert-dir=/tmp
---secure-port=4443
---kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
---kubelet-use-node-status-port
---metric-resolution=15s
---kubelet-insecure-tls# 仅用于测试环境2.3 Prometheus 监控栈部署
2.3.1 使用 Helm 快速部署
# 添加 Prometheus 社区 Helm 仓库
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
# 部署 kube-prometheus-stack
helm install prometheus prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace \
--set prometheus.prometheusSpec.serviceMonitorSelectorNilUsesHelmValues=false \
--set prometheus.prometheusSpec.podMonitorSelectorNilUsesHelmValues=false2.3.2 验证 Prometheus 部署
# 检查 Prometheus 组件状态
kubectl get pods -n monitoring
# 验证 Prometheus 服务
kubectl get svc -n monitoring prometheus-kube-prometheus-prometheus2.4 Prometheus Adapter 配置
2.4.1 部署 Prometheus Adapter
# 使用 Helm 部署
helm install prometheus-adapter prometheus-community/prometheus-adapter \
--namespace monitoring \
--set prometheus.url=http://prometheus-kube-prometheus-prometheus.monitoring.svc \
--set prometheus.port=90902.4.2 自定义指标配置
# prometheus-adapter-config.yaml
apiVersion:v1
kind:ConfigMap
metadata:
name:adapter-config
namespace:monitoring
data:
config.yaml:|
rules:
- seriesQuery: 'http_requests_per_second{namespace!="",pod!=""}'
resources:
overrides:
namespace: {resource: "namespace"}
pod: {resource: "pod"}
name:
matches: "^(.*)_per_second"
as: "${1}_per_second"
metricsQuery: 'sum(<<.Series>>{<<.LabelMatchers>>}) by (<<.GroupBy>>)'
-seriesQuery:'http_request_duration_seconds{namespace!="",pod!=""}'
resources:
overrides:
namespace: {resource:"namespace"}
pod: {resource:"pod"}
name:
matches:"^(.*)_duration_seconds"
as:"${1}_duration"
metricsQuery:'avg(<<.Series>>{<<.LabelMatchers>>}) by (<<.GroupBy>>)'2.5 验证 HPA 环境
2.5.1 检查 Custom Metrics API
# 验证自定义指标 API 可用性
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1" | jq .
# 检查可用的自定义指标
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/http_requests_per_second" | jq .2.5.2 基础 HPA 功能测试
# 创建测试 Deployment
kubectl create deployment test-app --image=nginx --replicas=1
# 创建基础 HPA
kubectl autoscale deployment test-app --cpu-percent=50 --min=1 --max=10
# 验证 HPA 状态
kubectl get hpa test-app
kubectl describe hpa test-app第三部分:基于 Spring Boot App 的实践
3.1 Spring Boot 应用开发
3.1.1 项目结构与依赖
创建一个支持 HPA 的 Spring Boot 微服务:
<!-- pom.xml -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
</dependencies>3.1.2 业务指标暴露
// MetricsController.java
@RestController
@RequestMapping("/api")
publicclassMetricsController {
privatefinal MeterRegistry meterRegistry;
privatefinal Counter requestCounter;
privatefinal Timer requestTimer;
publicMetricsController(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.requestCounter = Counter.builder("http_requests_total")
.description("Total HTTP requests")
.register(meterRegistry);
this.requestTimer = Timer.builder("http_request_duration_seconds")
.description("HTTP request duration")
.register(meterRegistry);
}
@GetMapping("/process")
public ResponseEntity<String> processRequest() {
return requestTimer.recordCallable(() -> {
requestCounter.increment();
// 模拟业务处理
try {
Thread.sleep(100 + (int)(Math.random() * 200));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return ResponseEntity.status(500).body("Request interrupted");
}
return ResponseEntity.ok("Request processed successfully");
});
}
@GetMapping("/health")
public ResponseEntity<Map<String, Object>> health() {
Map<String, Object> status = newHashMap<>();
status.put("status", "UP");
status.put("timestamp", System.currentTimeMillis());
status.put("requests_per_second", getCurrentRequestsPerSecond());
return ResponseEntity.ok(status);
}
privatedoublegetCurrentRequestsPerSecond() {
// 计算每秒请求数
return meterRegistry.get("http_requests_total").counter().count() /
(System.currentTimeMillis() / 1000.0);
}
}
#### 3.1.3 应用配置
```yaml
# application.yml
server:
port: 8080
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: always
metrics:
enabled: true
prometheus:
enabled: true
metrics:
export:
prometheus:
enabled: true
web:
server:
request:
autotime:
enabled: true
logging:
level:
com.example: INFO
org.springframework.web: DEBUG3.2 容器化与 Kubernetes 部署
3.2.1 Dockerfile 优化
# Dockerfile
FROM openjdk:17-jre-slim
# 创建应用目录
WORKDIR /app
# 复制应用 JAR
COPY target/spring-boot-hpa-demo-*.jar app.jar
# 设置 JVM 参数以支持容器环境
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
# 暴露端口
EXPOSE8080
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
CMD curl -f http://localhost:8080/actuator/health || exit 1
# 启动应用
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]3.2.2 Kubernetes 部署配置
# deployment.yaml
apiVersion:apps/v1
kind:Deployment
metadata:
name:spring-boot-hpa-demo
namespace:default
labels:
app:spring-boot-hpa-demo
spec:
replicas:2
selector:
matchLabels:
app:spring-boot-hpa-demo
template:
metadata:
labels:
app:spring-boot-hpa-demo
annotations:
prometheus.io/scrape:"true"
prometheus.io/port:"8080"
prometheus.io/path:"/actuator/prometheus"
spec:
containers:
-name:app
image:spring-boot-hpa-demo:latest
ports:
-containerPort:8080
name:http
resources:
requests:
cpu:100m
memory:256Mi
limits:
cpu:500m
memory:512Mi
livenessProbe:
httpGet:
path:/actuator/health
port:8080
initialDelaySeconds:60
periodSeconds:30
readinessProbe:
httpGet:
path:/actuator/health
port:8080
initialDelaySeconds:30
periodSeconds:10
env:
-name:SPRING_PROFILES_ACTIVE
value:"production"
---
apiVersion:v1
kind:Service
metadata:
name:spring-boot-hpa-demo
namespace:default
labels:
app:spring-boot-hpa-demo
spec:
selector:
app:spring-boot-hpa-demo
ports:
-port:80
targetPort:8080
name:http
type:ClusterIP3.3 HPA 配置与测试
3.3.1 多指标 HPA 配置
# hpa-multi-metrics.yaml
apiVersion:autoscaling/v2
kind:HorizontalPodAutoscaler
metadata:
name:spring-boot-hpa-demo
namespace:default
spec:
scaleTargetRef:
apiVersion:apps/v1
kind:Deployment
name:spring-boot-hpa-demo
minReplicas:2
maxReplicas:20
metrics:
# CPU 利用率指标
-type:Resource
resource:
name:cpu
target:
type:Utilization
averageUtilization:70
# 内存利用率指标
-type:Resource
resource:
name:memory
target:
type:Utilization
averageUtilization:80
# 自定义指标:每秒请求数
-type:Pods
pods:
metric:
name:http_requests_per_second
target:
type:AverageValue
averageValue:"100"
# 自定义指标:请求响应时间
-type:Pods
pods:
metric:
name:http_request_duration_seconds
target:
type:AverageValue
averageValue:"200m"# 200ms
behavior:
scaleUp:
stabilizationWindowSeconds:60
policies:
-type:Percent
value:100
periodSeconds:60
-type:Pods
value:2
periodSeconds:60
selectPolicy:Max
scaleDown:
stabilizationWindowSeconds:300
policies:
-type:Percent
value:50
periodSeconds:60
selectPolicy:Min3.3.2 负载测试脚本
#!/bin/bash
# load-test.sh
APP_URL="http://spring-boot-hpa-demo.default.svc.cluster.local"
DURATION=600 # 10 分钟测试
CONCURRENT_USERS=50
echo"开始负载测试..."
echo"目标 URL: $APP_URL"
echo"测试时长: $DURATION 秒"
echo"并发用户: $CONCURRENT_USERS"
# 使用 Apache Bench 进行负载测试
ab -n 100000 -c $CONCURRENT_USERS -t $DURATION"$APP_URL/api/process"
# 或使用 wrk 进行更精确的负载测试
# wrk -t12 -c$CONCURRENT_USERS -d${DURATION}s --latency "$APP_URL/api/process"
echo"负载测试完成"3.3.3 监控 HPA 行为
# 实时监控 HPA 状态
watch -n 5 'kubectl get hpa spring-boot-hpa-demo'
# 查看 HPA 详细信息
kubectl describe hpa spring-boot-hpa-demo
# 监控 Pod 扩缩容
watch -n 5 'kubectl get pods -l app=spring-boot-hpa-demo'
# 查看指标值
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/http_requests_per_second" | jq .3.4 实践结果分析
3.4.1 扩缩容行为观察
通过负载测试,可以观察到以下 HPA 行为:
1. 扩容阶段:
• 负载增加时,CPU 和请求指标同时上升 • HPA 根据最大值策略选择最激进的扩容建议 • Pod 数量从 2 个逐步扩展到 8-12 个
• 系统达到平衡状态,指标稳定在目标值附近 • HPA 停止扩容,维持当前副本数
• 负载减少后,HPA 等待稳定窗口期(5分钟) • 逐步缩减 Pod 数量,避免过度缩容
3.4.2 性能指标总结
总结
本文通过理论阐述、环境配置和实际案例,全面展示了 Kubernetes HPA 的工作原理和实践应用。通过基于 Spring Boot 的完整示例,验证了 HPA 在真实业务场景中的有效性和可靠性。
核心收获:
1. HPA 提供了强大的自动扩缩容能力,能够根据多种指标智能调整应用规模 2. 合理的指标选择和阈值设置是 HPA 成功的关键 3. 监控和观测是优化 HPA 性能的重要手段
最佳实践建议:
1. 从简单的 CPU/内存指标开始,逐步引入业务指标 2. 设置合理的扩缩容策略,避免频繁波动 3. 建立完善的监控体系,持续优化 HPA 配置
参考文献
1. Kubernetes HPA 官方文档 2. Prometheus Adapter 配置指南 3. Spring Boot Actuator 监控指南