K8s 的账单常常很迷惑:买了一堆节点,但每个团队到底用了多少?很多 Pod 的 Request 写得很"慷慨",实际用量不到 20%;节点池空转却照付钱。成本优化不是"压榨资源",而是"让每块钱对应到真实的工作负载"。本指南把 K8s 成本优化的全链路讲清楚:成本怎么构成与分摊、怎么用 VPA/KRR 画像真实用量、节点池与 Spot 怎么组合、Karpenter 怎么把节点数对准实际需求,以及用 OpenCost/Kubecost 把成本变成可观测的指标。
目录
- 1. 成本构成与分摊模型
- 2. 资源画像:Request 与真实用量
- 3. VPA 与资源推荐
- 4. 节点池与 Spot 实例
- 5. Karpenter 与弹性伸缩
- 6. 成本工具:OpenCost 与 Kubecost
- 7. 降本优先级与 ROI
- 8. 成本指标与看板
- 9. 生产最佳实践
1. 成本构成与分摊模型
1.1 K8s 账单由什么构成
云账单按"资源"计价,K8s 账单则要把实例成本分摊到工作负载。分摊要素:**计算(CPU)**按节点规格 × 时长、内存按申请量、存储按 PV 容量与 IOPS、网络按跨区域/公网流量(K8s 工具常算不到,要补)、托管费(EKS/GKE/AKS 的集群管理费)。三个口径别混:节点成本(买的)≠ Pod 成本(用的)≠ 业务成本(分摊的)。
1.2 分摊模型的两种做法
按 Request 分摊(默认):成本 = 节点单价 × (该 Pod 的 requests / 节点总容量),简单稳定但高估"要得多用得少"的 Pod。按实际用量分摊:成本 = 节点单价 × (实际用量 / 节点总量),更贴近真实但波动大、采集成本高。生产建议:用 Request 分摊做"预算",用实际用量做"优化依据"——谁占的承诺资源谁买单,但优化看真实用量。
2. 资源画像:Request 与真实用量
2.1 为什么要画像
现实很普遍:Deployment 里 requests 是"拍脑袋"写的(比如 CPU 4 核、内存 8Gi,实际 P99 只用 500m/1Gi),节点被"虚占",其他 Pod 挤不上,节点却要多买。画像 = 用历史指标回答:真实用量是多少(P50/P95/P99)、Request 应该设多少(要留多少余量)。
2.2 用 Prometheus 看用量
# CPU 利用率(按 request 归一)
sum by (pod) (rate(container_cpu_usage_seconds_total{pod=~"order-.*"}[5m]))
/ sum by (pod) (kube_pod_container_resource_requests{pod=~"order-.*",resource="cpu"})
# 内存峰值
max_over_time(container_memory_working_set_bytes{pod=~"order-.*"}[7d])
画像经验:看 P95/P99 而非平均值;CPU 容忍短尖峰(可限流),内存是硬上限(OOM 即死);内存 requests 建议取 P95~P99,CPU requests 可适度收紧。
3. VPA 与资源推荐
3.1 VPA 原理
VPA(Vertical Pod Autoscaler)观察 Pod 实际用量,给出/自动调整 requests 与 limits。模式:Off(只给推荐,不自动改,推荐用于"画像起步")、Initial(只在创建时设初值)、Auto(自动更新并重启 Pod 生效)。注意 VPA 不能与 HPA(按 CPU 伸缩)同指标混用,会打架。
3.2 VPA 配置示例
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: order-vpa
namespace: shop
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: order
updatePolicy:
updateMode: "Off"
resourcePolicy:
containerPolicies:
- containerName: "*"
controlledResources: ["cpu", "memory"]
minAllowed: { cpu: 100m, memory: 128Mi }
maxAllowed: { cpu: "4", memory: 8Gi }
查看推荐:kubectl get vpa order-vpa -n shop -o jsonpath='{.status.recommendations}'。
3.3 KRR:命令行的轻量画像
**KRR(Kubernetes Resource Recommender)**一条命令扫描全集群:krr simple 输出每个 Deployment 的推荐表(如 Deployment order: cpu request 4 → 1.2(省 70%),mem 8Gi → 3Gi)。
ℹ️ VPA Off 模式的用法:先
updateMode: Off挂上一周让 VPA/KRR 收集数据,再人工 review 推荐值,最后才切 Auto。上来就 Auto 会频繁重启 Pod。
4. 节点池与 Spot 实例
4.1 节点池分层
按负载特性分池:通用池(在线服务,稳定随业务伸缩)、批处理池(Job/训练,可中断、Spot 优先)、系统池(kube-system/监控,最小规模、不可中断)、GPU 池(贵,严格按需伸缩)。反模式:一个池跑所有负载 → 高峰互相挤、Spot 与关键服务混跑。
4.2 Spot 实例的正确用法
Spot(抢占式/竞价实例)价格是按需的 30%~70%,但随时可能被回收,只适合能容忍中断/能重新拉起的负载:批处理 Job(失败可重试)、无状态在线服务(多副本可漂移)、数据处理/CI。不适合:单副本有状态服务(数据库)、关键控制面、监控存储。用 nodeSelector 把 Job 拉到 Spot 池:spec.nodeSelector: { lifecycle: spot }。
Spot 的坑:
- 云商给约 2 分钟回收告警,用 node-drain/终止处理程序响应
- 至少 2 副本 + PDB,回收一个不影响
- 别把 Spot 当主力:占比过高,高峰大规模回收会雪崩
5. Karpenter 与弹性伸缩
5.1 为什么需要 Karpenter
传统 Cluster Autoscaler 按节点池配置缩放,粒度粗、慢(分钟级)、不会混搭机型。Karpenter 直接根据"待调度 Pod 的资源需求"创建最合适的节点,支持多种实例规格、Spot/按需混合、bin-packing,秒级扩缩、闲置节点自动回收——让"节点数"紧跟"真实需求",消除空转。
5.2 Karpenter 配置示例
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: general
spec:
disruption:
consolidationPolicy: WhenUnderutilized
template:
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand", "spot"]
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: general
配合 HPA:Pod 多 → 节点自动补 → 高峰过去 → 合并回收。
ℹ️ Karpenter 的省钱本质:它把"该有 3 台还是 7 台"从"人定"变成"按 Pod 需求动态算"。但前提是 Pod 的 requests 必须真实——Request 虚高,Karpenter 也只会多买节点。
6. 成本工具:OpenCost 与 Kubecost
6.1 OpenCost:开源成本引擎
OpenCost(CNCF 项目)安装:kubectl apply -f https://raw.githubusercontent.com/opencost/opencost/develop/kubernetes/opencost.yaml,然后 kubectl port-forward -n opencost svc/opencost 9090:9090 访问 Web UI,或调 API /model/allocation/compute?window=7d。它能告诉你每个命名空间/标签/Deployment 花了多少钱、按 Request 与实际用量两个口径、与云账单对账(接入 AWS/GCP/Azure 价格表)。
6.2 Kubecost:企业级可视与治理
Kubecost 在 OpenCost 之上加了:集群/命名空间/团队视图与 Drill-down、成本异常告警(某团队突增)、资源建议(结合 VPA 的降本预估)、多集群聚合与云对账、按团队隔离成本视图。起步建议:先 OpenCost(免费够用),需要多团队看板与告警再上 Kubecost。
6.3 与云账单对账
对账公式:云账单实例成本 - 分配给命名空间的成本 = 集群/系统/闲置成本。闲置成本是优化机会池:节点空转(利用率 < 30%)、系统命名空间占太多、大量没被分摊的存储/流量。目标:闲置成本占比 < 15%~20%,否则说明节点买多了/调度不均。
7. 降本优先级与 ROI
7.1 按投入产出排序
降本手段(先做投入小的):先画像(把虚高 Request 压到真实值,几乎零成本)、清理闲置(删没用 Deployment/PV,白捡)、节点弹性(CA/Karpenter 消除空转)、引入 Spot(批处理/无状态)、存储与流量治理(PV 缩容、压缩日志、减少跨区流量)、架构级(合并服务、降副本,收益大但投入大)。顺序原则:先省浪费(画像+清理)→ 再省弹性(伸缩+Spot)→ 最后才碰架构。
7.2 计算 ROI 的例子
例:某命名空间 100 个 Deployment,平均 Request 虚高 3 倍,节点月成本 20 万。画像后 Request 压到 1/3(配合弹性),按落地 30% 估算月省 6 万,一周人力成本可忽略 → ROI 极高,优先做。反例:为了省 2000 元去重构服务拆分,投入一个月 → ROI 低,往后放。
ℹ️ 降本不是"砍配置":压 Request 必须配合监控(不降 SLO)与资源余量。一次压太狠导致 OOM/CPU throttling,得不偿失。
8. 成本指标与看板
8.1 核心成本指标
用四个指标把成本做成看板:集群成本/节点成本(每月总账单)、分摊成本(按命名空间/团队/标签汇总)、闲置成本占比(节点利用率低的浪费比例)、单位成本效率(如每百万请求成本、每卡时成本)。配套信号:节点平均利用率(目标 50%+)、容器 Request 利用率 vs 实际(目标 < 2 倍)、空转节点数。
8.2 看板示例
┌──────────────────────────────────────────────┐
│ FinOps 看板(Grafana) │
│ 本月集群成本 ¥182,000 vs 上月 ¥205,000 ▼ │
│ 闲置成本占比 12%(目标 < 15%) │
│ 按团队:电商 45% / 算法 30% / 平台 25% │
│ 容器利用率:CPU 62% / 内存 71% │
│ 空转节点:3 台(估计浪费 ¥9,000/月) │
└──────────────────────────────────────────────┘
告警建议:单团队成本环比 +20% 告警、节点利用率持续 < 30% 告警、Spot 回收率骤升告警。
9. 生产最佳实践
9.1 Checklist
□ 用 OpenCost/Kubecost 建立分摊,先知道"钱花在哪"
□ 全局做资源画像(VPA Off / KRR),把虚高 Request 压下来
□ 压 Request 时留余量,并用监控确认 SLO 不降
□ 节点池分层 + 弹性伸缩(Karpenter/Cluster Autoscaler)
□ 批处理与无状态负载换 Spot,关键服务保持按需
□ 清理闲置:删无用 Deployment/PV/命名空间
□ 设置成本告警(团队突增/空转/Spot 回收)
□ 每月做一次成本 Review,把成本指标接入团队会议
9.2 常见坑与对策
| 坑 | 现象 | 对策 |
|---|---|---|
| Request 虚高 | 节点买多、Pod 挤不上 | VPA/KRR 画像后压到真实值 |
| 压太狠 | OOM/CPU throttling | 留余量 + 监控 SLO + 分批压 |
| 只加节点不回收 | 空转烧钱 | 弹性伸缩 + 合并回收 |
| Spot 混跑关键服务 | 回收即事故 | Spot 只给可中断负载 |
| 成本不可分摊 | 团队不认账 | 按标签分摊 + 看板 |
小结
K8s 成本优化的本质是"让资源紧跟真实需求,让账单对应到每个团队":先用 OpenCost/Kubecost 把成本分摊清楚,再用 VPA/KRR 画像把虚高 Request 压到真实值,接着用弹性伸缩(Karpenter)与 Spot 把节点数对准实际需求,最后用看板与告警让成本持续可观测。降本的正确顺序永远是:先省浪费(画像+清理)→ 再省弹性(伸缩+Spot)→ 最后才碰架构。记住一句话:成本优化的收益来自"闲置的消除"和"精确的匹配",而不是把服务砍瘦——用数据说话,用指标验收。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。