AWS 成本数据导出与 EKS Split Cost Allocation
两个团队共享同一组 EKS 节点。财务只能看到 EC2 实例费用,平台组按 namespace 粗分后发现总额对不上;开发者去 Cost Explorer 搜 Pod 名,结果一条也没有。大家开始怀疑标签没生效,其实数据入口就选错了:EKS Pod 拆分结果进入 Cost and Usage Report,不会直接出现在 Cost Explorer。
AWS Split Cost Allocation Data 把承载 Pod 的 EC2 成本按 CPU、内存和受支持的加速器维度拆成新的账单行,再通过 CUR 或 CUR 2.0 交付到 S3 和查询链。它能提供账单侧的 Pod 成本证据,却不是实时监控,也不会替团队决定共享成本应该由谁承担。
为什么 Cost Explorer 找不到 Pod
启用过程包含两个独立动作:管理账号在 Cost Management preferences 中为 EKS 选择 Split Cost Allocation 测量方式;CUR 或 CUR 2.0 export 再显式包含 split cost allocation data。只完成第一个动作,没有报表承接数据;只创建普通 CUR,没有打开 split 配置,也不会出现 split_line_item_* 字段。
数据链是:
EKS Pod requests / telemetry
↓
共享 EC2 父资源及摊销成本
↓
SplitUsage / SplitCost / UnusedCost
↓
CUR 或 CUR 2.0 Data Export
↓
S3 → Glue/Athena 或数仓 → 团队分摊视图这条链最长可能等待 24 小时,适合日级对账、showback 和 chargeback,不适合给 HPA、Karpenter 或在线限流提供分钟级反馈。实时利用率仍应走 Prometheus、CloudWatch 或 Kubernetes 观测链。
先选择分摊测量方式
EKS 当前有三种测量入口,选择会改变“用什么代表 Pod 占用”:
| 测量方式 | 分摊依据 | 适合的现场 | 新增依赖与风险 |
|---|---|---|---|
| Resource requests | Pod CPU、memory requests | 已治理 requests,希望用预留容量分摊 | CPU 和 memory requests 必须同时存在;错误 request 会直接污染账单归属 |
| Amazon Managed Service for Prometheus | requests 与实际利用率取较高值 | 已有 AMP 指标链,希望减少低 request 低估 | Organizations all features、service-linked role、采集与查询费用 |
| CloudWatch Container Insights | Container Insights 遥测 | 已采用 CloudWatch 容器观测 | 采集覆盖、日志指标费用与遥测断档 |
加速实例上的 GPU、Trainium、Inferentia 等专用处理器只支持 Resource requests 方式,并额外生成 accelerator 记录。选择遥测方式前要先回答:指标是否覆盖所有账号与集群、断档时如何标记、额外服务费用是否低于提高精度的收益,以及退出时谁核销 service-linked role 和 trusted access。
“实际利用率”也不等于最终业务价值。AWS 的 split usage 会根据所选方式计算,随后按父实例成本拆分;共享平台费、控制面、网络、存储和组织政策仍需要下游模型处理。
在管理账号启用数据链
操作应由拥有 Billing and Cost Management 变更权限的专用身份完成,不要用日常集群管理员账号兼任付款账号管理员。
打开 Billing and Cost Management 控制台的 Cost Management preferences。在 Split cost allocation data 中选择 Amazon EKS。选择 Resource requests、Amazon Managed Service for Prometheus 或 CloudWatch Container Insights。
保存设置并记录变更单、测量方式、付款账号和责任人。创建或编辑 legacy CUR / CUR 2.0 Data Export,启用 Split cost allocation data。选择 S3 交付位置、格式、压缩、区域和刷新策略,再建立 Glue/Athena 或数仓消费入口。
CUR 2.0 的表配置中应出现:
TIME_GRANULARITY = HOURLY
INCLUDE_RESOURCES = TRUE
INCLUDE_SPLIT_COST_ALLOCATION_DATA = TRUEINCLUDE_RESOURCES 让资源 ID 进入表,便于通过 split_line_item_parent_resource_id 回到承载 Pod 的 EC2 实例;小时粒度还决定 SplitUsageRatio 是否可用。启用这些配置会显著增加行数和文件大小,不能在没有容量评估的情况下直接复制到所有付款账号。
控制台、API 和 CLI 的具体权限动作以启用说明为准。成员账号能否读取结果取决于 CUR 和 S3 权限,但修改 Cost Management preference 的边界仍在 regular account 或 payer/management account。
建立一个可归属的验证工作负载
验证不需要新建昂贵节点。选择隔离的非生产 EKS namespace,确认现有节点有余量,再创建带完整 requests 与稳定标签的小型 Deployment:
apiVersion: v1
kind: Namespace
metadata:
name: split-cost-lab
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: allocation-probe
namespace: split-cost-lab
spec:
replicas: 1
selector:
matchLabels:
app: allocation-probe
template:
metadata:
labels:
app: allocation-probe
cost-center: platform-lab
environment: lab
spec:
containers:
- name: sleeper
image: registry.k8s.io/pause:3.10.1
resources:
requests:
cpu: 100m
memory: 64Mi
limits:
cpu: 100m
memory: 64Mi执行并保存证据:
kubectl apply -f allocation-probe.yaml
kubectl -n split-cost-lab rollout status deployment/allocation-probe --timeout=120s
kubectl -n split-cost-lab get pod -l app=allocation-probe \
-o custom-columns='NAME:.metadata.name,NODE:.spec.nodeName,CPU:.spec.containers[*].resources.requests.cpu,MEM:.spec.containers[*].resources.requests.memory'
kubectl -n split-cost-lab get pod -l app=allocation-probe --show-labels预期 Pod Ready,节点名、CPU request、memory request 和三个标签均可见。记录 Pod UID、父节点、namespace、workload 名和存活小时,用于数据到达后的反查;不要把真实客户名、邮箱或工单号放进标签。
正向实验:从 split 行回到父实例
等待 export 交付后,先检查 S3 是否出现新的 manifest/数据分区,再确认 Glue 或查询引擎 schema 包含 split_line_item_*。CUR 2.0 的列名采用下划线形式;legacy CUR 控制台与文档常显示 splitLineItem/SplitCost 形式,消费层应在 schema adapter 中处理,不要让报表直接依赖供应商物理列名。
查询应至少返回以下证据:
Pod 对应的 line_item_resource_id。承载节点对应的 split_line_item_parent_resource_id。CPU 与 memory 不同 usage type 的 reserved_usage、actual_usage 或 split_usage。
split_cost 与 unused_cost,有折扣时还要区分 net_split_cost 与 net_unused_cost。aws:eks:cluster-name、namespace、workload name/type 和 Deployment 标签。
Athena 表名和标签列会随 export 配置变化,先用 SHOW COLUMNS 核对实际 schema,再编写查询。下面是字段骨架,不应未经适配直接当作所有账号的固定 SQL:
SELECT
line_item_usage_start_date,
line_item_resource_id,
split_line_item_parent_resource_id,
line_item_usage_type,
split_line_item_reserved_usage,
split_line_item_actual_usage,
split_line_item_split_usage,
split_line_item_split_cost,
split_line_item_unused_cost
FROM <cur_2_table>
WHERE line_item_usage_account_id = '<member-account-id>'
AND line_item_usage_start_date >= TIMESTAMP '<window-start>'
AND line_item_usage_start_date < TIMESTAMP '<window-end>'
AND line_item_resource_id LIKE '%allocation-probe%';如果 Pod 名不在 resource ID 中,就用已激活的 EKS 标签、时间窗和父节点联合定位。验收不是“查到一行”,而是同一父实例、同一小时、同一成本口径下,Pod 分摊与 unused 部分能够回到父资源成本,且没有把 CPU、memory 两种 usage type 重复求和。
SplitCost 包含适用的 RI 或 Savings Plans 摊销;NetSplitCost 在账号存在适用折扣时表示折扣后的有效成本。Showback 与发票对账必须明确选择哪一列,不能把 public on-demand、amortized 和 net cost 混在同一总额中。
反向实验:缺少 requests 时会发生什么
Resource requests 模式要求 Pod 同时设置 CPU 和 memory requests。复制验证 Deployment,改名为 missing-request-probe,删除 memory request,只保留 CPU:
kubectl -n split-cost-lab patch deployment allocation-probe \
--type=json \
-p='[{"op":"remove","path":"/spec/template/spec/containers/0/resources/requests/memory"}]'
kubectl -n split-cost-lab rollout status deployment/allocation-probe --timeout=120s
kubectl -n split-cost-lab get pod -l app=allocation-probe -o yamlKubernetes 仍可能正常调度 Pod,这正是危险之处:运行成功不等于成本分摊完整。等待相同账单延迟后,按 Pod、namespace 与时间窗查询,预期该对象无法得到完整的 request-based split cost。若下游报表把缺失行静默当零,团队费用会被低估并转移到 unused 或其他聚合项。
恢复 memory request 后重新部署,并在后续账期窗口确认 split 行恢复。生产门禁应在 admission policy 或 CI 中拒绝缺 CPU/memory requests 的工作负载,不要等到账单断档后人工追补。
读懂 split 字段而不是只看金额
ReservedUsage 表示 Pod 配置的 CPU 或 memory request;ActualUsage 表示观测到的实际用量;SplitUsage 在适用模式下取二者较大值。SplitUsageRatio 表示它相对于父 EC2 可用 CPU 或内存的比例,并且只在小时粒度报表中提供。
SplitCost 是分给 Pod 的成本,UnusedCost 是该资源维度未利用部分按 split usage 比例分摊的成本。父资源 ID 把 Pod 行连接回 EC2 实例。一个 Pod 通常每小时增加 CPU、memory 两类记录;加速实例还增加 accelerator 记录。
每日新增行数可先估算:
普通 EKS:Pod 数 × 平均存活小时 × 2 × 24
加速实例:Pod 数 × 平均存活小时 × 3 × 24短生命周期 Job 会显著放大 S3 对象、Glue 分区、Athena 扫描和数仓计算。容量评审应同时看平均 Pod 数、每小时新 UID、标签宽度、保留期、压缩格式和下游查询模式,而不是只看集群节点数。
标签既是分摊键也是敏感数据
AWS 自动生成 aws:eks:cluster-name、aws:eks:namespace、aws:eks:node、aws:eks:deployment、aws:eks:workload-name 与 aws:eks:workload-type。workload-type 只在 Pod 恰好由一个受支持的内置 workload 管理时出现;自定义控制器、多 owner 或无法唯一归属的 Pod 不能强行补成 Deployment。
用户 Kubernetes label 也可以作为 cost allocation tag,但需要在管理账号激活。每个 Pod 最多导入按字母排序后的前 50 个标签;超出部分会丢弃,托管服务自动添加的标签也计入限制。Tag key 最长 128 字符,value 最长 256 字符,超限标签不会进入成本标签。
新增 label key 可能等待最长 24 小时才出现在激活页面,激活后还可能再等待最长 24 小时生效。删除 Pod label 不会自动停用已经导入的 cost allocation tag;停用后,相应列到下一个月的 CUR 才删除。标签 schema 因此必须由中央 owner 管理,不能让各团队无限创建高基数成本维度。
集群名、namespace、workload、节点、资源 ID、账号、折扣和成本中心会暴露组织结构与财务信息。原始 CUR 应进入中央 FinOps 数据域,应用团队只读取脱敏、按组织单元授权的派生视图。标签中禁止出现客户名、邮箱、工单、密钥片段或个人标识。
常见失败从哪一层查
| 现象 | 判断证据 | 常见原因 | 修复路径 |
|---|---|---|---|
| Cost Explorer 没有 Pod | export 配置与 S3 manifest | 产品边界,不是查询语法问题 | 改查启用 split 的 CUR/CUR 2.0 |
| CUR 没有 split 列 | table configuration | 只 opt in,未在报表启用 split | 编辑 export,确认新 schema 与新分区 |
| 有列但没有 EKS 行 | preference、账号、数据窗口 | EKS 未启用、查错付款账号、尚未到达 | 核对管理账号设置并等待交付窗口 |
| 部分 Pod 没成本 | Pod requests 与 owner reference | requests 不完整、自定义控制器、多 owner | 补 requests;未知 owner 单独进入治理池 |
| 标签缺失 | cost allocation tag 激活状态 | 超 50、超长、未激活或仍在传播 | 治理标签集合,等待传播后再查新行 |
| 金额重复或不守恒 | usage type、父资源、时间粒度 | CPU/memory 重复汇总,口径混用 | 按 origin/parent 和成本列建立守恒测试 |
| 查询费用突然升高 | S3 行数、Athena bytes scanned | 短 Pod、高基数标签、全表扫描 | 分区裁剪、Parquet、派生聚合与保留治理 |
“数据最多 24 小时到达”不是固定 SLA。真实账号应记录 preference 保存时间、export 交付时间、首条 split 行时间和关闭后的最后一条数据时间,形成自己的延迟基线。
权限边界要拆成四种角色
启用 preference、创建 export、写入 S3 和查询派生视图不是同一个权限包:
Billing 配置管理员只负责 EKS opt-in 与测量方式。Data Export 管理员负责 CUR 2.0、S3 交付和 schema 变更。FinOps 数据管道身份读取原始账单并写入受控派生层。
应用团队只读自身 namespace 或成本中心视图。
选择 AMP 时还会引入 Organizations trusted access、service-linked role、workspace 与指标权限;选择 Container Insights 会引入 CloudWatch 采集和费用。不能为了查看团队成本,把付款账号管理员、S3 原始账单或 Organizations 写权限授予普通开发者。
S3 bucket policy、KMS key policy、Glue Catalog、Athena workgroup 和跨账号角色必须一起审计。只有 bucket 可读但 KMS 不可解密会表现为查询失败;只有 Glue 可见但 S3 前缀不可读会表现为表存在却无数据。
与 FOCUS 和 Kubernetes 工具如何协作
AWS Data Exports 同时提供 FOCUS 1.2 with AWS columns,但 EKS Split Cost Allocation 的官方交付承诺落在 legacy CUR/CUR 2.0。FOCUS 的分摊字段从 1.3 才进入规范,不能假定 AWS FOCUS 1.2 已经完整提供 AllocatedResourceId 等字段。
跨云模型应保留三层:供应商原始 CUR、FOCUS 规范化账单、Kubernetes allocation 派生表。OpenCost 或 Kubecost 提供更及时的集群使用与分摊视图,CUR 提供账单事实;二者可以对账,但不能互相覆盖。差异应归入价格口径、折扣、usage/request、网络存储、控制面、idle 和延迟修正等明确类别。
升级、回滚与退出
CUR schema 或查询模型升级应先双写新表,用固定账期验证行数、父资源连接、成本守恒、标签覆盖和迟到修正,再切换报表。不要直接修改唯一生产视图;CUR 2.0 固定列与 nested map 仍可能影响旧 SQL。
退出时按以下顺序降低盲区:
冻结消费查询版本,保存最终对账快照和 schema。从目标 CUR/CUR 2.0 export 移除 split cost allocation data,确认下游能处理字段或新行停止。在 Cost Management preferences 关闭 EKS opt-in。
核销不再使用的 AMP/Container Insights、trusted access、service-linked role 和指标数据。按审计策略处理 S3、Glue、Athena、KMS、数仓表与派生报表。撤销跨账号查询角色和临时验证 namespace。
kubectl delete namespace split-cost-lab
kubectl get namespace split-cost-lab关闭功能不会自动删除历史账单文件,也不会自动停用所有 Kubernetes label 对应的 cost allocation tag。完成退出的证据应包括:没有新 split 行、权限已核销、查询与报表已替换、历史数据按保留策略处置,并且账单总额没有因为停用派生视图而失去审计入口。
