Kubernetes 资源优化:VPA/HPA 配置、资源请求与限制、节点亲和性与成本优化
Kubernetes 集群因过度配置资源和意外云账单而臭名昭著。研究表明,平均每个 Kubernetes 集群浪费了 50-70% 的分配计算资源。本指南涵盖了从正确的请求和限制到动态自动缩放和智能节点放置的系统性资源优化。
资源请求与限制

理解区别
- Request(请求):Kubernetes 为调度预留的量;保证可用
- Limit(限制):容器可以使用的最大值;超过 CPU 会被节流,超过内存会导致 OOM 杀死
resources:
requests:
memory: "256Mi"
cpu: "250m" # 0.25 vCPU
limits:
memory: "512Mi"
cpu: "1000m" # 1 vCPU
找到合适的值
从观察实际使用情况开始:
# 命名空间中所有 Pod 的当前使用情况
kubectl top pods -n production --sort-by=cpu
# 随时间变化的资源使用情况(需要 metrics-server)
kubectl top pods -n production --containers
# 通过 PromQL 查看历史使用情况
# 容器 7 天内的平均 CPU 使用率
avg_over_time(
rate(container_cpu_usage_seconds_total{
container="api",
namespace="production"
}[5m])[7d:]
)
# P95 内存使用量
quantile_over_time(0.95,
container_memory_working_set_bytes{
container="api",
namespace="production"
}[7d]
)
根据历史数据设置请求
# 经验法则:
# CPU 请求 = P50 使用量 * 1.2
# 内存请求 = P95 使用量 * 1.1
# CPU 限制 = P99 使用量 * 1.5(或移除 CPU 限制以避免节流)
# 内存限制 = P99 使用量 * 1.5(OOM 风险与浪费之间的权衡)
CPU 节流问题
将 CPU 限制设置得太低会导致即使节点有空闲资源也会发生节流:
# 检查 CPU 节流率
kubectl exec -n production deploy/api -- cat /sys/fs/cgroup/cpu/cpu.stat
# PromQL:节流率
rate(container_cpu_cfs_throttled_seconds_total{container="api"}[5m])
/ rate(container_cpu_cfs_periods_total{container="api"}[5m])
许多团队对延迟敏感的工作负载完全移除 CPU 限制,依靠 HPA 进行扩展而不是节流。
水平 Pod 自动缩放器(HPA)
HPA 根据指标调整 Pod 副本的数量。
基于 CPU 的 HPA
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api
minReplicas: 2
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60 # 目标 CPU 利用率 60%
behavior:
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Pods
value: 4
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300 # 等待 5 分钟再缩容
policies:
- type: Percent
value: 10
periodSeconds: 60

自定义指标 HPA
根据请求率、队列深度或任何 Prometheus 指标进行缩放:
metrics:
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "1000" # 每个 Pod 目标 1000 RPS
- type: External
external:
metric:
name: sqs_queue_depth
selector:
matchLabels:
queue: order-processing
target:
type: AverageValue
averageValue: "50" # 每个 Pod 50 条消息
安装 Prometheus Adapter 以将 Prometheus 指标暴露给 HPA:
# prometheus-adapter values.yaml
rules:
custom:
- seriesQuery: 'http_requests_total{namespace!="",pod!=""}'
resources:
overrides:
namespace: { resource: "namespace" }
pod: { resource: "pod" }
name:
matches: "^(.*)_totalquot;
as: "${1}_per_second"
metricsQuery: 'sum(rate(<<.Series>>{<<.LabelMatchers>>}[2m])) by (<<.GroupBy>>)'
垂直 Pod 自动缩放器(VPA)
VPA 根据观察到的使用情况自动调整资源请求。
# 安装 VPA
kubectl apply -f https://github.com/kubernetes/autoscaler/releases/latest/download/vertical-pod-autoscaler.yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: api-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: api
updatePolicy:
updateMode: "Auto" # Auto, Recreate, Initial, 或 Off
resourcePolicy:
containerPolicies:
- containerName: api
minAllowed:
cpu: 100m
memory: 128Mi
maxAllowed:
cpu: 4
memory: 4Gi
controlledResources: ["cpu", "memory"]
controlledValues: RequestsAndLimits
重要:VPA 和 HPA 不能同时管理 CPU/内存。使用 VPA 进行应用程序的资源大小调整,使用 HPA 进行副本数量缩放。对于需要两者的工作负载,将 VPA 设置为仅推荐模式(updateMode: "Off")并手动应用推荐。
节点亲和性与拓扑
节点亲和性
spec:
affinity:
nodeAffinity:
# 硬性要求:必须在此节点类型上运行
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node.kubernetes.io/instance-type
operator: In
values:
- m5.xlarge
- m5.2xlarge
# 软偏好:优先选择 us-east-1a 区域的节点
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- us-east-1a
Pod 反亲和性实现高可用
spec:
affinity:
podAntiAffinity:
# 硬性:两个副本不能在同一节点上
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: api
topologyKey: kubernetes.io/hostname
# 软性:优先跨可用区分布
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 50
podAffinityTerm:
labelSelector:
matchLabels:
app: api
topologyKey: topology.kubernetes.io/zone
拓扑分布约束(较新方法)
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: api
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: api

