3. 成本数据管道工程
当成本治理从“看账单”进入“做工程”阶段,第一块真正的地基不是预算告警,而是成本数据管道。AWS 提供的原始成本数据是 Cost and Usage Report(CUR),它按小时、按资源、按用量维度记录账户中每一次计费事件。CUR 2.0 在原有列式结构上引入了更规范的列命名、更细的账单周期语义,以及 Data Exports 交付方式,使数据可以直接以 Parquet 格式落入 S3,而不再必须依赖 CSV 加 Glue 转换。理解这条管道的每一段,决定了后续分摊、归因、异常检测能否稳定运行。
CUR 2.0 的数据格式与关键列
CUR 2.0 的本质是一张宽表:每一行代表“某个账单周期内、某个账户、某个资源、某个用量类型、某个定价维度”下的一次计费记录。它既包含用量(usage),也包含费用(cost),还包含资源标签、定价条款、折扣、摊销与预留实例覆盖等信息。理解 CUR 时,最容易被忽略的是费用列之间的语义差异:line_item_unblended_cost 是未混合的原始费用,line_item_blended_cost 是组织内混合后的费用,reservation_effective_cost 与 savings_plan_effective_cost 则把预付和承诺折扣按小时摊销到实际用量上。做成本归因时,如果只看 unblended,会低估 RI/SP 覆盖资源的真实单位成本;如果只看 blended,又无法定位具体账户的采购差异。
另一个关键维度是 line_item_line_item_type,它区分 Usage、Tax、Fee、Credit、Refund、RIFee、SavingsPlanCoveredUsage 等记录类型。很多“成本对不上”的问题,根源就是把 RIFee 或 Credit 行误当成普通用量行参与聚合。CUR 2.0 还通过 bill_billing_period_start_date 与 line_item_usage_start_date 明确区分账单周期与用量发生时间,这对跨月摊销和按天回溯至关重要。
S3 数据湖落地与目录设计
Data Exports 可以直接把 CUR 2.0 以 Parquet 格式投递到指定 S3 前缀。落地时最重要的不是“存下来”,而是目录结构是否与查询模式一致。Athena 按分区裁剪文件,如果目录按 billing-period=2025-06/account_id=111122223333/ 组织,那么“查某账户某月成本”只需扫描少量文件;如果所有文件平铺在一个前缀下,每次查询都要扫描全量,成本会随数据增长线性上升。
一个可用的落地约定是:s3://finops-cur/cur2/billing-period=YYYY-MM/account_id=.../region=.../。其中 billing-period 用账单周期而非用量日期,保证跨月摊销记录落在正确周期;account_id 用 line_item_usage_account_id,便于按业务账户归因;region 用于区域成本对比。需要注意,Data Exports 的 Parquet 文件本身已带列式压缩,单文件不宜过小,否则会产生大量小文件,拖慢 Athena 规划阶段。若发现单分区下文件数过多,可用 Glue ETL 或 S3 Batch 做小文件合并。
Glue 爬虫与 Athena 查询
Parquet 落地后,用 Glue Crawler 扫描 S3 前缀,自动推断 schema 并写入 Glue Data Catalog。Crawler 的关键配置是分区识别:如果目录名采用 key=value 形式,Crawler 会将其识别为分区列;否则只会当成普通路径。爬取完成后,在 Athena 中即可用标准 SQL 查询。下面是一段按账户与产品聚合当月摊销成本的示例查询:
SELECT
line_item_usage_account_id AS account_id,
product_servicecode AS service,
SUM(COALESCE(reservation_effective_cost, 0)
+ COALESCE(savings_plan_effective_cost, 0)
+ COALESCE(line_item_unblended_cost, 0)) AS amortized_cost
FROM finops_cur.cur2_parquet
WHERE billing_period = '2025-06'
AND line_item_line_item_type IN ('Usage','SavingsPlanCoveredUsage','RIFee')
GROUP BY 1, 2
ORDER BY amortized_cost DESC
LIMIT 50;
这段查询刻意把 RI/SP 有效成本与未混合成本相加,是为了让被承诺折扣覆盖的资源也体现真实摊销成本。若只写 line_item_unblended_cost,被 RI 覆盖的 EC2 用量会显示为 0,导致成本被严重低估。查询时始终带上 billing_period 条件,是触发分区裁剪、控制扫描量的最低要求。
Parquet 分区优化与常见判断
分区不是越多越好。分区列基数过高(例如把 resource_id 作为分区)会产生海量小分区,反而拖慢查询。合理的分区列应满足:查询条件中高频出现、基数适中、每个分区下有足够大的文件。对 CUR 而言,billing_period、account_id、region 是较稳妥的组合。若业务需要按标签归因,标签应作为普通列而非分区列,通过 Athena 的 resource_tags_user_* 列过滤。
line_item_usage_start_date、bill_billing_period_start_date 等列名,避免因列不存在导致查询失败或结果为空。把这条管道跑通后,成本数据才真正成为可查询、可聚合、可归因的资产。后续的分摊规则、异常检测、单位成本指标,都建立在这张按小时粒度、带标签、带摊销成本的 CUR 2.0 表之上。管道稳定,治理才有可靠的数据底座。