深色模式
K8s 成本与装箱优化
摘要:多数集群的真实利用率只有 10%-30%。本文讲清三个利用率口径的区别,给出 requests 调优的具体方法、提升装箱率的四种手段,以及如何用工具持续观测而不拍脑袋。
适用环境
- 可用 K8s 集群 +
kubectl - 已装 metrics-server(基础)与 Prometheus(推荐)
- 有按量计费的云节点或可量化的资源成本
操作步骤
一、先搞清三个利用率口径
bash
# 1. 分配率(requests / 可分配量)—— 调度视角
kubectl describe node | grep -A6 "Allocated resources"
# 2. 实际使用率(真实用量 / 可分配量)—— 成本视角
kubectl top nodes
# 3. 使用/请求比(真实用量 / requests)—— 浪费视角1
2
3
4
5
6
7
2
3
4
5
6
7
| 口径 | 数值高说明什么 | 典型问题 |
|---|---|---|
| 分配率 80% + 使用率 20% | requests 定得太高,大量资源被占着不用 | 最常见的浪费 |
| 分配率 30% + 使用率 25% | 装箱率低,节点有余量但装不进新 Pod | 资源碎片 |
| 使用/请求比 > 90% | requests 定得太低,有 OOM 风险 | 稳定性风险 |
建议
优化的第一目标不是「把使用率拉满」,而是把 requests 定准。requests 虚高会导致调度器以为节点满了,不得不一直扩容节点——这才是成本失控的根源。
二、量化当前浪费
bash
# 每个命名空间 requests 与实际用量的对比
kubectl top pod -A --no-headers | awk '{print $1"\t"$2"\t"$4}' | head -30
# 找出 requests 远大于实际用量的 Pod
kubectl get pod -A -o custom-columns=NS:.metadata.namespace,NAME:.metadata.name,CPU_REQ:.spec.containers[0].resources.requests.cpu,MEM_REQ:.spec.containers[0].resources.requests.memory1
2
3
4
5
2
3
4
5
用 Prometheus 算整体利用率:
promql
# 集群 CPU 分配率
sum(kube_pod_container_resource_requests{resource="cpu"})
/ sum(kube_node_status_allocatable{resource="cpu"})
# 集群 CPU 实际使用率
sum(rate(container_cpu_usage_seconds_total{container!=""}[5m]))
/ sum(kube_node_status_allocatable{resource="cpu"})
# 使用 / 请求 比(越低越浪费)
sum(rate(container_cpu_usage_seconds_total{container!=""}[5m]))
/ sum(kube_pod_container_resource_requests{resource="cpu"})1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
三、手段一:把 requests 定准
bash
kubectl top pod --containers -n <命名空间>1
做法:取两周内 P95 用量作为 requests,峰值的 1.5-2 倍作为 limits。用 VPA 自动推荐:
bash
helm install vpa prometheus-community/kube-state-metrics 2>/dev/null
kubectl apply -f https://github.com/kubernetes/autoscaler/raw/master/vertical-pod-autoscaler/hack/vpa-up.sh.yaml 2>/dev/null || true1
2
2
VPA 的 recommend 模式只给建议不修改,适合先观察:
yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: web-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: web
updatePolicy:
updateMode: "Off" # Off=只建议,Auto=自动改1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
bash
kubectl describe vpa web-vpa # 看 Recommendation 段1
注意
VPA 的 updateMode: Auto 会重建 Pod 来应用新资源值,与 HPA 同时作用于同一资源(CPU/内存)时会冲突打架。两者要么分资源(HPA 管 CPU、VPA 管内存),要么只用其一。
四、手段二:用 HPA 削峰填谷
bash
kubectl autoscale deploy web --cpu-percent=60 --min=2 --max=201
夜间流量低时自动缩容,把节点让出来。配合 Cluster Autoscaler 才真正省钱(见手段四)。
五、手段三:节点组混部与 Spot 实例
bash
kubectl label node <节点> workload-type=batch
kubectl get node -L workload-type,node.kubernetes.io/instance-type1
2
2
思路:
- 在线服务跑包年/预留实例(稳定、贵);
- 批处理、CI、离线任务跑 Spot/抢占式实例(便宜 60%-90%,可中断),用 taint + toleration 隔离。
bash
kubectl taint node <spot节点> spot=true:NoSchedule1
yaml
tolerations:
- key: "spot"
operator: "Equal"
value: "true"
effect: "NoSchedule"
nodeSelector:
workload-type: batch1
2
3
4
5
6
7
2
3
4
5
6
7
六、手段四:Cluster Autoscaler 缩节点
bash
helm install cluster-autoscaler autoscaler/cluster-autoscaler \
-n kube-system \
--set autoDiscovery.clusterName=<集群名> \
--set awsRegion=<区域>
kubectl -n kube-system logs -l app.kubernetes.io/name=aws-cluster-autoscaler --tail=501
2
3
4
5
2
3
4
5
CA 缩容的前提:节点上所有 Pod 的 requests 之和低于阈值、无不可迁移 Pod(本地存储、DaemonSet 之外的单副本)。
七、手段五:用 descheduler 治理碎片
bash
helm install descheduler descheduler/descheduler -n kube-system \
--set deschedulerPolicy.strategies.RemoveDuplicates.enabled=true \
--set deschedulerPolicy.strategies.LowNodeUtilization.enabled=true1
2
3
2
3
它会驱逐部分 Pod 触发重新调度,把负载往已有节点集中,从而让空闲节点被 CA 缩掉。
八、用工具做可视化
bash
helm install opencost opencost/opencost -n opencost --create-namespace
kubectl -n opencost port-forward svc/opencost 9003:90031
2
2
OpenCost / Kubecost 能按命名空间、Deployment、label 维度拆分成本,让「谁花了钱」变得可见。
九、容易忽略的成本项
- 闲置的 LoadBalancer Service:每个都是独立计费的云 LB。
- 孤儿 PVC:StatefulSet 缩容后残留的磁盘。
- 跨可用区流量:Pod 跨 AZ 调用产生的流量费。
- 日志/监控存储:往往能占到总成本的 10%-20%。
bash
kubectl get svc -A -o wide | grep LoadBalancer
kubectl get pvc -A | grep -v Bound1
2
2
验证
- [ ] 能算出集群的分配率与实际使用率,并知道差值
- [ ] VPA 给出的推荐值与实际用量接近
- [ ] 低峰期 HPA 副本数确实下降
- [ ] 无闲置 LoadBalancer 与孤儿 PVC
常见坑
- 只看使用率不看分配率:使用率低但分配率高,CA 不会缩容,钱照花。
- requests 一刀切设很大:为了「保险」给每个 Pod 4 核 8G,整集群装不下几个服务。
- HPA 与 VPA 同时管同一个指标:互相冲突导致副本数震荡。
- 开了 Spot 但没做容错:Pod 被回收时服务直接挂,必须配多副本 + PDB。
- 缩容触发了服务中断:Descheduler 驱逐了单副本服务,务必先给关键服务配 PDB。