打开网易新闻 查看精彩图片

Flexera 2023年云状态报告显示,成熟工程组织的非生产环境(non-prod)占总云支出的30%–40%。这个数字放在任何一家年云支出千万级的公司,都意味着每年有三到四百万美元在staging、dev、test环境里空转。但绝大多数团队对这些环境零可见性:没有成本追踪,没有闲置检测,没有问责机制。

问题不是缺工具。Prometheus和Grafana你早就有了。缺的是把成本数据接进工程师每天早上都会看的那个仪表盘。

本文展示如何构建一个Grafana仪表盘,同时展示环境健康状态(CPU、内存、Pod状态)和实时美元成本,按命名空间、按环境、按小时拆分。当工程师打开staging仪表盘,看到"这个集群今天花了89美元,利用率4%",他们会行动。

现状:三个窗口,零交集

现状:三个窗口,零交集

理解非生产环境的现状需要同时打开至少三个东西:云计费控制台看成本,Grafana看性能,kubectl或Lens看Pod状态。这些工具互不通信。

AWS Cost Explorer里的成本数据延迟24–48小时。Grafana展示实时利用率,但没有美元上下文。非生产命名空间在两者中都很少出现。

结果是没人知道非生产环境实时花了多少钱。闲置集群在夜间、周末、节假日持续运行。不是因为工程师不在乎,而是因为触发行动的反馈信号根本不存在。

我们要构建的架构把这三个窗口压缩成一个Grafana仪表盘,后端用Prometheus。

数据源:你已经有了,只是没连起来

数据源:你已经有了,只是没连起来

成本仪表盘运行在两个你可能已有的数据源上:kube-state-metrics和cAdvisor(随kubelet捆绑)。

kube-state-metrics暴露超过100个Kubernetes对象指标:Pod阶段、容器资源请求和限制、节点可分配容量。cAdvisor提供实时CPU和内存消耗。

两者都不直接暴露美元成本。这需要一条recording rule(记录规则),从第一性原理计算每命名空间的小时成本。

从kube-state-metrics需要的关键指标:

kube_pod_container_resource_requests{resource="cpu"} — 容器的CPU请求

kube_node_status_allocatable{resource="cpu"} — 节点的可分配CPU

kube_node_labels{label_node_kubernetes_io_instance_type} — 节点实例类型,用于映射到价格

cAdvisor提供:

container_cpu_usage_seconds_total — 实际CPU使用率,用于计算利用率百分比

container_memory_working_set_bytes — 实际内存使用

价格数据需要静态配置。AWS、GCP、Azure的按需价格不会实时变化,可以硬编码为ConfigMap或简单的JSON文件,由Prometheus通过file_sd_configs加载。

核心公式:容器成本 = 资源占比 × 节点价格

核心公式:容器成本 = 资源占比 × 节点价格

Recording rule是非谈判项。原始PromQL跨所有Pod和节点的成本计算很慢,仪表盘刷新需要2–4秒。Recording rule每5分钟预计算小时成本,查询时间降至50毫秒以下。

我们使用的成本分配模型源自OpenCost规范,适配按需定价。核心思想:容器成本等于其请求占节点资源的比例,乘以节点小时价格。

CPU公式:

container_hourly_cost_cpu = (cpu_request / node_allocatable_cpu) × node_hourly_price

内存同理,用内存单位。容器总成本取资源约束中的绑定项:CPU和内存分配比例的最大值。这防止多租户节点上的重复计算。

以AWS us-east-1区域运行t3.large实例的8节点staging集群为例:

t3.large按需价格:$0.0832/小时

节点可分配CPU:2 vCPU

节点可分配内存:8 GiB

集群小时成本:8 × $0.0832 = $0.6656

一个请求0.5 vCPU、2 GiB内存的容器:

CPU占比:0.5 / 2 = 25%

内存占比:2 / 8 = 25%

取最大值25%,小时成本:$0.6656 × 25% = $0.1664

Prometheus recording rule生成cost_per_namespace_hour指标:

预聚合在namespace层级,而非单个容器。工程师关心的是"staging-api命名空间花了多少钱",不是某个特定Pod。

仪表盘设计:一个屏幕,两种语言

仪表盘设计:一个屏幕,两种语言

仪表盘顶部是成本卡片:今日总成本、本周趋势、预算使用率(如果配置了软预算)。

中间是热力图:命名空间 × 小时,颜色深浅表示成本高低。一眼看出哪些命名空间在夜间持续烧钱。

底部是利用率散点图:横轴CPU利用率,纵轴内存利用率,气泡大小表示成本。右下角的肥大气泡 = 高成本低利用率 = 优化目标。

