文档中心
App Hub、Cloud Hub、FinOps 统一数据模型
以 GitOps 声明、Terraform state 与 Shared CMDB 为唯一事实来源,统一 App Hub、Cloud Hub、FinOps 三个多云中立视图的实体、标签、对账与分摊模型设计稿。
App Hub、Cloud Hub、FinOps 统一数据模型
版本:0.1 草稿 · 日期:2026-10-18 · 状态:待用户评审,不代表已实现。
0. 结论
三个视图共用一套事实:GitOps 声明(期望状态)、Terraform state(受管范围)和 Shared CMDB 观察(运行事实)三方对账后,形成同一个资源主键 canonical_id 和同一组标签 app / env / owner / tier。Cloud Hub 沿 provider → account/project → region → network → resource 往下钻取,App Hub 沿 app → service → workload → environment 往上聚合,FinOps 把按 account 进来的账单经同一主键和标签分摊到 app/env。各云原生的 App Hub 或资源中心只做单向输出适配器,不回读成事实。
现有基础比预想的多:CMDB PostgreSQL v1 schema(resources、desired_resources、resource_relations、collection_runs 等)已在 Playbooks 仓库落地,并有 UAT 按需入库证据;GitOps 有 CMDBCollectionScope,并把无只读身份的 provider 显式列为 pending。缺的部分:统一标签(现在只有 GCP 资源带 owner/workload/environment labels,而且 owner 的语义与本方案不同)、应用实体、PoP/配额实体、成本归属,以及定时对账 job。产品页 /products/cloud-hub 的数字目前看是静态数据,接入本模型之前不能当作实测状态。
1. 范围与证据边界
1.1 范围
| 在范围内 | 不在范围内 / 未看到 |
|---|---|
公开页 console.svc.plus/products/cloud-hub 的四个视图与厂商矩阵;GitOps/IaC/Playbooks/Toolkit/Observability 的公开源码;知识库 7 份白皮书 |
Home-Lab;需要登录的 /panel/cloud-hub、/panel/finops(未看到内容,本文不推测其现状);任何真实云 API、Vault、数据库的 live 状态 |
本文只是设计稿:不创建 Vault 记录、policy、云 IAM、数据库对象或 workflow;文中 YAML/SQL 都是草案,路径与字段都要经评审后才能进入 owner 仓库。
1.2 证据来源与固定版本
| 来源 | 版本 / 时间(UTC+8) | 用途 |
|---|---|---|
知识库白皮书(本仓库 docs/reference/ 等目录,下文缩写) |
平台白皮书 v1.4(基线 2026-10-05)记为 [PE];三档权限 v1.2 记为 [IAM];CMDB 规划 v0.1 记为 [CMDB];CMDB Vault 路径记为 [VP];编排架构记为 [ORC];统一 IaC Pipeline v0.7 记为 [PIPE];Global Mesh 产品白皮书记为 [GM] | 命名、职责、路径、状态 |
ai-workspace-infra/gitops |
50f9f74e(2026-10-18 16:08) |
resources/、topology/、schemas/、iac/、releases/、AGENTS.md、声明规范 |
ai-workspace-infra/playbooks |
068dd2a2(2026-10-18 15:09) |
contracts/cmdb-postgresql-v1/migrations/001、002,cmdb-monitoring Vector 模板 |
ai-workspace-infra/iac_modules |
4b49911f(2026-10-18 13:52) |
terraform-hcl-standard/docs/cmdb-observations-contract.md、cmdb-inventory-contract.md、AGENTS.md |
ai-workspace-infra/platform-ops-toolkit |
7f6cfff(2026-10-17 19:40) |
docs/resource-aggregation-model.md、workflow 目录 |
ai-workspace-infra/observability |
a69c1024(2026-10-15 22:48) |
README、docs/zh/architecture.md |
ai-workspace-infra/global-mesh |
3a9bfae2(2026-09-14 02:06) |
database/schema.sql、scripts/sync_vps_regions.py |
ai-workspace-services/billing-service(不在原清单内,但 [PE] 11.11 引用了其 cloud_vendor_costs 与 FinOps 后台任务) |
3ec0955b(2026-10-18 01:20) |
sql/migrations/2026100701_cloud_vendor_costs.up.sql、docs/open-platform-finops-control-plane.md、docs/unified-billing-finops-architecture.md |
1.3 标注约定
- 分类:
已有=白皮书或仓库里有同名、同义的定义,并能引用出处;扩展=已有定义,但需要增加字段或范围;新增=源文件和仓库里都找不到。 - 实现状态:沿用 [PE] 1.3 的证据等级。
已实现(UAT)=有白皮书记载的真实运行证据;源码/CI=代码或合同存在,没有 live 验收;规划=只在文档中;待验收=已落地但缺验收;待核验=本文无法判断。 - 有出处的说法写成“[来源] 章节”;仓库文件写成相对路径。
1.4 本次核对得到的关键现状
- CMDB PG v1 已有表结构:
playbooks/contracts/cmdb-postgresql-v1/migrations/001_initial_schema.sql定义了collection_runs、resources、raw_snapshots、resource_observations、desired_resources、resource_relations、monitoring_status和 6 个视图,与 [CMDB] 4.1 一致。[CMDB] 12 记录 2026-10-09 Shared 主机上的 UATcmdb已完成迁移、GCP 入库和 Grafana 只读验证;定时采集未启用。 - pending 已有声明先例:
gitops/topology/shared/cmdb/observability-uat.yaml(kind: CMDBCollectionScope)启用了 GCP 的 compute/cloudrun collector;pending_providers列出 aws、linode、ucloud、cloudflare、supabase。vultr、hetzner、contabo 不在列表内。 - 标签现状:只有 GCP 资源声明带
labels,键为owner(值是platform-ops-toolkit,即管理工具)、workload、environment、lifecycle。Vultr/Akamai 声明用的是无键的tags: [web-saas, prod, ...]列表。tier只在GlobalMeshPools.metadata.labels出现过,取值out-of-band-network。监控 Vector 模板打的标签是instance/job/provider/project_id/logical_project/environment/env/service_domain,没有app。 - 已有一套 FinOps 标签与分摊规划:billing-service 的
open-platform-finops-control-plane.md中,FINOPS-003 定义了managed_by/repository/service/product/team/environment/cost_center/owner,FINOPS-603 定义了Unallocated/Untagged/Unknown Resource/Shared Platform。该文档自己把分摊引擎标为 🔴 Phase 2。 - 产品页数据像是静态的:global-mesh 仓库
telemetry_snapshots表的字符串默认值(如'225.58k'、'↗ 26.1%')与页面显示一致;sync_vps_regions.py里sync_time是硬编码的,hetzner/ucloud/contabo 的数量也是常量。GitOps PRODGlobalMeshPools声明了 41 个 provider-region,页面写的是 48 个 PoP。 - Observability 栈:仓库 README 写的是 VictoriaMetrics/VictoriaLogs/VictoriaTraces + OpenTelemetry Collector + Grafana,并非 Prometheus + Loki。产品页上 Hetzner 节点文案写着 “Telemetry Hub (Loki/Prom)”,与仓库不一致(见 §6 Q09)。
2. 统一实体模型
2.1 实体关系总览
erDiagram
PROVIDER ||--o{ CLOUD_ACCOUNT : has
PROVIDER ||--o{ REGION : offers
REGION ||--o{ POP : hosts
CLOUD_ACCOUNT ||--o{ QUOTA : limits
CLOUD_ACCOUNT ||--o{ RESOURCE : owns
REGION ||--o{ RESOURCE : locates
RESOURCE ||--o{ RESOURCE_RELATION : links
RESOURCE ||--o{ RESOURCE_LABEL : carries
RESOURCE ||--o| DESIRED_RESOURCE : reconciles
APP ||--o{ APP_SERVICE : contains
APP_SERVICE ||--o{ APP_WORKLOAD : runs_as
APP_WORKLOAD ||--o{ WORKLOAD_BINDING : bound_by
WORKLOAD_BINDING }o--|| RESOURCE : targets
CLOUD_ACCOUNT ||--o{ COST_LINE_ITEM : bills
COST_LINE_ITEM ||--o{ COST_ALLOCATION : splits_to
COST_ALLOCATION }o--o| APP : attributes
RECONCILE_RUN ||--o{ WORKLOAD_BINDING : produces
COLLECTION_RUN ||--o{ RESOURCE : observes
2.2 实体清单
| 实体 | 视图 | 关键字段 | 主键 / 关联 | 来源分类 |
|---|---|---|---|---|
| Provider | Cloud Hub | provider、provider_class、terraform_tree、auth_mode、coverage_status |
PK provider(规范化小写,见 §6 Q05) |
扩展:[PIPE] 1.2 六 provider 表 + iac/capabilities.yaml |
| CloudAccount | 三视图共用 | provider、account_ref、project_ref、scope、identity_ref、identity_status、cost_source |
PK (provider, account_ref, project_ref),与 CMDB resources 唯一键的前三列一致 |
扩展:[IAM] 3 account_ref/project_ref;[PE] 4.2 |
| Region | Cloud Hub | provider、region_ref、country_code |
PK (provider, region_ref) |
扩展:collection_runs.region_ref、resources.region |
| PoP | Cloud Hub | pop_id、provider、region_ref、role、source |
PK pop_id;FK Region |
扩展:GlobalMeshPools.spec.providers[].regions[] |
| Network | Cloud Hub | 作为 resource_kind='network' 的 Resource |
复用 resources + resource_relations(contains) |
新增(kind 取值) |
| Resource | 三视图 | canonical_id、native_resource_id、resource_kind、scope、provider_state、lifecycle、attributes |
唯一键 (provider, account_ref, project_ref, resource_kind, native_resource_id) |
已有:001_initial_schema.sql;[IAM] 10 |
| DesiredResource | 对账 | gitops_ref、resource_key、observed_match |
UNIQUE (gitops_ref, resource_key) |
已有:同上 |
| Quota | Cloud Hub | quota_kind、limit_value、used_value、observed_at |
PK (account, region_ref, quota_kind, run_id) |
新增 |
| ResourceLabel | 三视图 | label_key ∈ {app,env,owner,tier}、label_value、source |
PK (canonical_id, label_key, source) |
新增 |
| App | App Hub | app_key、project、owner、lifecycle |
PK app_key = <project>/<app> |
新增 |
| AppService | App Hub | service_key、tier |
PK <app_key>/<service> |
新增 |
| AppWorkload | App Hub | workload_key、env、runtime_mode、gitops_path |
PK <service_key>@<env>/<workload> |
新增;runtime_mode 复用 [PE] 5.1 mode |
| Environment | App Hub | shared / sit / uat / prod(+ CMDB 的 unknown) |
枚举 | 已有:[PE] 4.1;CMDB scope CHECK |
| WorkloadBinding | App Hub | binding_source、match_state |
PK (workload_key, canonical_id) |
新增;match_state 复用 [CMDB] 6 declaration 维度 |
| CostLineItem | FinOps | billed_cost、currency、charge_period、cost_role、source |
FK CloudAccount;可选 FK Resource | 扩展:cloud_vendor_costs;FINOPS-501 FOCUS(规划) |
| CostAllocation | FinOps | bucket、app_key、env、allocated_cost、rule_ref |
FK CostLineItem | 扩展:FINOPS-601/603(规划) |
| ReconcileRun | 对账 | kind、gitops_sha、iac_sha、toolkit_sha、outcome、receipt |
PK run_id |
扩展:[IAM] 10 采集回执与 ingestion receipt 分开 |
| ExportTarget | 输出 | adapter、target_ref、last_export_run |
PK (adapter, target_ref) |
新增 |
2.3 主键与标识规则
- 资源身份只认 CMDB 的
canonical_id。它由cmdb_observations_v1.py根据 provider/account/project/kind/native ID 派生,名称和 IP 都不是身份([CMDB] 4.1、[IAM] 10)。 - GitOps 侧的
resource_key沿用 Toolkitresource-aggregation-model.md的写法<project>/<env>/<provider>/<declaration>(例:svc.plus/uat/akamai/open-platform)。 - App 侧的键全部用小写加连字符(遵循 [PE] 8.5.2 第 6 条)。
app的值优先沿用现有的labels.workload和ResourceMatrix.resources[].target_domain(如web-saas、ai-workspace、agent-proxy-jp,见 §6 Q02 中关于区域后缀的问题)。 scope记录资源实际归属;采集运行在 Shared 上执行,并不把资源改记为 shared([IAM] 3)。- 跨环境共用的资源用
resource_relations表示,不复制成多条资源([IAM] 3)。
2.4 统一标签规范(新增,需评审)
| 标签 | 含义 | 取值规则 | 与现有字段的映射 |
|---|---|---|---|
app |
业务应用(App Hub 顶层) | ^[a-z0-9-]+$;必须在 App 目录中登记 |
现有 GCP labels.workload;VPS tags[0](待核验是否稳定) |
env |
生命周期范围 | shared / sit / uat / prod;无法确认的资源由 CMDB 记为 unknown,不能由人打标签 |
GCP labels.environment;Vector env/environment;CMDB scope |
owner |
责任团队 / 负责人组(不写个人邮箱) | 团队标识,如 platform、billing |
冲突:现有 owner: platform-ops-toolkit 表示管理工具,建议改为 managed_by(FINOPS-003 已有该键);见 §6 Q01 |
tier |
应用架构层 | client / edge / control / compute / data / platform |
对应产品页“端-边-控-算-数”五层([GM] 第四章)加 Shared 平台层;与 GlobalMeshPools 的 tier: out-of-band-network 冲突,见 §6 Q03 |
规则:
- 标签写在 GitOps 声明的
labels(GCP)或 provider 适配后的等价字段上,由 IaC renderer 下发到云资源。provider 不支持键值标签时,IaC adapter 负责编码,具体方式待核验。 - CMDB 观察到的标签以
source=provider_api写入resource_labels,声明中的标签以source=gitops_declared写入;两者不一致时记为drifted。 - 缺少
app或env的资源进入“未归属”(§3.3),不按名称猜测。按规则推断的结果必须标source=inferred_rule并单独展示。 - 本表只是标签规范的草案,不替代 FINOPS-003。两套键如何合并,见 §6 Q01。
2.5 GitOps 侧示例 YAML(草案)
格式遵循 gitops/docs/gitops-declaration-standard.md:单文档、apiVersion/kind/metadata{name,project,environment}/spec,只用 YAML,不含秘密。拟放在 topology/shared/hub/,先例是 topology/shared/cmdb/;路径待决,见 §6 Q12。
# topology/shared/hub/app-catalog.yaml(草案,新增 kind)
apiVersion: gitops.svc.plus/v1alpha1
kind: ApplicationCatalog
metadata:
name: svc-plus-apps
project: svc.plus
environment: shared
spec:
label_keys: [app, env, owner, tier]
apps:
- app: web-saas
owner: platform # 团队标识,非个人
lifecycle: persistent
services:
- service: console
tier: edge
- service: accounts
tier: control
- service: billing
tier: control
- service: postgresql
tier: data
workloads:
- env: uat
runtime_mode: selfhost
resource_refs:
- resources/<project>/uat/gcp/web-saas.yaml
compose_ref: compose/web-saas/docker-compose.yml
- env: prod
runtime_mode: serverless
topology_ref: topology/prod/serverless/runtime-topology.yaml
- app: observability
owner: platform
lifecycle: persistent
services:
- service: observability-server
tier: platform
workloads:
- env: shared
runtime_mode: selfhost
resource_refs:
- resources/<project>/shared/gcp/<observability-declaration>.yaml
# topology/shared/hub/provider-accounts.yaml(草案,新增 kind;ID 一律用占位符)
apiVersion: gitops.svc.plus/v1alpha1
kind: ProviderAccountCatalog
metadata:
name: svc-plus-provider-accounts
project: svc.plus
environment: shared
spec:
providers:
- provider: gcp
provider_class: iaas
terraform_tree: gcp-cloud
accounts:
- account_ref: "<gcp-account-ref>"
project_ref: "<gcp-project-id>"
scope: prod
identity_ref: kv/prod/svc.plus/cloud/gcp/<gcp-project-id>/federation/cmdb-readonly
identity_status: pending_verification
cost_source: pending
- provider: hetzner
provider_class: vps
terraform_tree: null # 当前无 Terraform tree / 无身份合同
accounts: []
coverage_status: pending
reason: account-and-readonly-identity-pending
# 现有 resources/<project>/<env>/gcp/*.yaml 的 labels 扩展(片段)
labels:
app: web-saas # 由现有 workload 迁移
env: prod # 由现有 environment 迁移
owner: platform # 语义改为责任团队
tier: data
managed_by: platform-ops-toolkit # 承接原 owner 的语义(待决)
lifecycle: persistent
CMDBCollectionScope 的扩展点:在 pending_providers 中补上 vultr、hetzner、contabo;给每个 collector 增加 quota 与 labels 两个采集产品。这两项为新增 kind 能力,需要 IaC collector 支持后才能启用。
2.6 CMDB 侧 PG 表结构草案(003_hub_model.sql)
本草案只做追加(additive),沿用 001 的约定:SET LOCAL ROLE cmdb_owner、CHECK 枚举、writer 只拿指定表的 SELECT/INSERT/UPDATE、reader 只读视图、没有 down 迁移。未经评审不能放进 contracts/cmdb-postgresql-v1/migrations/。
BEGIN;
SET LOCAL ROLE cmdb_owner;
SET LOCAL search_path = cmdb, pg_catalog;
CREATE TABLE IF NOT EXISTS provider_catalog (
provider text PRIMARY KEY CHECK (provider ~ '^[a-z0-9-]+$'),
provider_class text NOT NULL CHECK (provider_class IN
('iaas','vps','edge','serverless','managed_database','existing_inventory')),
terraform_tree text,
coverage_status text NOT NULL DEFAULT 'pending' CHECK (coverage_status IN
('enabled','pending','unsupported','pending_verification')),
gitops_ref text NOT NULL,
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS cloud_accounts (
provider text NOT NULL REFERENCES provider_catalog(provider),
account_ref text NOT NULL,
project_ref text NOT NULL DEFAULT '',
scope text NOT NULL CHECK (scope IN ('shared','sit','uat','prod','unknown')),
identity_ref text, -- 逻辑 KV 路径引用,非秘密
identity_status text NOT NULL DEFAULT 'pending' CHECK (identity_status IN
('verified','pending','unsupported','pending_verification')),
cost_source text NOT NULL DEFAULT 'pending' CHECK (cost_source IN
('billing_api','billing_export','price_book','none','pending')),
gitops_ref text NOT NULL,
PRIMARY KEY (provider, account_ref, project_ref)
);
CREATE TABLE IF NOT EXISTS regions (
provider text NOT NULL REFERENCES provider_catalog(provider),
region_ref text NOT NULL,
country_code text,
source text NOT NULL CHECK (source IN ('provider_api','gitops_declared','external_inventory')),
PRIMARY KEY (provider, region_ref)
);
CREATE TABLE IF NOT EXISTS pops (
pop_id text PRIMARY KEY,
provider text NOT NULL,
region_ref text NOT NULL,
role text,
source text NOT NULL CHECK (source IN ('gitops_declared','provider_api','external_inventory')),
gitops_ref text,
FOREIGN KEY (provider, region_ref) REFERENCES regions(provider, region_ref)
);
CREATE TABLE IF NOT EXISTS quotas (
provider text NOT NULL,
account_ref text NOT NULL,
project_ref text NOT NULL DEFAULT '',
region_ref text NOT NULL DEFAULT '',
quota_kind text NOT NULL,
limit_value numeric,
used_value numeric,
run_id text NOT NULL REFERENCES collection_runs(run_id),
observed_at timestamptz NOT NULL,
PRIMARY KEY (provider, account_ref, project_ref, region_ref, quota_kind, run_id),
FOREIGN KEY (provider, account_ref, project_ref)
REFERENCES cloud_accounts(provider, account_ref, project_ref)
);
CREATE TABLE IF NOT EXISTS resource_labels (
canonical_id text NOT NULL REFERENCES resources(canonical_id),
label_key text NOT NULL CHECK (label_key IN ('app','env','owner','tier')),
label_value text NOT NULL CHECK (label_value ~ '^[a-z0-9-]+$'),
source text NOT NULL CHECK (source IN ('provider_api','gitops_declared','inferred_rule')),
run_id text NOT NULL,
observed_at timestamptz NOT NULL,
PRIMARY KEY (canonical_id, label_key, source)
);
CREATE TABLE IF NOT EXISTS apps (
app_key text PRIMARY KEY, -- <project>/<app>
project text NOT NULL,
app text NOT NULL,
owner text NOT NULL,
lifecycle text NOT NULL,
gitops_ref text NOT NULL
);
CREATE TABLE IF NOT EXISTS app_services (
service_key text PRIMARY KEY, -- <app_key>/<service>
app_key text NOT NULL REFERENCES apps(app_key),
service text NOT NULL,
tier text NOT NULL CHECK (tier IN ('client','edge','control','compute','data','platform'))
);
CREATE TABLE IF NOT EXISTS app_workloads (
workload_key text PRIMARY KEY, -- <service_key>@<env>/<workload>
service_key text NOT NULL REFERENCES app_services(service_key),
env text NOT NULL CHECK (env IN ('shared','sit','uat','prod')),
runtime_mode text NOT NULL CHECK (runtime_mode IN ('selfhost','serverless','hybrid','existing')),
gitops_path text NOT NULL
);
CREATE TABLE IF NOT EXISTS reconcile_runs (
run_id text PRIMARY KEY,
kind text NOT NULL CHECK (kind IN ('cloud_hub','app_hub','finops','export')),
gitops_sha text NOT NULL,
iac_sha text,
toolkit_sha text NOT NULL,
inputs_sha256 text NOT NULL,
started_at timestamptz NOT NULL,
completed_at timestamptz,
outcome text NOT NULL CHECK (outcome IN ('running','success','partial','failed')),
receipt jsonb NOT NULL DEFAULT '{}'::jsonb
);
CREATE TABLE IF NOT EXISTS workload_bindings (
workload_key text NOT NULL REFERENCES app_workloads(workload_key),
canonical_id text NOT NULL REFERENCES resources(canonical_id),
binding_source text NOT NULL CHECK (binding_source IN ('label_match','gitops_declared','relation')),
match_state text NOT NULL CHECK (match_state IN ('matched','drifted','declared_only','observed_only')),
reconcile_run_id text NOT NULL REFERENCES reconcile_runs(run_id),
PRIMARY KEY (workload_key, canonical_id)
);
-- 覆盖状态:无身份 provider 显示 pending,不显示为成功零资源
CREATE OR REPLACE VIEW v_cloud_hub_coverage AS
SELECT a.provider, a.account_ref, a.project_ref, a.scope,
CASE WHEN p.coverage_status <> 'enabled' OR a.identity_status <> 'verified' THEN 'pending'
WHEN lr.outcome IS NULL THEN 'never_observed'
WHEN lr.outcome = 'failed' THEN 'failed'
WHEN lr.outcome = 'partial' OR NOT lr.scope_complete THEN 'partial'
WHEN lr.completed_at < now() - interval '15 minutes' THEN 'stale'
ELSE 'complete' END AS coverage,
lr.completed_at AS last_complete_at
FROM cloud_accounts a
JOIN provider_catalog p USING (provider)
LEFT JOIN LATERAL (
SELECT outcome, scope_complete, completed_at FROM collection_runs c
WHERE c.account_ref = a.account_ref AND c.project_ref = a.project_ref
ORDER BY started_at DESC LIMIT 1) lr ON true;
CREATE OR REPLACE VIEW v_unattributed_resources AS
SELECT r.canonical_id, r.provider, r.account_ref, r.project_ref, r.scope, r.resource_kind,
CASE WHEN NOT EXISTS (SELECT 1 FROM resource_labels l
WHERE l.canonical_id = r.canonical_id AND l.label_key = 'app'
AND l.source <> 'inferred_rule') THEN 'untagged'
ELSE 'unknown_app' END AS reason
FROM resources r
WHERE r.lifecycle = 'present'
AND NOT EXISTS (SELECT 1 FROM workload_bindings b
WHERE b.canonical_id = r.canonical_id AND b.match_state = 'matched');
INSERT INTO schema_migrations (version, description)
VALUES ('003', 'Hub model: providers, accounts, regions, PoPs, quotas, labels, apps, bindings')
ON CONFLICT (version) DO NOTHING;
COMMIT;
授权沿用 001:cmdb_writer 对新表有 SELECT/INSERT/UPDATE;cmdb_reader 只读 v_cloud_hub_coverage、v_unattributed_resources 以及后续的 App/FinOps 视图;cmdb_auditor 读 reconcile_runs 的脱敏视图([IAM] 9.4)。
FinOps 表(cost_line_items、cost_allocations)放在哪个数据库是待决项(§6 Q07),这里只列字段要点:
cost_line_items:provider, account_ref, project_ref, charge_period_start/end, service_name, sku, native_resource_id NULL, region, billed_cost numeric(20,6), effective_cost numeric(20,6), currency, cost_role ∈ {authoritative, adjustment, estimated}, source ∈ {billing_api, billing_export, price_book}, source_run_id, tags jsonb。成本角色引用 FINOPS-601;金额用 numeric,不用现有cloud_vendor_costs.cost_amount的DOUBLE PRECISION(见 Q08)。cost_allocations:line_id, bucket ∈ {attributed, untagged, unknown_resource, shared_platform, unallocated, pending_provider}, app_key NULL, env NULL, allocated_cost, rule_ref, reconcile_run_id。bucket 前五类对齐 FINOPS-603;pending_provider是新增,用来表示“无身份/无账单来源”,与“已知为 0”区分。
3. 三个视图
3.1 Cloud Hub
要回答的问题:我们在哪些 provider/account/region 有资源?每个范围的采集是否完整、新鲜?配额还剩多少?PoP 的实测状态如何?哪些 provider 仍是 pending?
数据来源:
| 层 | 来源 | 状态 |
|---|---|---|
| provider/account 目录 | GitOps ProviderAccountCatalog(新增)+ iac/capabilities.yaml + CMDBCollectionScope |
前者新增;后两者已有 |
| region/network/resource | CMDB resources,由 IaC collector 经 cmdb.observations.v1 产生,Playbooks render_ingest_sql.py 入库 |
GCP compute/cloudrun 已实现(UAT 按需);其余 provider 规划 |
| Terraform 受管范围 | IaC cmdb.json(cmdb.v1)+ state key([PE] 5.3) |
已有;不直接读 tfstate |
| existing 节点 | external_inventory([CMDB] 5,ULightHost) |
已有合同 |
| 配额 | provider 配额 API | 新增,collector 未实现 |
| PoP 声明 | topology/<env>/selfhost/global-mesh-pools.yaml |
已有声明(PROD 41 / UAT 17 个 region) |
| PoP 实测 | Observability:Blackbox Exporter 探测指标进 VictoriaMetrics | 探针组件已有;PoP 探测目标与 app 标签为新增 |
计算/对账逻辑:
- 覆盖状态按
v_cloud_hub_coverage计算:provider 未启用或身份未 verified →pending;最近一次 run 为 failed/partial → 照实展示;完整成功但超过 freshness([CMDB] 6:15 分钟)→stale。 - 资源计数只统计
lifecycle='present'的资源(002已在v_provider_summary中这样做)。Compute 计实例数,Serverless 计服务数,revision 不计入([CMDB] 5)。 - 删除只能走
missing_candidate → deletion_confirmed([CMDB] 6),对账 job 不能直接删除。 - 三方差异:同时在 GitOps 声明、
cmdb.json/state、provider 观察中出现 →matched;只在声明中 →declared_only;只在观察中 →observed_only;属性不一致 →drifted([CMDB] 6,写回desired_resources.observed_match)。 - “上次同步”显示
max(collection_runs.completed_at),按 provider 分别展示,不能用一个全局时间戳。
与产品页现有模块的对应:
| 产品页模块 | 映射 | 差距 |
|---|---|---|
| VPS 能力与可用区交叉矩阵(Linode/Hetzner/UCloud/Contabo/Vultr × 7 维) | provider_catalog + 能力声明(静态、desired 层) |
能力分值目前来自 global-mesh node_capabilities 默认值,不是观察值;需要标成“声明/营销数据” |
| VPS 算力 PoP 地图 | pops + 探测指标 |
48 vs 41 待核验;Hetzner/Contabo/Vultr 无身份 → 应显示 pending |
| SaaS 零信任网格 | resource_relations(mesh peer)+ vpn-overlay/ 声明 |
关系采集为新增 |
| “上次同步 2026-09-14 16:30” | 改为按 provider 显示的 last_complete_at |
当前值来自静态数据(§1.4 第 5 条) |
3.2 App Hub
要回答的问题:某个 app 由哪些 service/workload 组成?每个环境跑在哪些云资源上?声明与实际是否一致?健康状况如何?哪些资源不属于任何 app?
数据来源:GitOps ApplicationCatalog(新增)、resources/ 中的 labels、topology/<env>/<mode>/runtime-topology.yaml(EdgeRoutingConfig)、compose/ + .doco-cd.*.yml(当前实际部署路径;services/ 是未启用的 K8s/Flux 路线,见 services/README.md)、releases/(SelfhostRelease)、CMDB resources/resource_labels/resource_relations、Observability 指标。
计算/对账逻辑:
- 绑定优先级:
gitops_declared(catalog 中的resource_refs解析出resource_key → desired_resources.canonical_id)>label_match(观察到的app+env标签对应唯一 workload)>relation(经resource_relations继承,例如数据盘 → VM)。 - 声明绑定与标签绑定指向不同 workload 时记为
drifted,不自动改写任何一方。 - 健康状态不在 CMDB 内计算。Observability 按
app/env标签聚合(需要先在 Vector 模板中加入app标签,属新增),CMDB 只保存monitoring_status(已有表)。容器 running 不算健康([PE] 6.1)。 - Serverless workload 用产品指标和健康检查回执,不报“缺少主机探针”告警([CMDB] 8)。
- 环境混用的情况照实展示,不以声明为准做“修正”。例如 [PE] 11.9(2026-10-14)记录
web-saas-uat主机读取.env.prod:App Hub 应把这类 workload 标为drifted。
与产品页现有模块的对应:“应用架构拓扑”的五层(端-边-控-算-数)对应 tier,节点是 app_services,连线来自 resource_relations 和 EdgeRoutingConfig 的 upstream。“工程生命周期”对应 releases/ + Toolkit 回执,本模型只给出关联键(app_key、tag/digest、run ID),不重建发布证据链([PE] 10.1)。
3.3 FinOps
要回答的问题:每个 account 本期花了多少?其中多少能归到 app/env?未归属的有多少、为什么?哪些 provider 没有账单来源(pending)?
数据来源:
| 来源 | 适用 provider | 状态 |
|---|---|---|
billing-service cloud_vendor_costs + FinOpsSyncer(AWS CostExplorer / GCP BigQuery / Azure Consumption) |
AWS/GCP/Azure | 源码/CI(该文档注明三云凭据接线待核实,“可能尚未拉到真实数据”);SQL 已通过 PG17 资格检查([PE] 11.11),PROD 尚未执行 |
| Price Book(月租/带宽/IPv4) | Contabo/Vultr 等无账单 API 的 VPS | 规划(FINOPS-304 🔴) |
| 云账单 export(资源级明细) | AWS/GCP | 规划(FINOPS-401~403 演进) |
| Cloudflare / Supabase 用量 | Workers/Pages、Supabase 组织 | 新增;额度口径见 [PE] 7.2.2 |
计算/对账逻辑:
- 账单先落到 CloudAccount 下(
provider, account_ref, project_ref),对不上账号的行进入unknown_resource,不丢弃。 - 有
native_resource_id的行,经resources找到canonical_id,再经workload_bindings(matched)或resource_labels(app, env)归到 app/env →attributed。 - 资源存在但没有
app标签 →untagged;资源属于 Shared 平台(如 observability、vault)→shared_platform,二次分摊规则留待后续决定(FINOPS-602);账单只有账号/服务级、没有资源 ID →unallocated。 - provider 没有身份或没有账单来源 →
pending_provider,金额显示“未知”,不显示 0。 - 总成本只计
authoritative + adjustment;allocated不重复求和;estimated只用于对比(FINOPS-601)。 - Free tier 抵扣、共享额度按 billing account / organization 计([PE] 7.2.2),不能按环境重复计算。
与产品页现有模块的对应:公开页没有 FinOps 模块;[GM] 3.2 的“FinOps 混合多云成本对账”表是方案对比的估算,不是账单数据,不能作为本视图的数据。/panel/finops 未看到,暂不映射。
3.4 三档权限在三个视图中的落地
沿用 [IAM] 2.1/2.4 的 reader/operator/admin,三层分别授权、不继承:
| 能力 | reader | operator | admin |
|---|---|---|---|
| 三个视图只读(含 PROD 明细的脱敏字段) | ✅ 经 cmdb_reader 视图 |
✅ | ✅ |
| 修改 GitOps catalog / 标签 | ❌ | 走 PR 评审,不直接写 CMDB | 走 PR 评审 |
| 触发对账 / 导出 job | ❌ | ✅ 仅 allowlist 中的 Toolkit 入口 | ✅ |
| 维护分摊规则 / Price Book | ❌ | ❌ | ✅ 版本化,需审批(FINOPS-1203 审计) |
| 查看原始账单明细 | ❌(默认只看聚合) | 按 account 授权 | ✅ |
collector 是云账号层 reader + CMDB 服务层 operator 特化(cmdb_writer),不是 OS operator([IAM] 2.4)。输出适配器需要写入目标云的 App Hub,必须单独设置写身份,不能复用 collector 的只读身份(见 Q10)。
3.5 统一状态语义(三视图一致)
| 状态 | 含义 | 展示 |
|---|---|---|
pending |
没有已核验的只读身份或数据来源 | 灰色,带 reason;计数和金额显示“未知”,不显示 0 |
failed / partial |
最近一次采集或对账失败、不完整 | 保留最近一次成功的观察,并标记 |
stale |
超过 freshness TTL | 照常展示,标记过期 |
complete |
范围完整,数据新鲜 | 正常展示 |
| 已实现 / 规划 / 待验收 | 功能本身的交付状态(源自 [PE] 1.3) | 在视图页脚或功能清单中单独标注,不与资源状态混在一起 |
4. 对账与同步流程
flowchart LR
subgraph SRC[事实输入]
G[GitOps 固定 SHA<br/>catalog / resources / topology / labels]
S[IaC cmdb.json<br/>Terraform 受管范围]
P[Provider 只读 API<br/>cmdb.observations.v1]
X[external_inventory<br/>带 TTL]
B[账单 API / export / Price Book]
M[Observability 指标<br/>按 app/env 聚合]
end
G --> R[Toolkit 对账 job<br/>固定 owner SHA]
S --> R
P --> I[Playbooks 入库<br/>render_ingest_sql.py] --> DB[(Shared CMDB<br/>schema cmdb)]
X --> I
R --> DB
B --> F[成本归属<br/>落点待决] --> DB
M -.健康摘要.-> DB
DB --> V1[Cloud Hub 视图]
DB --> V2[App Hub 视图]
DB --> V3[FinOps 视图]
DB --> E[输出适配器<br/>单向导出]
E --> GA[各云原生 App Hub / 资源中心]
GA --- NB[禁止回读为事实]
sequenceDiagram
participant T as Toolkit 对账 job
participant G as GitOps@SHA
participant C as CMDB (cmdb_writer)
participant O as 输出适配器
T->>G: 读取 catalog / resources / labels(缺声明即失败)
T->>C: 读取最近完整 collection_runs 与 resources
T->>T: 计算 desired↔observed↔state 匹配与 bindings
T->>C: 事务写入 reconcile_runs / workload_bindings / desired_resources.observed_match
T->>C: 写入成本归属(若该阶段启用)
T-->>T: 生成脱敏 receipt(SHA、输入 hash、计数)
O->>C: 只读 matched 绑定
O->>O: 渲染目标云 App Hub 结构(不含秘密)
O-->>C: 写 export receipt(成功/失败),不回写资源事实
对账规则:
- 输入固定:GitOps/IaC/Playbooks/Toolkit 都使用固定 SHA;缺少声明、SHA 不匹配、目标被 inputs 覆盖时直接失败([PIPE] 4.1、[PE] 2 原则 6)。
- 只用完整范围:对账只消费
scope_complete = true的 run;partial 或 failed 范围保留旧绑定,并标记stale([CMDB] 4.2)。 - 幂等:同一
inputs_sha256重放结果不变;旧 run 不能覆盖新 run([CMDB] 4.2、[IAM] 10)。 - 单向:输出适配器只读 CMDB;目标云 App Hub 里的人工改动不回流,下次导出会被覆盖或报告冲突(导出语义待决)。
- 禁止副作用:对账 job 不执行 Terraform apply、DNS、SSH、探针安装,也不加载 backend 凭据([IAM] 4.2)。
- 回执分离:采集 receipt、入库 receipt、对账 receipt、导出 receipt 各自独立,用 correlation ID 关联([IAM] 10)。
- 调度:在 Toolkit 中新增一个 workflow,或复用 [CMDB] 9 拟议的 DAG
validate-scope → collect → normalize-and-commit → verify-receipt → publish,在其后追加reconcile → export;Toolkit 现有 workflow 目录中没有对账或采集入口(新增)。
5. 总对照表
| # | 项 | 分类 | 出处 | 实现状态 |
|---|---|---|---|---|
| 1 | 四仓职责与“GitOps 声明 / IaC state / CMDB 事实”分离 | 已有 | [PE] 3.1、3.3 | 已有合同 |
| 2 | desired vs observed 分层,declared_only 不得显示 running |
已有 | [CMDB] 6;Toolkit docs/resource-aggregation-model.md |
源码/文档 |
| 3 | CMDB PG schema cmdb(resources、observations、runs、relations、desired、monitoring) |
已有 | [CMDB] 4.1;playbooks/contracts/cmdb-postgresql-v1/001、002 |
已实现(UAT 按需,[CMDB] 12);定时采集未启用 |
| 4 | 资源唯一键 (provider, account_ref, project_ref, resource_kind, native_resource_id) |
已有 | [CMDB] 4.1;[IAM] 10 | 已实现(schema) |
| 5 | cmdb.observations.v1 信封与 GCP collector |
已有 | [IAM] 5、13.1;iac_modules/.../cmdb-observations-contract.md |
源码;GCP 已实现(UAT 按需) |
| 6 | 六状态维度(provider_state / freshness / health / declaration / monitoring / lifecycle) | 已有 | [CMDB] 6 | 部分进入 schema;health 维度待实现 |
| 7 | 无身份 provider 保持 pending,不当成功零资源 | 已有 | [VP] 末段;[IAM] 10;CMDBCollectionScope.pending_providers |
已在声明中;视图未实现 |
| 8 | 三档权限 reader/operator/admin 与三层授权 | 已有 | [IAM] 2.1、2.4 | 规划(proposed-not-applied) |
| 9 | CMDB DB principal(reader/writer/migrator/auditor/backup) | 已有 | [IAM] 9.4;[CMDB] 4;001 GRANT |
已实现(schema 授权);Vault live 待验收 |
| 10 | CMDB Vault 路径 kv/<scope>/svc.plus/services/observability/database/cmdb/<principal> |
已有 | [VP];[IAM] 7.3 | 规划;scope 存在冲突(Q04) |
| 11 | 云只读身份路径 kv/<scope>/svc.plus/cloud/<provider>/<account>/{federation,api}/cmdb-readonly |
已有 | [VP];[IAM] 7.2 | 规划 |
| 12 | Terraform state key 维度 | 已有 | [PE] 5.3;[PIPE] 7.1 | 已有合同 |
| 13 | cmdb.json(cmdb.v1)作为 IaC → Playbooks 资源事实 |
已有 | [PE] 5.2;iac_modules/.../cmdb-inventory-contract.md |
已实现([PE] 11.11 PROD 资源 CMDB 已发布) |
| 14 | existing / external_inventory 带 TTL | 已有 | [PE] 5.4;[CMDB] 5 | 已有合同 |
| 15 | Grafana cmdb_postgres datasource 与只读视图 |
已有 | [CMDB] 7;Playbooks README | 已实现(UAT 6 面板);PROD 待验收 |
| 16 | Provider 目录(provider_catalog) | 扩展 | [PIPE] 1.2;gitops/iac/capabilities.yaml |
规划 |
| 17 | CloudAccount 目录 + identity_status + cost_source | 扩展 | [PE] 4.2;[IAM] 3 | 规划 |
| 18 | Region 实体 | 扩展 | collection_runs.region_ref、resources.region |
规划 |
| 19 | PoP 实体(来自 GlobalMeshPools 声明) | 扩展 | topology/*/selfhost/global-mesh-pools.yaml;[GM] 第一、二章 |
声明已有;实测待核验 |
| 20 | Network 作为 resource_kind + contains 关系 | 新增 | — | 规划 |
| 21 | 配额(quotas)实体与采集 | 新增 | — | 规划 |
| 22 | 统一标签 app/env/owner/tier | 新增 | 部分键已存在:GCP labels.workload/environment/owner;FINOPS-003(billing-service) |
规划;与现有键冲突(Q01–Q03) |
| 23 | resource_labels 表(观察 vs 声明标签) | 新增 | — | 规划 |
| 24 | App / Service / Workload 实体与 ApplicationCatalog kind | 新增 | 候选名来自 ResourceMatrix.target_domain、[PE] 7.2.1(accounts/billing/console) |
规划 |
| 25 | WorkloadBinding 与 match_state | 扩展 | [CMDB] 6 declaration 维度;desired_resources.observed_match |
规划 |
| 26 | resource_relations 承载拓扑/网格连线 |
扩展 | [CMDB] 4.1 | schema 已有;关系采集待实现 |
| 27 | 对账 job(Toolkit 调度)与 reconcile_runs | 扩展 | [CMDB] 9 拟议 DAG;[IAM] 4.2、10 | 规划;Toolkit 无现成入口 |
| 28 | 输出适配器(单向导出到各云 App Hub / 资源中心) | 新增 | — | 规划 |
| 29 | Observability 按 app 标签聚合健康 | 扩展 | [CMDB] 7 exporter 低基数指标;Vector 模板已有 env/provider 等标签 | 规划(app 标签未下发) |
| 30 | 成本事实表 | 扩展 | billing-service cloud_vendor_costs;[PE] 11.11 Billing #44;FINOPS-501 |
源码/CI;PROD 未执行 |
| 31 | 成本分摊到 app/env 与归属桶 | 扩展 | FINOPS-601/602/603(billing-service 文档) | 规划(🔴 Phase 2) |
| 32 | pending_provider 成本桶(与 0 区分) |
新增 | 由 [VP] pending 原则推出 | 规划 |
| 33 | Price Book 作为 VPS 成本来源 | 扩展 | FINOPS-304 | 规划(🔴) |
| 34 | 免费额度与共享计量口径 | 已有 | [PE] 7.2.2 | 文档基线(2026-10-06 核对) |
| 35 | Hybrid/Selfhost/Serverless mode 作为 workload runtime_mode | 已有 | [PE] 5.1;[ORC] 1 | 已有合同 |
| 36 | Hetzner/Contabo/Vultr 纳入 Cloud Hub 覆盖(pending) | 新增 | 产品页列出;CMDBCollectionScope 未列 |
待核验 |
| 37 | 产品页“上次同步”替换为按 provider 的 last_complete_at | 新增 | — | 规划 |
计数:共 37 项,其中已有 17 项、扩展 11 项、新增 9 项。“已有”只表示有出处,不等于已实现,实现状态见最后一列。
6. 与白皮书冲突或待决问题
| 编号 | 问题 | 冲突来源 | 建议 / 需要谁决定 |
|---|---|---|---|
| Q01 | owner 语义与标签键集合冲突:GitOps 现有 owner: platform-ops-toolkit 表示管理工具;FINOPS-003 定义了 8 个键(含 team/product/service/cost_center/managed_by);群里对齐的是 4 个键 |
resources/**/gcp/*.yaml;billing-service FINOPS-003 |
用户决定:owner 改为责任团队,原值迁到 managed_by;FINOPS-003 是否收敛为 app/env/owner/tier + 可选 cost_center |
| Q02 | app 粒度:agent-proxy-jp/us/sg 应该是一个 app 的不同 workload,还是三个 app |
ResourceMatrix、labels.workload |
建议 app=agent-proxy,区域放在 workload;需用户确认 |
| Q03 | tier 值域与现有 tier: out-of-band-network 冲突 |
GlobalMeshPools.metadata.labels |
建议原值改为 network_role 或另一个键 |
| Q04 | CMDB 存储 scope:CMDBCollectionScope 和 [VP] 用 environment: uat、kv/uat/...;[IAM] 1.3/7.3 的目标是 storage_scope: shared、kv/shared/... |
[VP] vs [IAM] | 按 [IAM] 1.3 以 shared 为目标;迁移步骤遵循 [IAM] 11.1;本模型新增的表跟随同一个库 |
| Q05 | provider 命名未规范化:gcp/gcp-cloud、akamai/akamai-cloud/linode、ucloud/ucloud-global/ulighthost/surfercloud、vultr/vultr-vps 同时存在 |
GitOps 路径、registry、CMDB、IAM schema | 需要一份 provider 别名映射表(registry owner:Toolkit/IaC) |
| Q06 | 环境枚举不一致:Toolkit 聚合合同只允许 sit/uat/prod;CMDB 有 shared/unknown;IAM schema 的 environment 只有 sit/uat/prod;GitOps 有 dev 路径 |
resource-aggregation-model.md、001、schemas/iam-integration.schema.yaml、resources/*/dev/ |
按 [CMDB] 4.1 显式扩展 shared;dev 是否纳入待决 |
| Q07 | 成本事实放在哪里:billing-service 的 cloud_vendor_costs 位于业务库(PROD 数据面,53 表合同的一部分);本方案要求 Shared CMDB 统一对账 |
[PE] 11.11;billing-service NATIVE_FINOPS | 选项 A:成本事实留在 Billing,CMDB 只同步归属所需的聚合;选项 B:成本库独立放在 Shared。需用户与 Billing owner 决定 |
| Q08 | cloud_vendor_costs.cost_amount 是 DOUBLE PRECISION,account_id 混装 AWS account/GCP project/Azure subscription;凭据路径 kv/billing-service 不符合 [PE] 8.5 的 kv/<scope>/<project>/<category> |
billing-service SQL、FINOPS-1201 | 新表用 numeric 和分列;Vault 路径按 8.5 迁移,需另行评审 |
| Q09 | Observability 栈描述不一致:任务背景写 OTel + Prometheus + Loki;仓库是 Victoria* + OTel Collector;产品页写 “Loki/Prom” | observability README;产品页 | 以仓库为准;产品页文案需要更正(待用户确认) |
| Q10 | 输出适配器需要目标云的写权限,与 collector“只读、发现资源不产生管理授权”的原则冲突 | [IAM] 4.2、7.4 | 单独设 export operator 身份和 Vault 记录,逐云评审;是否首期就做导出待决 |
| Q11 | 产品页的 48 PoP、5 厂商、流量数字来自静态默认值;GitOps PROD 声明 41 个 region;global-mesh 仓库另有一套 Supabase schema(vps_providers/mesh_nodes),构成第二事实源 |
global-mesh database/schema.sql、sync_vps_regions.py |
产品页改为读 CMDB 视图;global-mesh 表降级为展示缓存或退役,需用户决定 |
| Q12 | 新增 kind 的目录:topology/shared/hub/ 还是其他位置;services/ 是未启用的 K8s/Flux 路线,App Hub 的 service 来源应以 compose/ + Doco-CD 为准 |
gitops/services/README.md;[PE] 5.1 把 services/ 描述为服务配置 |
GitOps owner 决定;[PE] 5.1 的描述需要对齐 |
| Q13 | [PE] 7.1 和 [ORC] 引用 resource-matrix.json,仓库已改为 topology/uat/hybrid/resource-matrix.yaml,且 AGENTS.md 禁止 JSON |
[PE] 7.1 vs gitops/AGENTS.md |
白皮书下一版需要更正 |
| Q14 | Hetzner/Contabo 不在六个 Terraform provider 之内,CMDBCollectionScope 也没有它们和 Vultr 的 pending 条目 |
[PIPE] 1.2;CMDBCollectionScope |
先作为 pending 登记;是否走 external_inventory 或 Price Book 待决 |
| Q15 | Shared 平台成本(observability/vault/CI)的二次分摊规则 | FINOPS-602 | 首期只展示 shared_platform 桶,不做分摊 |
| Q16 | /panel/cloud-hub、/panel/finops 现有功能未知 |
需要登录 | 需要用户提供截图或说明后再做映射 |
7. 分阶段实施建议
| 阶段 | 交付 | 放行条件 | owner |
|---|---|---|---|
| H0 规范评审 | 本文 + Q01–Q06、Q12 的决议;标签规范 v1;provider 别名表;环境枚举 | 用户签字确认;无账号 ID 或秘密进入文档 | 用户、GitOps |
| H1 Schema 与标签 | GitOps:ApplicationCatalog/ProviderAccountCatalog 的 YAML schema(同时补 schema 校验);现有 GCP labels 迁移(workload → app、environment → env、owner → managed_by + 新 owner);CMDBCollectionScope 补 vultr/hetzner/contabo 的 pending 条目 |
YAML 解析与合同检查通过;渲染无资源替换(label 变更需 plan 确认 0 destroy/replace,[PE] 12.3 停止条件) | GitOps → IaC renderer |
| H2 CMDB 003 迁移 | 003_hub_model.sql + 视图 + 授权;在 UAT/隔离库演练 |
reader 写入被拒、writer DDL 被拒、重放幂等([CMDB] 9.1);备份可恢复 | Playbooks |
| H3 Cloud Hub 闭环 | GCP 标签与配额采集;v_cloud_hub_coverage;产品页改为读视图(只读 API);pending 照实展示 |
同一 run 的 API 计数与视图一致;pending provider 不显示 0 | IaC、Playbooks、Console owner |
| H4 App Hub 闭环 | Toolkit 对账 job;workload_bindings;Vector 模板加 app 标签;拓扑视图 |
声明与观察的差异都有对应状态;健康数据来自 Observability,不由 CMDB 推断 | Toolkit、Playbooks、Observability |
| H5 FinOps 闭环 | 先定 Q07 落点;GCP/AWS 账单行进入 cost_line_items;归属桶;Price Book |
总额与账单一致;pending_provider 单独展示;分摊不重复求和 |
Billing owner、Toolkit |
| H6 输出适配器(可选) | 单向导出到一个目标云的 App Hub | 独立写身份;导出 receipt;不回读 | IaC(云写入)、Toolkit(编排) |
| H7 多云覆盖 | AWS/Linode/UCloud/Cloudflare/Supabase/Vultr adapters([IAM] 11 P4) | 每个 provider 身份逐项核验;不支持的项目照实报告 | IaC |
推进顺序遵循 [PE] 12.2:owner 先落地 → caller 切换 → UAT 验证 → 清理旧路径 → 归并文档。任何一步缺少 receipt 都不进入下一步。H1 之前不改任何云资源;H3 之前,产品页上的静态数字应标注“演示数据”(需用户确认)。
反馈