Kubernetes 多团队资源计量与成本分摊实战
验证:未记录
- 适用版本
- 未记录
- 验证范围
- 尚未登记验证记录。
本文缩写(英文全称与中文含义)
| 缩写 | 英文全称/名称 | 中文含义 |
|---|---|---|
| API | Application Programming Interface | 应用程序编程接口 |
| CPU | Central Processing Unit | 中央处理器 |
| CR | Custom Resource | Kubernetes 自定义资源对象 |
| CRD | Custom Resource Definition | Kubernetes 自定义资源定义 |
| CSV | Comma-Separated Values | 逗号分隔值格式 |
| FinOps | FinOps | 结合工程、财务与业务进行云成本管理的实践名称 |
| GiB | Gibibyte | 二进制容量单位,1 GiB = 2³⁰ 字节 |
| GPU | Graphics Processing Unit | 图形处理器 |
| HA | High Availability | 高可用 |
| HTTP | Hypertext Transfer Protocol | 超文本传输协议 |
| ID | Identifier | 标识符 |
| IOPS | Input/Output Operations Per Second | 每秒输入输出操作数 |
| JSON | JavaScript Object Notation | 一种结构化数据格式 |
| LB | Load Balancer | 负载均衡器 |
| NAT | Network Address Translation | 网络地址转换 |
| PV | Persistent Volume | 持久卷 |
| PVC | Persistent Volume Claim | 持久卷申领 |
| RBAC | Role-Based Access Control | 基于角色的访问控制 |
| RDS | Relational Database Service | AWS 关系型数据库服务 |
| S3 | Simple Storage Service | AWS 对象存储服务 |
| SKU | Stock Keeping Unit | 库存单位;云计费中指具体计费项 |
| UI | User Interface | 用户界面 |
| UTC | Coordinated Universal Time | 协调世界时 |
目标与落地边界#
多个团队共用一个 Kubernetes 集群时,需要回答三个问题:资源属于谁、占用了多少、应承担多少钱。推荐先做 Showback(展示费用),经过完整账期验证后再做 Chargeback(内部结算)。
本文提供一个单集群试点方案:Namespace 归属 + Prometheus 指标 + OpenCost 成本分配 + 月度对账。命令示例用于测试集群;已有监控系统的集群应复用现有组件。本文提供配置和验收步骤,不代表已在你的集群部署验证。
| 数据 | 作用 | 不能替代什么 |
|---|---|---|
| Requests / Limits / Quota | 申请量、运行上限、团队额度 | 不等于实际使用量或云账单 |
| CPU、内存历史指标 | 核时、GiB 时、利用率 | 不直接给出折扣后的真实费用 |
| OpenCost Allocation | 将成本模型分配到工作负载 | 不能默认视为财务最终账单 |
| 云成本与用量报告 | 实际价格、折扣、账单调整 | 通常不能单独解释每个 Pod 的份额 |
1. 先固定计费规则#
试点建议采用以下规则,写入平台与团队共同认可的成本说明:
- 归属:一个 Namespace 只属于一个团队,一个团队可以有多个 Namespace。
- 展示:同时展示 Requests、实际 Usage、OpenCost 分配成本和最终结算成本。
- 计算:以节点/资源池和时间窗口分配 CPU、内存成本,不把不同价格的节点混成一个固定单价。
- 公共组件:平台维护明确清单;先单列,再按团队直接成本比例分摊。
- 闲置容量:试点阶段由平台单列承担,专属节点池的闲置可按约定归对应团队。
- 无归属资源:进入
unallocated桶,不能丢弃,也不能默认为零费用。 - 账期:本例按 UTC 自然日出日报,月账单采用明确的起止时间;展示人民币时另记录汇率和日期。
- 数据缺口:标记为不完整,补数或人工确认后才能结算。
公共成本可以固定比例分摊、按代理指标分摊,或由平台统一承担;没有唯一通用公式,关键是规则可解释且保持一致。参考 FinOps 成本分配指南。
2. 团队资源归属与额度#
2.1 Namespace 与标签#
shared-prod
├── payment-prod team=payment
├── payment-dev team=payment
├── order-prod team=order
├── monitoring 平台公共成本
├── opencost 平台公共成本
└── kube-system 平台公共成本保存为 payment-namespace.yaml:
apiVersion: v1
kind: Namespace
metadata:
name: payment-prod
labels:
team: payment
cost-center: payment
environment: prodkubectl apply -f payment-namespace.yaml
kubectl get namespaces -L team,cost-center,environment平台维护 Namespace → team → cost-center 的权威映射;部署模板也添加 team、app 和 environment 标签。Namespace 标签不会自动继承到 Pod,不能假设给 Namespace 打标签后,所有按 Pod label 聚合的工具就自动识别团队。
本方案先按 Namespace 导出费用,再按权威映射聚合团队。映射需保存生效起止时间,Namespace 转交团队时不能用今天的归属重写历史账单。
通过 RBAC 限制团队只能操作自己的 Namespace,由平台维护归属标签、Quota 和 LimitRange。可用 Kyverno 校验标签和资源声明,进一步验证 Pod 的团队标签与 Namespace 一致;不能只校验“标签非空”。Namespace 本身也不构成完整安全隔离,仍需 NetworkPolicy 等措施。参考 Kubernetes 多租户。
2.2 Quota 与 LimitRange#
保存为 payment-budget.yaml:
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-budget
namespace: payment-prod
spec:
hard:
requests.cpu: "8"
requests.memory: 16Gi
limits.cpu: "16"
limits.memory: 32Gi
requests.storage: 100Gi
persistentvolumeclaims: "10"
---
apiVersion: v1
kind: LimitRange
metadata:
name: container-defaults
namespace: payment-prod
spec:
limits:
- type: Container
defaultRequest:
cpu: 100m
memory: 128Mi
default:
cpu: "1"
memory: 512Mikubectl apply -f payment-budget.yaml
kubectl -n payment-prod describe resourcequota team-budget这些数值是演示额度,应按业务调整。Quota 中的 8 核是团队申请量上限,不代表已经为团队预留 8 核,也不应直接收取 8 × 整月小时数 的费用。参考 ResourceQuota。
3. “2 核 × 10 小时”中的时间怎么算#
3.1 Requests:申请量在时间上的累计#
假设一个普通单容器 Pod 从 08:00 到 18:00 都在运行,CPU request 一直为 2 核:
申请累计量 = 2 核 × 10 小时 = 20 核时单位是“核时”,不是“核”。监控系统周期性采样 requests,然后按时间积分,不是事后只看当前 Pod 的年龄。若中间扩容:
| 时段 | Pod 数量 | 每个 Pod 的 CPU request | 小计 |
|---|---|---|---|
| 08:00–12:00 | 1 | 2 核 | 8 核时 |
| 12:00–18:00 | 3 | 2 核 | 36 核时 |
| 总计 | 44 核时 |
通用公式:申请核时 = Σ(每段申请核数 × 该段秒数 / 3600)。
必须明确 Pod 状态口径:未绑定节点的 Pending Pod 不消耗节点分配容量;已经绑定节点但拉取镜像失败、初始化或终止中的 Pod,需要按工具的实际模型单独验证。不能把“创建到删除”的墙钟时间直接当成 OpenCost 的计费时间。OpenCost 对 Pod 状态的覆盖存在明确边界,详见 OpenCost 规范。
3.2 Usage:CPU 真正执行的累计时间#
container_cpu_usage_seconds_total 是累计 CPU 秒数。假设同一组容器这 10 小时内累计消耗 18,000 CPU 秒:
实际消耗 = 18,000 / 3,600 = 5 核时
平均使用 = 5 核时 / 10 小时 = 0.5 核
相对申请量的平均利用率 = 5 / 20 = 25%CPU 秒跨核心累计:同时占满 2 核运行 1 秒,约产生 2 CPU 秒。Pod 存活 10 小时,不等于满负荷运行了 10 小时。
| 量 | 本例结果 |
|---|---|
| 运行持续时间 | 10 小时 |
| Requests 累计量 | 20 核时 |
| 实际 CPU 消耗 | 5 核时 |
| 相对 Requests 利用率 | 25% |
容器重启后计数器可能重置,应该对每条原始计数器使用 increase / rate,再做聚合。不要用最后值减最早值,也不要先加总不同容器的计数器再计算增长率。参考 Prometheus 查询函数。
3.3 内存、存储与网络#
- 内存用量通常选 working set,按 GiB 对时间积分,得到 GiB 时;它不是 CPU 那样的累计计数器,不能套用
increase。 - 内存 request 同样积分成 GiB 时;working set 也不等于业务堆内存。
- PVC 通常按供应容量计费,不能只统计文件系统已用字节;额外 IOPS、吞吐、快照应按账单纳入。
- 网络费用依赖流量方向、可用区、地域、NAT 和负载均衡服务,不能把容器收发总字节统一乘公网出流量价格。
4. 部署 Prometheus 与 OpenCost#
4.1 前置条件与版本#
需要 Helm、kubectl、一个可安装 CRD/RBAC 的测试集群;日报导出还需要 curl、jq。先核对当前上下文:
kubectl config current-context
kubectl get nodes
helm version
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo add opencost-charts https://opencost.github.io/opencost-helm-chart
helm repo update
helm search repo prometheus-community/kube-prometheus-stack --versions
helm search repo opencost-charts/opencost --versions
# 替换成经过兼容性核对的 Chart 版本,保存到部署记录。
export MONITORING_CHART_VERSION='<监控 Chart 版本>'
export OPENCOST_CHART_VERSION='<OpenCost Chart 版本>'
helm show values prometheus-community/kube-prometheus-stack --version "$MONITORING_CHART_VERSION" > monitoring-default-values.yaml
helm show values opencost-charts/opencost --version "$OPENCOST_CHART_VERSION" > opencost-default-values.yaml以下监控安装针对空白测试环境。已有 kube-prometheus-stack 的集群,应把所需选项合并进现有完整 values,再走原有发布流程,避免创建第二套重复抓取的系统。
4.2 监控配置#
保存为 monitoring-values.yaml:
kube-state-metrics:
metricLabelsAllowlist:
- namespaces=[team,cost-center,environment]
- pods=[team,app,environment]
prometheus:
prometheusSpec:
scrapeInterval: 30s
evaluationInterval: 1m
retention: 45dhelm upgrade --install monitoring prometheus-community/kube-prometheus-stack -n monitoring --create-namespace --version "$MONITORING_CHART_VERSION" -f monitoring-values.yaml --wait --timeout 10m
kubectl -n monitoring get prometheus,pods,services测试配置没有设置持久化,不能承担生产账单留存。生产需在完整 values 中配置 prometheus.prometheusSpec.storageSpec 的 PVC、StorageClass、容量和备份,或接入经过验证的长期存储;保留期应覆盖完整账期及重算窗口,并监控磁盘使用量。安装前的数据不会自动补齐。
kube-prometheus-stack 通常部署 kube-state-metrics,并配置 kubelet/cAdvisor 抓取。实际能否采到指标必须通过 Targets 验证,不能以 Helm 安装成功代替数据验收。配置依据:官方 Chart values。
4.3 OpenCost 配置#
保存为 opencost-values.yaml。先用上一步 kubectl get services 确认 Prometheus Service 名称,按实际修改:
opencost:
exporter:
defaultClusterId: shared-prod
prometheus:
internal:
enabled: true
serviceName: monitoring-kube-prometheus-prometheus
namespaceName: monitoring
port: 9090
external:
enabled: false
metrics:
serviceMonitor:
enabled: true
additionalLabels:
release: monitoring
scrapeInterval: 30s
ui:
enabled: truehelm upgrade --install opencost opencost-charts/opencost -n opencost --create-namespace --version "$OPENCOST_CHART_VERSION" -f opencost-values.yaml --wait --timeout 5m
kubectl -n opencost get deployment,pods,servicemonitor
kubectl -n opencost logs deployment/opencost --all-containers=true --tail=100
kubectl -n opencost port-forward deployment/opencost 9003:9003 9090:9090保持转发终端运行,浏览器打开 http://localhost:9090。API 地址为 http://localhost:9003。生产对团队开放 UI/API 前应补充认证授权,避免暴露其他团队的成本数据。
release: monitoring 用于匹配本例 Prometheus 的 ServiceMonitor 选择器,不是 OpenCost 固定要求。已有安装应检查 Prometheus CR 的 serviceMonitorSelector 和 serviceMonitorNamespaceSelector,确保能发现 opencost 命名空间中的对象。参考 OpenCost Helm 安装 和 Chart values。
4.4 数据与价格验收#
在 Prometheus 页面检查 Targets,并执行以下 PromQL,确认有数据、时间新鲜且没有重复抓取:
count(container_cpu_usage_seconds_total{container!="",container!="POD",namespace!=""})
count(container_memory_working_set_bytes{container!="",container!="POD",namespace!=""})
count(kube_pod_container_resource_requests)
count(kube_namespace_labels{label_team!=""})
count(node_cpu_hourly_cost)
count(node_ram_hourly_cost)多行应分别执行。数量为零或无结果时,先修复采集。还应抽查 node 单价和节点 SKU/地域/Spot 类型;“有价格”不代表“价格正确”。检查 OpenCost 导出指标与已有 kube-state-metrics 是否重叠,对重复指标按 job 过滤或在采集层去重。参考 OpenCost 与 Prometheus 集成。
本例未配置云账单凭据,因此不能宣称完成了折扣、承诺用量或实际账单对接。云环境需按官方提供商指南配置成本报告及最小只读权限;自建集群需配置自定义价格,将采购摊销等成本换成一致的时间单价。参考 OpenCost 安装与提供商配置入口。
5. 用 PromQL 验证资源计量#
以下查询用于单集群核对与趋势分析。前提是每个原始容器仅抓取一次;多集群系统需增加 cluster 过滤和聚合维度,HA 副本需在查询层去重。job/metrics_path 标签因安装方式不同,应先在 Prometheus 中核对。
5.1 过去 24 小时的实际 CPU 核时#
sum by (namespace) (
increase(container_cpu_usage_seconds_total{
namespace!="", container!="", container!="POD",
metrics_path="/metrics/cadvisor"
}[24h])
) / 3600在确定的日终时间执行 Instant Query,而非取当前瞬间 CPU 使用率乘 24。范围向量可包含窗口内已删除容器的历史样本;不要再与“当前仍存在的 Pod 清单”关联,否则会漏掉已结束的任务。
increase 会处理可见的计数器重置,并对区间边界做外推,因此结果可能是小数。它不能恢复首次抓取之前、最后一次抓取之后或完全没抓到的使用量;只留下一个样本的短容器无法可靠计算增量。该查询是监控计量,不是毫秒级财务计量器。
5.2 Requests 与内存的 1 分钟记录规则#
保存为 cost-recording-rules.yaml。本例只统计 Running、已绑定节点的普通容器 request;不覆盖 init container 最大值、Pod overhead 或 Pod 级资源声明等完整调度语义,不能替代 OpenCost 分配引擎。
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: namespace-cost-metering
namespace: monitoring
labels:
release: monitoring
spec:
groups:
- name: namespace-cost-metering
interval: 1m
rules:
- record: namespace:cpu_requests_running:cores
expr: |
sum by (namespace) (
max by (namespace, pod, uid, container) (
kube_pod_container_resource_requests{resource="cpu",unit="core",node!=""}
)
and on (namespace, pod, uid)
(max by (namespace, pod, uid) (kube_pod_status_phase{phase="Running"}) == 1)
)
- record: namespace:memory_working_set:gib
expr: |
sum by (namespace) (
container_memory_working_set_bytes{
namespace!="",container!="",container!="POD",
metrics_path="/metrics/cadvisor"
}
) / 1073741824kubectl apply -f cost-recording-rules.yaml
kubectl -n monitoring get prometheusrule namespace-cost-metering还需在 Prometheus 的 Rules 页面确认规则已加载、健康且按分钟产出;只有 CR 创建成功并不代表被选择器选中。本例依赖 kube-state-metrics 的 uid 等标签,升级后应核对实际指标模式。
在记录规则已经覆盖完整一天后,分别查询:
sum_over_time(namespace:cpu_requests_running:cores[24h]) / 60sum_over_time(namespace:memory_working_set:gib[24h]) / 60结果分别是申请核时和实际内存 GiB 时。除以 60 的原因:每个样本代表约 1 分钟,即 1/60 小时;如果把规则间隔改成 30 秒,就应除以 120。
这是一分钟矩形积分近似,会受到抓取间隔、陈旧样本、规则停顿、Pod 状态切换影响。Pod 不运行时序列可能不存在,不能仅凭该 Namespace 样本数不足 1440 就判定采集故障;应结合 Prometheus/目标健康、抓取缺口和规则执行失败判断。不能用 avg_over_time × 24 无条件替代积分,否则仅运行 1 小时的 Pod 可能被扩大成 24 小时。
生产对短于采样间隔的 Job,如有严格计费要求,应补充工作负载生命周期记录或更细粒度计量,并明确最低计费粒度。普通分钟采样无法保证完整捕获这类任务。
6. 导出 OpenCost 日报#
OpenCost 的 cpuCoreHours 是其分配模型中的资源量,不能直接当成上一节 CPU counter 算出的实际核时。OpenCost 的资源分配原则包含 request 与 usage 的比较,具体数值受窗口、分辨率、状态和实现版本影响;不能用“整月 request 总量与 usage 总量取最大值”自行替代。参考 Allocation API。
先做滚动窗口检查:
curl --fail --silent --show-error --get http://localhost:9003/allocation --data-urlencode 'window=24h' --data-urlencode 'aggregate=namespace' --data-urlencode 'resolution=1m' --data-urlencode 'includeIdle=true' --data-urlencode 'shareIdle=false' --data-urlencode 'idleByNode=true' > allocation-preview.json
jq -e '.code == 200 and (.data | type == "array")' allocation-preview.json正式日报必须使用固定窗口;把下面日期替换成已完整采集的 UTC 日期。香港时间一天的 00:00 边界对应前一天 16:00Z,若按本地自然日结算,应转换边界并全程保持一致。
export COST_WINDOW_START='2026-09-20T00:00:00Z'
export COST_WINDOW_END='2026-09-21T00:00:00Z'
curl --fail --silent --show-error --get http://localhost:9003/allocation --data-urlencode "window=${COST_WINDOW_START},${COST_WINDOW_END}" --data-urlencode 'step=24h' --data-urlencode 'resolution=1m' --data-urlencode 'aggregate=namespace' --data-urlencode 'includeIdle=true' --data-urlencode 'shareIdle=false' --data-urlencode 'idleByNode=true' > allocation-day.json
jq -e '.code == 200 and (.data | type == "array") and (.data | length > 0)' allocation-day.json
jq -r '
["allocation","cpuCost","ramCost","pvCost","networkCost","totalCost"],
(.data[] | to_entries[] |
[.key,.value.cpuCost,.value.ramCost,.value.pvCost,
.value.networkCost,.value.totalCost]) | @csv
' allocation-day.json > allocation-day.csv保留原始 JSON;CSV 是便于核对的摘要,totalCost 可能还含 GPU、负载均衡、共享费用或调整项,不能只将展示的四列相加。缺失字段输出为空,需要调查,不能静默补成零。HTTP/JSON 成功及非空也不能证明窗口完整,仍需采集覆盖率检查。
把 __idle__ 等工具特殊项归到单独账目,普通 Namespace 按历史归属表聚合,公共 Namespace 按清单归平台费用。没有映射的 Namespace 必须进入未归属报表。参数示例依据 OpenCost API 示例。
日常可以用调度任务调用集群内 API 导出,不依赖人工 port-forward;导出任务保留日期、cluster ID、Chart/App 版本、价格口径、归属表版本、查询参数和数据质量状态,写入有版本控制/备份的存储。按 cluster + 日期 + 规则版本幂等覆盖或保存修订版,不重复追加费用。
7. 从模型成本到真实账单#
7.1 节点成本不能重复计算#
云厂商通常对一台节点整体收费。CPU 单价和内存单价是对同一节点成本的拆分,不能各自承担 100% 节点费用。需要满足同一窗口内的核算约束:
节点成本 ≈ CPU 分配成本 + 内存分配成本 + GPU 等适用成本
节点成本分配结果 = 团队份额 + 公共工作负载份额 + 闲置/系统开销 + 差异云账单对账应按节点 ID/资源池、实例类型、地域、购买方式及时间窗口匹配。Spot 与按需价格、承诺用量折扣及预付摊销必须按同一财务口径处理,不能让某个团队承担另一种节点池的价格。
如果目前只有模型价格,可先用其作为分配权重:在同一可比费用池内,某项结算额 = 该项模型份额 / 全部模型份额 × 该费用池实际金额。分母必须包含公共和闲置项;分母为零或数据不完整时应进入差异处理,不能强行分完。该方法是内部约定的归一化方案,不是 OpenCost API 自动保证的行为。
7.2 明确费用范围与避免重复#
| 费用 | 归属方法 | 常见错误 |
|---|---|---|
| 节点计算 | OpenCost 分配模型结合节点实际账单 | 把完整节点账单与分配结果再次相加 |
| PVC/云盘 | PVC → PV → 云资源 ID → 团队 | 删除 Pod 后就停止算保留磁盘费用 |
| Ingress/LB/NAT | 独享直接归属,共享按约定指标 | 用全部容器网络字节替代计费流量 |
| 控制平面/监控/日志 | 公共成本清单 | 将公共 Pod 同时计入团队直接成本和共享池 |
| RDS/S3 等外部资源 | 云资源标签和独立账单映射 | 误以为 OpenCost 自动覆盖全部云资源 |
| 未分配磁盘/闲置节点 | 单列并指定责任人 | 只展示有 Pod 的资源导致漏账 |
7.3 一个完整月度示例#
假设已经完成同一币种、同一账期的真实账单对齐:
| 成本桶 | 金额 |
|---|---|
| 支付团队直接成本 | ¥4,000 |
| 订单团队直接成本 | ¥2,000 |
| 公共平台成本 | ¥1,000 |
| 闲置成本 | ¥2,000 |
| 总计 | ¥9,000 |
按直接成本比例分摊公共成本,闲置由平台承担:
支付团队 = 4,000 + 1,000 × 4,000 / 6,000 = ¥4,666.67
订单团队 = 2,000 + 1,000 × 2,000 / 6,000 = ¥2,333.33
平台 = ¥2,000
合计 = ¥9,000若所有团队直接成本都是零,不能除以零,应按预先约定由平台承担或固定权重分配。金额计算使用 Decimal 等十进制类型,并指定最后一分钱的舍入归属。若已启用工具的闲置分摊,后处理不能再次分摊同一笔闲置。
月度闭环必须能解释:真实账单 = 团队结算 + 平台承担 + 未归属 + 已说明的对账差异。税费、退款、账单延迟和汇率差异要有单独规则,而不是通过篡改资源核时凑平。
8. 验收实验与看板#
8.1 可控工作负载#
保存为 cost-demo.yaml,演示容器只 sleep,用于观察低使用、高申请的差异:
apiVersion: apps/v1
kind: Deployment
metadata:
name: cost-demo
namespace: payment-prod
spec:
replicas: 1
selector:
matchLabels:
app: cost-demo
template:
metadata:
labels:
app: cost-demo
team: payment
environment: prod
spec:
containers:
- name: sleeper
image: busybox:1.37.0
command: ["sh", "-c", "sleep 86400"]
resources:
requests:
cpu: "2"
memory: 256Mi
limits:
cpu: "2"
memory: 512Mikubectl apply -f cost-demo.yaml
kubectl -n payment-prod rollout status deployment/cost-demo --timeout=120s
kubectl -n payment-prod get pods -l app=cost-demo -o wide记录真正开始 Running 的时间,保持 30 分钟,然后删除:
kubectl -n payment-prod delete deployment cost-demo该 Pod 在稳定运行的 30 分钟内,CPU request 约贡献 2 × 0.5 = 1 核时,内存 request 约贡献 0.25 × 0.5 = 0.125 GiB 时;实际 CPU 消耗应很低。指标积分存在边界误差,检查对应 Pod 或测试前后 Namespace 增量,不应将 Namespace 中其他工作负载算进实验。
8.2 上线前验收表#
| 实验 | 验收点 |
|---|---|
| 固定 requests 运行 30 分钟 | 申请核时接近理论值,偏差能由采样边界解释 |
| 1 副本扩成 3 副本 | request 曲线约变为原来 3 倍,核时按分段累计 |
| 容器重启 | counter 重置后没有负费用或巨大尖峰 |
| 删除 Pod 后查询历史 | 已结束工作负载仍在历史日报中 |
| 短 Job | 明确是否漏采,不能宣称分钟采样完全覆盖 |
| 不带归属的 Namespace | 出现在未归属项中,不被丢弃 |
| 停止抓取 | 日报质量标记为不完整,而不是费用突然归零 |
| 同一日报重复导出 | 固定窗口结果可核对,后处理不会重复入账 |
| 月末对账 | 同范围同币种金额闭环,剩余差异有解释 |
试点可以先约定长时稳定负载积分误差目标,例如不超过 5%,这只是内部验收目标,不是工具保证;短任务需另设口径。
Grafana 建议展示:团队/月累计费用、日趋势、CPU request 核时与实际核时、内存 request 与 working set、公共成本、闲置成本、未归属成本、预算消耗、采集缺口。团队管理员看自己的明细,平台看跨团队全貌;日报输出不能替代访问隔离。
9. 常见问题与推广顺序#
| 问题 | 排查方向 |
|---|---|
| OpenCost 没数据 | Prometheus 地址、ServiceMonitor 选择器、网络和必需指标 |
| 成本为零或异常低 | 节点价格缺失、采集时间太短、状态未覆盖、短任务漏采 |
| 核时翻倍 | cAdvisor 重复抓取、HA 副本重复计算、多集群未区分 |
| namespace 有费用但没有 team | 没有归属映射,或误认为标签会自动继承 |
| PromQL 与 OpenCost 不一致 | request/usage/allocation 口径、状态、分辨率、窗口不一致 |
| 云账单明显大于 OpenCost | 折扣口径、外部服务、控制平面、网络、保留磁盘及遗漏节点 |
| 历史账单无法重算 | 指标已过期、未持久化、归属表或价格规则没有版本 |
推荐按四步推广:先完成 Namespace 归属与采集验收;再出团队资源日报;接着加入价格、公共和闲置成本并完成一个完整账期对账;最后由财务与团队共同确认后启用内部结算。财务确定价格和共享规则,平台负责数据完整性,团队负责 requests 合理性和归属准确性。
停用试点时,只删除本次创建的测试 Deployment 和记录规则;共享监控、业务 Namespace 及其 PVC 不应作为测试清理目标。原始日报与结算依据按约定留存。