关键交互:点击任意命名空间,下钻到该环境的详细视图。显示该命名空间下的所有Deployment,每个的CPU请求vs实际使用、内存请求vs实际使用、小时成本。

这个下钻视图暴露了一个常见反模式:请求设置过高。某服务请求4 vCPU但实际使用0.3,Grafana会显示"请求成本$1.33/小时,实际使用成本$0.10/小时",浪费率93%。

工程师看到数字后会自己调整。不需要成本优化团队发邮件,不需要FinOps工具生成报告。反馈循环压缩到同一个屏幕内。

Recording Rule 实战:从慢查询到50毫秒

Recording Rule 实战:从慢查询到50毫秒

原始PromQL计算namespace成本看起来像这样:

sum by (namespace) (

打开网易新闻 查看精彩图片

kube_pod_container_resource_requests{resource="cpu"}

* on(node) group_left

(node_hourly_price / kube_node_status_allocatable{resource="cpu"})

这条查询在1000+ Pod的集群上执行需要2–4秒。每次仪表盘刷新都卡一下,工程师会关掉这个面板。

Recording rule改成每5分钟预计算:

groups:

- name: cost

interval: 5m

rules:

- record: namespace:container_cpu_cost_per_hour:sum

expr: |

sum by (namespace, node) (

kube_pod_container_resource_requests{resource="cpu"}

* on(node) group_left

(node_hourly_price / kube_node_status_allocatable{resource="cpu"})

仪表盘查询简化为:

sum by (namespace) (namespace:container_cpu_cost_per_hour:sum)

查询时间:50毫秒。工程师无感知刷新。

价格数据:静态配置,动态加载

价格数据:静态配置,动态加载

云厂商价格不会每小时变,但实例类型会新增。我们把AWS价格表做成ConfigMap:

apiVersion: v1

kind: ConfigMap

metadata:

name: cloud-pricing

data:

aws_us_east_1.json: |

"t3.micro": 0.0104,

"t3.small": 0.0208,

"t3.medium": 0.0416,

"t3.large": 0.0832

Prometheus通过file_sd_configs加载,配合relabel规则把实例类型映射到价格指标:

relabel_configs:

- source_labels: [__meta_kubernetes_node_label_node_kubernetes_io_instance_type]

target_label: instance_type

recording rule里用lookup把instance_type转成node_hourly_price。

价格更新频率?每月一次足够。AWS降价通知会提前30天发出,有充足时间更新ConfigMap。

利用率计算:请求 vs 实际

利用率计算:请求 vs 实际

成本只算请求(request)会低估,只算实际使用(usage)会掩盖配置问题。我们展示两者。

请求成本:基于resource_requests,代表"承诺支出",即集群必须预留的资源。

实际成本:基于cAdvisor的usage数据,按节点价格比例折算,代表"真实消耗"。

两者比值定义效率分数:

efficiency_score = actual_cost / request_cost

分数接近1 = 请求设置合理。分数低于0.3 = 严重过度配置,优化空间巨大。

打开网易新闻 查看精彩图片

这个分数直接显示在仪表盘上,按命名空间排序。工程师打开页面,看到自己负责的命名空间效率分数0.15,会主动排查。

多环境策略:staging、dev、test差异化

多环境策略:staging、dev、test差异化

不同非生产环境有不同的成本容忍度和优化策略。

Staging:需要24/7运行,但利用率可以低。重点监控"是否有人在用",通过活跃Deployment数量判断。零活跃Deployment连续4小时 = 自动缩容候选。

Dev:个人开发环境,下班后应该归零。设置硬预算:单个命名空间日成本上限$20,超限时告警并建议销毁。

Test:CI/CD触发,按需启动。重点监控"启动到就绪"时间,以及测试完成后是否及时清理。遗留Pod是主要成本泄漏点。

仪表盘用变量切换环境,每个环境有独立的recording rule集合和告警阈值。

告警设计:从被动响应到主动干预

告警设计:从被动响应到主动干预

成本告警容易变成噪音。我们设置三层:

信息层:日成本超过周均值150%,发送到Slack成本频道。不打扰,只记录。

警告层:单个命名空间连续6小时零利用率但持续产生成本,@channel提及。暗示配置错误或遗忘的环境。

行动层:dev环境日成本超$50且效率分数低于0.2,自动创建Jira工单并分配命名空间所有者。工单包含Grafana链接和优化建议。

关键:告警里嵌入仪表盘深链。工程师点击直接跳到问题命名空间,不需要手动导航。

实施路径:从0到1的四周计划

实施路径:从0到1的四周计划

第一周:部署kube-state-metrics和cAdvisor(如果还没有),验证指标采集。

第二周:构建价格ConfigMap,编写核心recording rule,验证成本计算与AWS账单误差在5%以内。

第三周:搭建基础仪表盘,邀请2-3个团队试用,收集反馈。

第四周:全量推广,配置告警,建立命名空间所有权映射(成本归因需要知道谁负责)。

常见陷阱:

Spot实例价格波动的处理。如果集群混用按需和Spot,需要分别计算。Spot价格变化快,建议用AWS的Spot价格API每小时更新,或保守估计按按需价格的30%折算。

预留实例(RI)和节省计划(Savings Plans)的摊销。如果组织购买了RI,实际成本低于按需价格。需要与财务团队对齐:仪表盘展示"摊销成本"还是"按需等价成本"?前者反映真实支出,后者反映优化机会。

共享节点的成本归属。系统Pod(kube-system、monitoring)的成本按什么比例分摊?建议单独标记为"平台成本",不归因到业务命名空间。

效果验证:数字说话

效果验证:数字说话

某中型SaaS公司实施此方案后的数据:

实施前:非生产环境占云支出38%,利用率中位数12%,成本可见性延迟48小时。

实施后6个月:非生产环境占比降至22%,利用率中位数提升至31%,实时成本可见性。

关键行为改变:工程师开始在下班前检查仪表盘,主动销毁闲置环境。周五下午的成本曲线从平坦(持续运行)变成阶梯状(批量清理)。

一个意外发现:测试环境的"启动-测试-清理"周期中,清理阶段的失败率被长期忽视。成本仪表盘暴露了遗留Pod的累积效应,推动CI/CD流水线修复了清理逻辑。

扩展:从成本到碳排

扩展:从成本到碳排

云厂商开始提供碳排放数据。AWS在2023年底推出Customer Carbon Footprint Tool,延迟同样24–48小时。

同样的架构可以扩展:把碳排放因子(kg CO2e / kWh)加入价格ConfigMap,recording rule计算namespace_hourly_carbon。

仪表盘增加"碳成本"面板。对25-40岁的工程师群体,这个数字有时比美元更有驱动力。

实施复杂度:碳排放数据的地域差异比价格更大。us-east-1和eu-west-1的电网清洁度不同,需要按区域配置。

替代方案对比:为什么不直接用OpenCost/Kubecost

替代方案对比:为什么不直接用OpenCost/Kubecost

OpenCost是开源成本监控工具,Kubecost是其商业版。两者都提供namespace级成本拆分,为什么不直接用?

OpenCost需要单独部署,增加组件运维负担。它的数据模型与Prometheus不完全兼容,导出到Grafana需要额外适配。

更重要的是:工程师的屏幕时间有限。多一个工具 = 多一个标签页 = 更低的查看频率。把成本数据塞进现有的Grafana仪表盘,利用的是已经建立的习惯,而非创造新习惯。

Kubecost的商业功能(预算告警、优化建议)确实更完善。但基础版本的功能——namespace成本拆分、利用率关联——完全可以用原生Prometheus + Grafana实现,零额外许可成本。

选择取决于组织成熟度。已经有强FinOps团队、预算充足的公司,Kubecost的ROI明确。工程文化偏向"自己造"、对 vendor lock-in 敏感的组织,本文方案更可持续。

维护成本:不是一次性项目

维护成本:不是一次性项目

价格ConfigMap需要月度更新。新实例类型上线时,未配置价格的节点会显示为"unknown",需要人工介入。

Recording rule的评估性能需要监控。集群规模增长10倍后,5分钟评估间隔可能不够,需要调整到2分钟或增加Prometheus资源。

命名空间所有权映射会腐烂。工程师转岗、项目重组后,成本归因到错误的人,告警会被忽略。建议季度审计,与IAM权限审查同步进行。

仪表盘本身的迭代:工程师会提出新需求——按Deployment拆分、按团队聚合、与CI/CD构建号关联。预留20%时间持续优化。

一个未被讨论的问题

一个未被讨论的问题

成本可见性提升后,有些工程师会过度优化。把请求压到极限,节省几美元,但增加了生产环境OOM(内存溢出)的风险。

需要在团队层面建立规范:非生产环境的请求可以激进,但生产环境保留安全边际。成本仪表盘上标记环境类型,避免误读。

另一个边界:个人开发环境的隐私。成本数据可能暴露某工程师"这周只写了100行代码但环境跑了50小时"。数据访问权限需要设计:工程师看自己的命名空间,经理看团队汇总,财务看全局。

某公司在实施后发现,dev环境的成本分布高度不均:20%的命名空间占80%成本。深入调查后发现,部分工程师把dev环境当"个人服务器"跑长期任务。这不是技术问题,是政策问题。成本仪表盘暴露了现象,解决方案需要HR介入。