适用场景
部署在 Kubernetes 中的 Web、API 或消费服务在高峰期 CPU 已长期接近上限,但 HorizontalPodAutoscaler(HPA)始终保持原副本数;也可能在 kubectl get hpa 中看到 CPU 显示为 <unknown>。本文以基于 CPU 利用率的 HPA 为例,给出一条从工作负载到聚合指标 API 的排查路径。
现象与影响
典型表现包括:
- HPA 的
TARGETS为<unknown>/70%,或当前值一直是0%; - Pod 已出现排队、5xx 或延迟升高,副本数却没有增加;
kubectl top pod也无法返回资源使用率;- HPA Events 出现
FailedGetResourceMetric、failed to get cpu utilization。
不要先把 maxReplicas 调大。HPA 只有拿到“实时使用量”和“Pod 请求量”后才能计算利用率;指标链路断开时,扩大上限不会产生扩容动作。
HPA 计算依赖
CPU 利用率模式下,HPA 的核心计算是:
期望副本数 = ceil(当前副本数 × 当前平均 CPU 利用率 / 目标利用率)
其中“利用率”不是节点 CPU 百分比,而是容器实际 CPU 使用量除以该容器 resources.requests.cpu。因此至少需要同时满足:
metrics-server能从每个 kubelet 获取/metrics/resource;- API aggregation 层的
metrics.k8s.io可用; - 被 HPA 选择到的 Pod 容器声明了 CPU request;
- HPA 的 selector 与 Deployment 的标签一致,且 Pod 已 Ready。
排查步骤
1. 从 HPA 状态和事件开始
kubectl -n production get hpa api -o wide
kubectl -n production describe hpa api
重点看 Metrics、Conditions 和末尾 Events。例如 unable to get metrics for resource cpu: no metrics returned from resource metrics API 说明问题在指标链路;missing request for cpu 则直接指向工作负载资源声明。
2. 对比 metrics API 与 HPA 视角
kubectl top nodes
kubectl -n production top pods -l app=api --containers
kubectl get --raw /apis/metrics.k8s.io/v1beta1/nodes | head
kubectl get apiservice v1beta1.metrics.k8s.io
kubectl top 和 raw API 都失败时,不必在应用内继续找原因。APIService 的 Available 必须为 True;若为 False,执行下面命令取得精确原因:
kubectl describe apiservice v1beta1.metrics.k8s.io
kubectl -n kube-system get pods -l k8s-app=metrics-server -o wide
kubectl -n kube-system logs deploy/metrics-server --tail=200
日志中常见的 x509: cannot validate certificate 是 kubelet 服务端证书的 SAN 不包含节点 InternalIP;context deadline exceeded 常见于控制面到节点 10250 端口的网络策略或防火墙不通;403 则要检查 kubelet authentication/authorization 或 metrics-server 的 RBAC。
3. 核对 Pod 的 CPU request
kubectl -n production get deploy api \
-o jsonpath='{range .spec.template.spec.containers[*]}{.name}{"\trequest="}{.resources.requests.cpu}{"\n"}{end}'
输出为空的容器会使基于 CPU utilization 的 HPA 无法计算该 Pod。一个容易漏掉的点是 sidecar:只要 HPA 选择到的 Pod 中有相关容器未设置 request,指标可能被跳过。补齐所有业务容器和 sidecar 的 request,例如:
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: "1"
memory: 512Mi
200m 表示 0.2 个 CPU 核,不是 200MB。request 应以稳定负载的 P95 为起点,并预留突发空间;盲目设置得过小会让利用率虚高,造成频繁扩缩容。
一次定位与修复示例
某 API 服务的 HPA 配置为目标 CPU 70%、最小 3 副本。高峰时每个 Pod 实际使用约 350m CPU,理论利用率应为 350m / 200m = 175%,应该迅速扩容。实际输出却是:
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS
api Deployment/api <unknown>/70% 3 12 3
排查发现 metrics-server 日志报 kubelet 证书校验失败。测试环境节点使用自签名 kubelet 证书,且证书未包含 InternalIP。短期恢复措施是在 metrics-server 参数中增加 --kubelet-insecure-tls,并确保按 InternalIP 选取节点地址:
args:
- --kubelet-preferred-address-types=InternalIP,Hostname,ExternalIP
- --kubelet-insecure-tls
修改后等待 metrics-server 滚动完成,再依次验证:
kubectl -n kube-system rollout status deploy/metrics-server
kubectl top pod -n production -l app=api
kubectl -n production describe hpa api
生产环境不应把 --kubelet-insecure-tls 当作长期方案。根治方式是为 kubelet 服务端证书签发包含节点 InternalIP/DNS 的证书,并在 metrics-server 中恢复 TLS 校验。这样既避免 HPA 失效,也避免指标采集链路被中间人伪造。
让 HPA 行为更稳定
指标恢复后,建议显式配置缩容稳定窗口,减少瞬时流量回落造成的抖动:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
minReplicas: 3
maxReplicas: 12
behavior:
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 25
periodSeconds: 60
扩容每分钟最多翻倍,缩容则以五分钟窗口和每分钟 25% 限速进行。具体数值应匹配应用启动时间、连接预热时间和下游承受能力。对于主要受队列长度或 RPS 驱动的服务,也应考虑通过 Prometheus Adapter 提供业务指标,而不是只看 CPU。
预防清单
- 为
v1beta1.metrics.k8s.ioAPIService 的 Available 状态建立告警; - 在发布检查中执行
kubectl top pod,将指标链路作为集群基础能力验收; - 用准入策略要求受 HPA 管理的容器声明 CPU/内存 request;
- 为 HPA 的
ScalingActive=False、副本数到达上限和持续高延迟分别告警; - 演练指标服务重启、节点证书轮换和网络策略变更,避免故障只在流量高峰暴露。
总结
HPA 不扩容时,先判断是“没有指标”还是“计算结果不需要扩容”。依次验证 HPA 事件、metrics.k8s.io、metrics-server 到 kubelet 的连接,以及容器 CPU request,通常能很快收敛问题。恢复后再通过扩缩容行为策略和监控闭环,才能让弹性能力在高峰真正可用。
Discussion
评论