竞价/可抢占实例以降低成本
在竞价实例上运行无状态工作负载可节省 60-90% 的费用:
# Karpenter NodePool 用于竞价实例
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: spot-workers
spec:
template:
metadata:
labels:
capacity-type: spot
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"]
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: node.kubernetes.io/instance-type
operator: In
values: ["m5.xlarge", "m5.2xlarge", "m4.xlarge", "m4.2xlarge"]
nodeClassRef:
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
name: default
disruption:
consolidationPolicy: WhenUnderutilized
consolidateAfter: 30s
优雅处理竞价实例终止
# 处理 2 分钟终止通知
spec:
terminationGracePeriodSeconds: 120
containers:
- name: api
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 10"] # 排空连接
资源配额和 LimitRange
每个命名空间的 ResourceQuota
apiVersion: v1
kind: ResourceQuota
metadata:
name: production-quota
namespace: production
spec:
hard:
requests.cpu: "100"
requests.memory: "200Gi"
limits.cpu: "200"
limits.memory: "400Gi"
pods: "200"
services.loadbalancers: "5"
persistentvolumeclaims: "20"
默认请求的 LimitRange
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: production
spec:
limits:
- type: Container
default: # 如果未指定,默认限制
cpu: 500m
memory: 256Mi
defaultRequest: # 如果未指定,默认请求
cpu: 100m
memory: 128Mi
max: # 允许的最大值
cpu: 4
memory: 8Gi
min: # 要求的最小值
cpu: 50m
memory: 64Mi
使用 Kubecost 进行成本监控
helm install kubecost cost-analyzer \
--repo https://kubecost.github.io/cost-analyzer/ \
--namespace kubecost \
--create-namespace \
--set kubecostToken="your-token"
关键 Kubecost 查询:
- 每个命名空间的成本
- 每个团队标签的成本
- 空闲/浪费的资源
- 节省建议
Cluster Autoscaler 与 Karpenter 对比
| 特性 | Cluster Autoscaler | Karpenter |
|---|---|---|
| 缩放触发 | 节点组利用率 | 待处理 Pod |
| 新节点速度 | 2-5 分钟 | 30-60 秒 |
| 实例选择 | 预定义节点组 | 动态装箱 |
| 竞价支持 | 通过节点组 | 原生支持 |
对于新集群,推荐使用 Karpenter,因为它具有更快的缩放和更好的装箱能力。
结论
Kubernetes 资源优化是一个持续的过程。从测量实际使用情况和设置准确的请求开始。添加 HPA 以处理流量峰值而无需过度配置。使用 VPA 建议来调整容器大小。通过拓扑约束跨可用区分布工作负载。在竞价实例上运行无状态工作负载以大幅节省成本。通过系统优化,团队通常可以将 Kubernetes 成本降低 40-60%,同时不牺牲可靠性。