成本证据模型与工具选型:先回答问题,再决定平台
一次架构评审里,IaC 机器人预测数据库扩容后每月增加 30%,Kubernetes 成本页面显示该服务只占集群成本的 12%,云账单却在下个账期上涨 18%,产品报表又说单订单成本下降了。四个数字都可能正确:它们分别描述变更前估算、工作负载分摊、供应商收费和业务产出,却被会议当成同一个“成本”互相否定。
再成熟的仪表盘也无法自动消除这种冲突。团队先要说明正在回答什么问题、证据来自哪个时间窗口、金额采用哪种口径、结果能否回到原始费用和业务事件,然后才有资格讨论工具。否则,选型会退化成页面数量、告警数量和产品演示的比较。
同一个金额可能属于五种证据
成本系统最常见的错误不是算错,而是把不同确定性的数字放进同一列。至少要区分五个层级:
| 证据层 | 典型对象 | 能回答什么 | 不能证明什么 |
|---|---|---|---|
| Estimate | IaC 规格、目录价格、usage 假设 | 变更方向和主要成本驱动 | 上线后的实际发票 |
| Allocation | workload、request、usage、分摊规则 | 技术消耗应归给谁 | 供应商最终收了多少钱 |
| Asset | 实例、磁盘、IP、LB、承诺资源 | 哪些资产承载费用、是否闲置 | 业务是否获得价值 |
| Invoice | 账单行、折扣、抵扣、税费、修订 | 供应商交付的收费事实 | 内部责任是否公平 |
| Unit economics | 订单、租户、请求、毛利、SLO | 每单位价值消耗多少技术成本 | 单个资源应如何配置 |
这五层不是成熟度排序。Estimate 出现在变更发生前,Invoice 往往迟到,Allocation 需要运行时和组织映射,Unit economics 还要等待业务计量。一个决策通常要拼接多层证据,但不能把它们压成一个没有来源的 cost 字段。
decision evidence =
question + scope + time window + cost basis
+ source revision + identity snapshot
+ calculation policy + owner + confidencecost basis 至少标明 List、Contracted、Billed 或 Effective。FOCUS 规范把 BilledCost 与 EffectiveCost 分开:前者接近发票确认,后者适合把承诺覆盖成本摊回消费期。二者服务不同决策,不能相加生成“总成本”。FOCUS 1.4 规范还要求消费方理解修订、交付和数据完整性语义;规范版本也不能替代供应商实际交付版本。
先写决策问题,再连接证据
“想降本”不能形成工具需求。架构团队应把问题写成可证伪句子:
这个 PR 是否增加了数据库规格或新增长期资产?某 namespace 的增长来自业务流量、过量 request,还是共享和空闲政策变化?账单上涨来自用量、价格、折扣覆盖、税费,还是迟到修订?
一次 rightsizing 是否在不破坏 SLO 的情况下兑现了净节省?每订单成本下降,是技术效率提升,还是高毛利业务占比变化?
每个问题都要绑定决策时点和允许动作。PR 阶段可以阻止合并或要求豁免;运行阶段可以生成候选项,但不能未经工作负载 owner 批准直接缩容;发票阶段可以重算和对账,却不能改写已经关账的结果。
decisionId: db-scale-review
question: "规格变化是否值得进入灰度"
scope: "service:<service-id>"
timeWindow: "<baseline-window>"
requiredEvidence:
- type: estimate
freshness: "same-revision"
- type: allocation
freshness: "<approved-lag>"
- type: slo
freshness: "<approved-lag>"
actions:
pass: canary
fail: redesign
unknown: manual-review
owner: "<workload-owner>"
expiresAt: "<decision-expiry>"unknown 必须是有效状态。数据迟到、身份缺失或口径不兼容时,系统应阻止自动结论,而不是用 0、上期值或平均值填补后继续决策。
工具不是一条从免费到企业版的直线
不同工具控制不同证据面。Infracost 把 IaC 变化、价格和 usage 假设带进代码评审;OpenCost 生成 Kubernetes allocation、asset 与 cloud cost API;Kubecost 在 Kubernetes 成本、网络、多集群和治理工作流上提供另一组能力;云厂商账单导出交付收费、折扣和供应商原生分摊;FOCUS 统一部分跨云语义,但不会自动修复身份、政策或缺失数据。
| 决策问题 | 主证据入口 | 需要补齐的证据 |
|---|---|---|
| PR 变更前估算 | Infracost scan/inspect 或云定价 API | usage 假设、折扣边界、上线后回测 |
| Kubernetes workload 归属 | OpenCost、Kubecost、云原生容器分摊 | 原始账单、历史 owner、共享/空闲政策 |
| 资产与承诺利用 | 云账单、资产目录、成本平台 | 工作负载关键性、退出和中断风险 |
| 跨云账单模型 | FOCUS 数据集与供应商扩展列 | 供应商 schema 版本、修订、分摊差异 |
| 单位经济 | 成本账本与业务事件流 | SLO、收入/毛利口径、反作弊和迟到事件 |
没有任何一项应该因为“能展示总费用”就成为唯一账本。OpenCost 的 Allocation 不等于发票,Infracost 的 usage file 不等于实际计量,云门户页面也不等于不可变历史数据。选型目标是让证据可组合、可导出和可替换,而不是让一个产品拥有所有事实。
用能力契约代替功能勾选表
在试点开始前,给每个候选工具建立相同的能力契约。契约不写“支持报表”,而写可验证行为:
{
"tool": "<candidate>",
"evidenceTypes": ["allocation", "asset"],
"grain": ["cluster", "namespace", "workload"],
"costBasis": ["modeled-effective"],
"sourceLineage": "origin-id-exportable",
"revisionModel": "append-with-revision",
"freshnessSlo": "<approved-lag>",
"authorization": "scope-filtered-server-side",
"export": ["json", "parquet"],
"writeActions": "separate-identity",
"exitEvidence": ["raw", "policy", "mapping", "audit"]
}粒度声明决定能否连接:只有 namespace 聚合的结果不能追踪短任务;没有 origin ID 的分摊无法逐费用守恒;不保存 revision 的导出无法解释历史变化。权限声明也必须写到服务端查询和导出,而不是“页面隐藏了其他团队”。
跑一遍可复现的选型实验
下面的 Node.js 脚本不连接真实云环境,只验证候选能力能否满足两个具体决策。保存为 evaluate-cost-tools.mjs:
const needs = {
prGate: {
required: ["estimate", "revision", "structured-export", "exit"],
weights: { estimate: 4, revision: 3, "structured-export": 2, exit: 3 },
},
k8sShowback: {
required: ["allocation", "asset", "origin-lineage", "rbac", "exit"],
weights: { allocation: 4, asset: 2, "origin-lineage": 4, rbac: 3, exit: 3 },
},
};
const candidates = [
{ name: "iac-estimator", capabilities: ["estimate", "revision", "structured-export", "exit"] },
{ name: "cluster-allocator", capabilities: ["allocation", "asset", "origin-lineage", "rbac", "exit"] },
{ name: "pretty-dashboard", capabilities: ["allocation", "asset"] },
];
for (const [decision, need] of Object.entries(needs)) {
console.log(`\n[${decision}]`);
for (const tool of candidates) {
const missing = need.required.filter((item) => !tool.capabilities.includes(item));
const earned = need.required
.filter((item) => tool.capabilities.includes(item))
.reduce((sum, item) => sum + need.weights[item], 0);
const total = Object.values(need.weights).reduce((sum, value) => sum + value, 0);
console.log(JSON.stringify({ tool: tool.name, score: `${earned}/${total}`, missing }));
}
}执行:
node evaluate-cost-tools.mjs正向结果应显示 iac-estimator 完整满足 prGate,cluster-allocator 完整满足 k8sShowback。这不是宣布它们“综合最好”,而是证明两个决策需要不同主工具。
反向实验给 pretty-dashboard 增加一个高权重的 dashboard-count,再删除 origin-lineage 和 exit 的必需约束。它会获得漂亮分数,却仍无法逐费用对账或迁移历史。这个实验揭示了选型表最危险的偏差:易演示功能会压过不可见但决定长期可控性的证据能力。
实验完成后删除本地脚本即可:
rm evaluate-cost-tools.mjsPowerShell 使用 Remove-Item .\evaluate-cost-tools.mjs。
真实试点要验证四条链
本地评分只能淘汰明显不合格候选,不能替代接入试点。生产候选要用同一批脱敏输入并行验证:
身份链:provider resource ID 能否稳定连接 cluster、namespace、workload、owner 和 cost center。金额链:同口径下 allocation 能否回到 origin charge,账单能否回到 invoice,修订是否幂等。决策链:PR、异常、优化候选和 chargeback 是否保留规则版本、审批、豁免和回滚。
退出链:原始数据、映射、政策、结果、审计和凭证能否导出、接管并核销。
试点数据不需要覆盖全部组织,但必须覆盖难例:短生命周期任务、共享平台、空闲容量、负数抵扣、迟到费用、资源改名、owner 迁移、多币种和没有标签的资产。只用稳定的单集群日常负载,会把最昂贵的数据缺陷留到上线后。
双轨窗口内按同一主键比较,不只比较总额:
coverage_delta = candidate_covered_origin / expected_origin
conservation_delta = origin_effective - sum(allocated_effective)
identity_delta = owner_old != owner_new
freshness_delta = now - latest_complete_window
decision_delta = old_action != candidate_action任何差异都要分类为语义变化、数据缺失、身份变化、规则差异、供应商修订或实现缺陷。总额接近但 owner 大规模迁移,仍然不能切换。
架构评审先过门槛,再比较得分
正式评审不应把所有指标加权后选最高分。证据时效、证据完备性、权限隔离和退出能力是准入门槛;只有候选都越过门槛,成本、易用性和交付速度才适合进入加权比较。否则,一个页面丰富但无法追溯原始费用的产品,可能靠大量低价值功能抵消一个致命缺口。
| 门槛 | 评审证据 | 通过标准 | 拒绝或暂停条件 |
|---|---|---|---|
| 时效 | 每层数据的 eventTime、availableAt、完整窗口水位 | 决策发生前已到达,且延迟不超过该决策批准的 SLO | 用未闭合账期驱动自动优化,或用昨日 owner 解释今日资源 |
| 完备性 | expected origin、covered origin、未知归属、修订批次 | 覆盖率达到约定阈值,守恒差可解释,未知项单独暴露 | 缺失行被填 0,负数抵扣被过滤,未知费用被静默均摊 |
| 可退出 | 原始引用、映射、政策、结果、审计的导出与重放 | 空环境能重放代表性窗口,导出格式和标识不依赖供应商 UI | 只能导出截图或聚合 CSV,历史规则无法带走 |
| 权限 | 采集、查询、导出、执行四类身份与审计事件 | 最小权限、服务端域隔离、短期凭证、执行身份独立 | 长期管理员密钥贯穿账单、集群和执行动作 |
| 容量 | 峰值行数、基数、扫描量、重算时长、API 限流 | 峰值和补算均在容量预算内,限流后可续跑且不重复入账 | 平均负载通过,但月末峰值或历史重算无法完成 |
时效和完备性必须成对判断。一个十分钟前更新但只覆盖 70% origin 的页面并不“新鲜”;一个最终覆盖 100% 却在关账后才到达的结果,也不能支撑实时变更门禁。评审记录应同时展示 latest_complete_window、覆盖率、守恒差和未知金额,禁止只展示最后更新时间。
双轨试点至少跨过一次账期关闭、一次供应商修订和一次 owner 变更。旧链路继续承担现有报表,新链路只生成影子结果;两边使用相同的 origin 主键、汇率日、成本口径和身份快照。只有连续窗口同时满足覆盖率、守恒差、身份迁移率、查询延迟和失败恢复目标,才能先切读、再切告警,最后才讨论自动执行。任一硬门槛失败,结论都是延长试点或淘汰,而不是用总体平均分放行。
评分模型也要做反向验证。把脚本里的 pretty-dashboard 改成拥有十个每项 1 分的展示能力,同时让 origin-lineage、rbac 和 exit 各值 3 分;若模型直接求和,它可能以 10 > 9 获胜。正确实现应先执行否决条件:
const veto = ["origin-lineage", "rbac", "exit"];
const missingVeto = veto.filter((item) => !tool.capabilities.includes(item));
const eligible = missingVeto.length === 0;
console.log({
tool: tool.name,
eligible,
decision: eligible ? "enter-weighted-review" : "reject",
missingVeto,
});正向验证给候选补齐三项硬能力,预期输出为 eligible: true,它才进入成本和体验评分。反向验证删除任意一项,即使继续增加仪表盘、报表和告警分数,预期仍为 decision: reject。判断标准不是总分高低,而是硬门槛不能被软能力补偿;如果程序仍输出放行,评分模型本身就没有评审资格。
评审结论写入可版本化的架构决策记录,至少保存:决策问题和有效期、候选版本、证据窗口及完整水位、硬门槛结果、权重与否决项、双轨差异、权限边界、容量基线、三年总拥有成本、供应商依赖、退出重放结果、批准人和异议。记录引用原始证据位置,不把截图当唯一附件;权重变化必须新建修订,不能覆盖当时的判断依据。
复评不是按年走形式。出现以下任一事件就重新打开决策:供应商 schema 或计费语义变化,关键 API 弃用,价格或合同阶梯变化超过预算阈值,覆盖率或时效连续越线,未知成本占比上升,组织 owner 模型调整,集群或账号规模跨越容量档位,权限事件暴露越权,工具无法在恢复目标内补算,退出演练失败,或者候选版本改变结果超过批准误差。复评先冻结自动动作并恢复到最近通过的证据合同,再判断修复、降级、并存还是退出。
权限和敏感信息从第一天拆开
成本数据会暴露账号、资源 ID、产品规模、客户标签、合同折扣、利润和组织关系。采集身份只读指定账单导出;Kubernetes 采集器只读所需资源;价格查询与账单读取分离;数据仓库 writer 只写目标前缀;业务 owner 只能查询本域;执行 rightsizing 或关闭资源的身份另行审批。
候选工具若要求一个长期管理员 token 同时读取组织账单、遍历集群并修改工作负载,应直接进入高风险项。优先使用短期工作负载身份、托管身份或细粒度角色;导出设置 TTL,加密存储,日志只记录批次和匿名化主键。试点输入使用语义占位符,不能把真实账单复制到工单、聊天或公开仓库。
容量和总拥有成本藏在数据路径里
工具许可只是成本的一部分。短任务和高基数标签会放大指标序列、账单行、对象存储、Catalog 分区、查询扫描量和分摊结果;多集群会增加标签冲突、跨区传输与保留副本;长窗口重算会占用仓库计算并影响正常报表。
容量模型至少记录:日增原始行、平均分摊目标数、保留期、压缩率、重算并发、查询窗口、指标基数、API 限流和导出延迟。总拥有成本还要加入部署升级、数据工程、权限审计、争议处理、供应商支持、迁移和退出的人力。一个较贵但能导出完整 lineage 的平台,可能比便宜却锁住历史口径的平台更低风险。
故障从“哪层证据坏了”开始排
| 现象 | 首先检查的证据 | 常见根因 | 恢复动作 |
|---|---|---|---|
| PR 估算突然为 0 | IaC revision、usage、价格响应 | 不支持资源、凭证失效、解析失败 | 标记 unknown,禁止把 0 当节省 |
| namespace 成本暴涨 | identity、request/usage、shared/idle policy | owner 迁移、共享驱动改变、空闲重分 | 固定旧快照并逐 origin 对比 |
| 平台总额与发票不符 | cost basis、账期、修订、税费抵扣 | 混用 Billed/Effective、迟到交付 | 在同一口径重算,不覆盖原始行 |
| 单位成本下降但总成本上升 | 业务分母、SLO、产品组合 | 分母增长、长尾恶化、事件重复 | 验证业务事件唯一性和分层指标 |
| 切换后历史数字漂移 | schema、政策、价格与映射版本 | 新平台重解释历史 | 停止切流,恢复旧视图并保留差异 |
排障时先冻结输入批次与查询参数,再定位证据层。直接调整仪表盘过滤器会让故障现场消失,也会让后续复盘无法复现。
升级、回滚与退出决定选型是否成熟
升级前保存 schema、API、价格源、政策和权限快照,用代表性历史窗口在新版本平行计算。比较覆盖率、守恒差、owner 迁移、查询延迟和导出兼容;通过后只切换读流量,写动作继续关闭。出现差异时恢复旧 reader 和旧政策,不删除新版本证据。
退出测试应在采购前完成一次。导出原始引用、规范化数据、身份映射、分摊规则、决策记录、审计日志和历史结果,在空环境中重放一个窗口。随后撤销账单、集群、仓库、VCS 和对象存储凭证,证明旧平台不能继续采集或执行动作。不能导出的 lineage 和政策,本质上是尚未计价的迁移债务。
架构决策记录要留下取舍,不留下冠军榜
最终决策至少记录问题、证据层、候选能力缺口、试点差异、权限面、容量、总拥有成本、回滚条件和退出证据。工具可以组合:估算器负责变更前,集群分摊器负责运行中,云账单负责收费事实,业务数据负责价值;统一数据合同和责任 owner 把它们连接起来。
成熟选型的结论通常不是“采购一个全能平台”,而是明确哪一层由谁作为 system of record,其他工具如何提供派生证据,发生冲突时按什么顺序裁决。只要这条证据链可复现、可质疑、可回滚和可退出,工具更换就不会迫使组织重写自己的成本历史。
