資源元資料 (Resource Metadata) 簡介
在大規模的雲端環境中,可視化和控制至關重要。Google Cloud 提供了兩種主要機制來組織和識別資源:Labels (標籤) 和 Tags (標記)。雖然它們聽起來很相似,但用途卻大不相同。專業雲端架構師 (Professional Cloud Architect) 必須設計一致的元資料分類體系 (Taxonomy),以支援帳單、自動化、安全性和合規性。
白話文解釋 Labels and Tags
比喻 1 — 便利貼 vs. 識別證
想像 Label 是你貼在筆電上的便利貼。上面寫著「行銷部財產」或「專案 X 使用」。它非常適合用來盤點庫存和了解誰該付帳。然而,Tag 就像是識別證 (Security Badge)。它不只是描述這個人,實際上還授予了他們進入建築物中某些門(防火牆規則)的權限。
比喻 2 — 超市庫存
Labels 就像超市裡的價格標籤和部門標牌(農產品、乳製品)。它們幫助經理了解哪個部門賺的錢最多。Tags 則像條碼,自動化系統利用條碼來決定某個物品是否被允許通過「快速通道」(網路安全政策)。
比喻 3 — 圖書館系統
Labels 是書脊上的主題類別(歷史、科學),用於組織書架。Tags 則是 RFID 晶片,如果你在未借閱的情況下試圖將書帶走,它就會觸發警報(政策執行)。
附加在資源上的鍵值對 (Key-value pairs),用於組織資源和成本分配。它們在帳單導出中是可見的。
具有命名空間的鍵和值,提供了一種條件式允許或拒絕政策的方法,並在網路安全中被大量使用。
Labels vs. Tags:主要差異
| 功能 | Labels | Tags |
|---|---|---|
| 主要案例 | 帳單、組織、庫存 | IAM 政策、網路防火牆、安全性 |
| 可視化 | 帳單導出、控制台、SDK | Resource Manager、IAM、防火牆 |
| 範圍 | 資源層級 | 組織/專案層級 (具命名空間) |
| 繼承性 | 否 | 是 (具階層性) |
| 格式 | key: value (小寫,特定字元) |
tagKeys/tagValues (具命名空間) |
設計分類體系 (Taxonomy)
成功的標記和標籤策略需要一致的分類體系 (Taxonomy)。常見類別包括:
- 所有權 (Ownership):
owner: marketing、team: data-eng。 - 環境 (Environment):
env: prod、env: sandbox。 - 成本中心 (Cost Center):
cost-center: 12345。 - 應用程式 (Application):
app: checkout-service。 - 合規性 (Compliance):
compliance: hipaa。
大規模執行政策
你不能依賴手動輸入元資料。
- Terraform: 在 IaC 模組中定義 Labels 和 Tags。在 Google Provider 中使用
default_labels將全域標籤套用於所有資源。 - 組織政策 (Organization Policy): 使用「必要標籤 (Required labels)」約束(在可用之處)來防止建立缺少強制性元資料的資源。
- Cloud Asset Inventory: 定期查詢你的資源庫,找出「孤兒」資源或那些標籤不合規的資源。
架構師洞察: 在 PCA 考試中,請記住 Tags(最近推出作為 Network Tags 的繼任者)現在是跨專案管理防火牆政策的首選方式,因為它們受 IAM 管理,並可以沿著資源階層繼承。 ::
Resource Manager Tags:TagKey 與 TagValue 架構
Resource Manager 的 Tags 是一級資源 (first-class resource),存活於 tagKeys/{numeric_id} 與 tagValues/{numeric_id} 命名空間之下,並掛在被建立時所屬的 Organization、Folder 或 Project 節點上。每個 TagKey 在其父節點下是唯一的(例如 123456789012/environment),每個 TagValue 代表一個被允許的值(例如 production、staging)。資源與 tag 的綁定透過 TagBinding 資源完成,後者以完整資源名稱指向目標(//compute.googleapis.com/projects/p/zones/z/instances/i)。
生命週期與 IAM 分離
- 建立 tag keys/values 需要在父節點(Org 或 Folder)擁有
roles/resourcemanager.tagAdmin。 - 將既有 tag 綁定到資源上,則需要對該 tag value 擁有
roles/resourcemanager.tagUser,並對目標資源也擁有tagUser。這種權限切分讓中央平台團隊負責維護分類體系,而應用團隊可以自助綁定。 - Tag bindings 會沿著資源階層向下繼承。掛在 Folder 上的 tag 會套用到底下每一個 Project(以及大多數子資源),除非被覆寫。
gcloud 操作流程
gcloud resource-manager tags keys create environment \
--parent=organizations/123456789012
gcloud resource-manager tags values create production \
--parent=123456789012/environment
gcloud resource-manager tags bindings create \
--tag-value=123456789012/environment/production \
--parent=//cloudresourcemanager.googleapis.com/projects/my-prod-proj
與 labels 不同,你無法透過底層資源的 gcloud ... update 批次修改 tag bindings — tags 是由自己獨立的 API 介面管理。這正是讓它們能安全用於 IAM Conditions 的關鍵:政策可以信任這個 tag 是由具備明確 tagUser 權限的主體所設定,而非任何擁有工作負載編輯權限的人。
由 Tags 驅動的 IAM Conditions
當 Tags 與 IAM Conditions 結合時威力大增。你可以只在目標資源帶有特定 tagValue 時才授予角色,藉此打造屬性式存取控制 (ABAC),而不需要大量增生服務帳號或專案。
範例:只能讀取 production buckets
bindings:
- role: roles/storage.objectViewer
members:
- group:[email protected]
condition:
title: only-prod-buckets
expression: |
resource.matchTag('123456789012/environment', 'production')
resource.matchTag() 這個 CEL 函式會評估繼承下來的 tag bindings,所以 Folder 層級設定的 tag,其下每一個 bucket 都會生效。如果你想釘住一個能在改名後仍然存在的數字 tag id,可以搭配 resource.matchTagId()。
IAM Conditions 對 tags 的判斷是在請求發生時即時進行,所以一旦移除 tag binding,存取權的移除可說是立即生效(仍需扣除幾分鐘的憑證快取 TTL)。這讓 tags 成為時限性存取的絕佳「kill switch」 — 比跨數百個專案輪換 IAM bindings 快得多。
GCP 上常見的 ABAC 模式
- 環境隔離:
env=prod→ 禁止 CI 服務帳號執行storage.objects.delete。 - 資料分級:
data-class=restricted→ 要求主體必須是特權群組,且需從 VPC-SC perimeter 內部存取。 - 成本回收:
cost-center=12345→ 只有該成本中心的成員可以開新執行個體。
Labels 完全做不到這些事。它們不在 IAM Conditions 暴露給 resource CEL 物件的範圍裡。
帳單導出與基於 Labels 的成本分配
GCP 帳單透過標準帳單導出寫入 BigQuery。其 schema 包含一個重複欄位 labels,型別為 STRUCT<key STRING, value STRING>;另外還有一個 system_labels 欄位由 GCP 自動為 GKE clusters、Cloud SQL instances、Compute Engine VMs 等資源填入。
範例:每個應用程式每月成本
SELECT
(SELECT value FROM UNNEST(labels) WHERE key = 'app') AS app,
FORMAT_DATE('%Y-%m', usage_start_time) AS month,
ROUND(SUM(cost), 2) AS cost_usd
FROM `billing.gcp_billing_export_v1_0123ABC`
WHERE _PARTITIONTIME >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 90 DAY)
GROUP BY app, month
ORDER BY month DESC, cost_usd DESC;
注意事項
- Labels 是在用量發生當下被記錄的 — 今天替 VM 重新打 label 不會回溯改寫昨天的帳單列。請在建立資源時就把分類體系搞對。
labels陣列只會包含真正套上去的 labels。沒打 label 的資源會以app IS NULL出現;把這個量值放上 FinOps 儀表板當作「漏洞 (leakage)」指標。- 對於 BigQuery 本身,掛在 query job 上的
labels也會傳遞到帳單導出,這讓你能把 on-demand 查詢成本歸屬到特定儀表板、dbt model 或臨時分析師。
BigQuery 帳單導出的 labels 欄位是不可變的歷史元資料。如果你的 chargeback 模型事後需要修正某個錯誤的 label,必須在上層疊一張對應表 (old_app -> new_app) 並在報表中 join,而不是試圖去 mutate 導出。這是反覆出現的財務爭議來源,PCA 等級的架構師必須事先預判。
GKE 工作負載 Labels 與 System Labels
在 Google Kubernetes Engine 上,cluster 層級的 GCP labels 與 Kubernetes pod labels 是兩個不同的層次,但會彼此配合,讓你能以 namespace、workload 或團隊為單位分配成本。
兩層 labels
- GCP 資源層 labels:掛在 cluster、node pool 以及底層 Compute Engine VMs 上。它們會以 compute SKUs 出現在帳單導出中。
- GKE 成本分配 labels — 在 cluster 上以
--enable-cost-allocation啟用後,GKE 會在帳單列上補上system_labels,例如goog-k8s-cluster-name、goog-k8s-cluster-namespace、goog-k8s-workload-name,即使你從未在 GCP 端設定過這些。
實務設定
gcloud container clusters update prod-cluster \
--enable-cost-allocation \
--region=asia-east1
啟用後,你就可以把單一 bin-packed node pool 的帳單拆給共享它的多個工作負載:
SELECT
(SELECT value FROM UNNEST(system_labels) WHERE key = 'goog-k8s-cluster-namespace') AS ns,
(SELECT value FROM UNNEST(system_labels) WHERE key = 'goog-k8s-workload-name') AS workload,
SUM(cost) AS cost_usd
FROM `billing.gcp_billing_export_v1_0123ABC`
WHERE service.description = 'Kubernetes Engine'
GROUP BY ns, workload;
至於 Kubernetes 那一側的 label 衛生,可以用 Policy Controller (Gatekeeper) 的 constraint 強制要求 pod 必須帶有 team、app.kubernetes.io/name 等指定 labels。這能讓 Kubernetes 端與 GCP 端的分類體系維持對齊,讓儀表板數字保持一致。
Cloud Asset Inventory:以元資料進行篩選與稽核
Cloud Asset Inventory (CAI) 是橫跨整個 Organization 中所有資源與 IAM 政策的權威資源庫,並對每一項資產的 labels 與 tagKeys 都建立索引。CAI 是「找出所有缺少 cost-center label 的資源」或「列出所有帶 env=prod 但沒有 data-class tag 的 VM」這類查詢的正確工具。
搜尋與導出
# 找出整個組織內缺少 cost-center label 的資源
gcloud asset search-all-resources \
--scope=organizations/123456789012 \
--query="NOT labels.cost-center:*"
# 找出帶有 prod environment tag 的資源
gcloud asset search-all-resources \
--scope=organizations/123456789012 \
--query="tagKeys:123456789012/environment AND tagValues:production"
持續合規
- Feeds: 設定 CAI feed 寫入 Pub/Sub topic,使資源的 labels 或 tag bindings 一旦變更便即時發出事件。把它接到 Cloud Function 以重新打 label 或發出警示。
- 導出到 BigQuery: 每日匯出完整資產清單,能讓你把資源庫與帳單做 join,找出有花錢但缺少必要元資料的資源。
- Saved queries: 為每條合規規則(缺少 label、錯誤 tag、孤兒服務帳號)建立
--query=...片段函式庫,並在排程的 GitHub Actions 中執行。
CAI 是唯一能跨服務一致地查詢元資料的 API — gcloud compute instances list --filter 只能看到 Compute Engine。
Label 與 Tag 的配額與格式限制
Labels 與 tags 都有硬性限制,常讓第一次設計分類體系的團隊吃驚。在 PCA 考試裡熟記這些數字,可以避免你設計出在規模化後悄悄崩潰的策略。
Labels
- 大多數服務 每個資源最多 64 個 labels。
- Key:1–63 字元,小寫字母、數字、底線或破折號;必須以小寫字母開頭。
- Value:0–63 字元,字元集相同。
- 不允許大寫與國際字元 —
Owner: Alice是無效的;改用owner: alice。
Tags
- 每個 organization 最多 1,000 個 tag keys。
- 每個 tag key 最多 1,000 個 tag values。
- 每個資源最多 50 個 tag bindings(若繼承下來也算進這個上限;請以最新文件為準)。
- Tag key 短名:1–63 字元;允許大寫、底線、句點、破折號 — 字元集比 labels 寬鬆許多。
常見考題陷阱:考生以為 labels 可以裝下豐富的值,例如電子郵件位址或像 v1.2.3 這樣的版本字串。在 label value 裡 v1.2.3 的句點是無效字元,電子郵件裡的 @ 也是。請改用底線 (v1_2_3),或把這類資訊改放到 annotation 或 description 欄位。Tags 在短名中是允許句點的,但 tag values 一樣有受限的字元集。
一套能撐過稽核的成本分配策略
可被辯護的成本分配模型,建立在一組小而強制的 labels 上,並一致地套用於每一個會產生費用的資源。
最小可行 label 集合
| Label key | 基數 | 真值來源 |
|---|---|---|
cost-center |
~50 個值 | 財務 ERP |
business-unit |
~10 個值 | 人資/組織圖 |
app |
~500 個值 | 服務目錄 / CMDB |
env |
4–5 個值 | 部署管線 |
data-class |
3–4 個值 | 資料治理團隊 |
數字如何流動
- 每個資源都由 Terraform 在建立時依模組預設打上 label。
- 帳單導出每小時串流到 BigQuery。
- 排程查詢把
cost按cost-center拆分,並與財務 ERP 對表取得 chargeback 代碼。 - Looker Studio(或 Looker)依事業單位呈現 chargeback 儀表板。
- 未打 label 的花費獨立成「漏洞 (leakage)」項目,目標控制在總花費的 < 2%。
為何不直接用專案?
專案本身已經會隔離帳單,但你的應用程式與成本中心通常遠多於可以舒適建模為專案的數量。Labels 讓你能在專案內部切片(同一個專案多個 microservices、多個團隊共用一個 GKE cluster),同時讓專案維持為 IAM 與配額的單位。兩者是互補關係。
Label 衛生政策:讓必要欄位真的被執行
Label 策略的好壞,取決於必要欄位的執行強度。你應該疊加多層執行手段,而不是只挑一種。
多層執行
- IaC 預設值: 在 Terraform 的
googleprovider 上設定default_labels,讓 Terraform 建立的每個資源都繼承cost-center、env、app。把這些變數設為必填(type = string,不給 default)。 - 模組驗證: 在共用 Terraform 模組裡用
validation區塊,在plan階段就拒絕空 label。 - Pre-commit hook: 用
tflint加自訂規則,若 PR 內任一google_*資源缺少必要欄位就讓 PR 失敗。 - 組織政策 (Organization Policy): 在可用之處,用
gcp.resourceLocations與 label 相關約束從一開始就防止未打 label 的資源被建立。 - CAI feed + Cloud Function: 作為非 Terraform 路徑(主控台手動點選、第三方 agent)的最後防線;由 Pub/Sub 觸發的函式把資源加入或移出「不合規」的 labels 群組並通知擁有者。
- 排程稽核: BigQuery 排程查詢把帳單導出與必要欄位清單 join,每週在 Slack 發出 label 覆蓋率最差的團隊報告。
PCA 考試請記住經典執行金字塔:預防 (Org Policy) → 在 IaC 內強制 (Terraform 預設值 + 模組驗證) → 偵測 (CAI feeds + BigQuery 排程查詢) → 補救 (Cloud Function 或 Workflows)。多層防護永遠勝過任何單一控制。
用 Cloud Functions 與 CAI Feeds 自動補救
你可以用事件驅動的補救管線把 label 漂移問題收口。無論缺的是哪一個必要 label,模式都一樣。
參考架構
- 在 Organization 層級建立 Cloud Asset Inventory feed,按你關心的 asset types (
compute.googleapis.com/Instance、storage.googleapis.com/Bucket、bigquery.googleapis.com/Dataset) 過濾,並把每次變更發布到 Pub/Sub topic。 - Cloud Function (2nd gen) 訂閱該 topic。對每則訊息:
- 解析 asset payload。
- 檢查必要欄位 (
cost-center、env、app) 是否存在且非空。 - 若缺漏且父專案帶有
default-cost-centerlabel,把該值複製到子資源上。 - 否則透過 Slack/電子郵件通知擁有團隊,並把資源打上
compliance=non-complianttag。
- Dead-letter topic 收容函式無法處理的訊息;由 on-call 工程師每週清空。
程式骨架 (Python)
def remediate(event, context):
asset = json.loads(base64.b64decode(event["data"]))["asset"]
labels = asset["resource"]["data"].get("labels", {})
missing = [k for k in ("cost-center", "env", "app") if k not in labels]
if not missing:
return
project = asset["ancestors"][0]
default = lookup_project_default(project)
if default:
apply_labels(asset["name"], default)
else:
notify_owner(asset, missing)
運維要點
- 函式必須是 idempotent:套上 label 後它會再看到該 asset,所以寫入前要先檢查目前狀態。
- 流量控制:吵雜資源(例如 Dataflow job 大量產生暫存資源)可能淹沒函式。請使用 Pub/Sub flow control,並對函式設定 max-instance 上限。
- 把每一次 label 變更連同函式的主體一起記錄到稽核 log。
BigQuery 視圖:統一的 Label 目錄
一張 BigQuery 視圖暴露「每個資源及其 labels,並與帳單與資源庫 join 起來」,會成為 FinOps、安全與平台團隊的主力工作面。建一次,到處查。
把那一段「按 key 取出 label」的 unnest 子查詢包成 SQL UDF,例如 fn.label(labels, 'app')。每位分析師之後只要寫 fn.label(labels, 'team') 而不必到處複製貼上子查詢 — 這會大幅提升 FinOps 報表之間的一致性。
視圖設計
CREATE OR REPLACE VIEW finops.resource_catalog AS
SELECT
r.name AS resource_name,
r.asset_type,
r.ancestors[OFFSET(0)] AS project,
r.labels,
(SELECT value FROM UNNEST(r.labels) WHERE key = 'cost-center') AS cost_center,
(SELECT value FROM UNNEST(r.labels) WHERE key = 'env') AS env,
(SELECT value FROM UNNEST(r.labels) WHERE key = 'app') AS app,
b.last_30d_cost_usd
FROM `cai_export.assets` r
LEFT JOIN (
SELECT
project.id AS project,
(SELECT value FROM UNNEST(labels) WHERE key = 'app') AS app,
ROUND(SUM(cost), 2) AS last_30d_cost_usd
FROM `billing.gcp_billing_export_v1_0123ABC`
WHERE _PARTITIONTIME >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
GROUP BY project, app
) b
ON r.ancestors[OFFSET(0)] = CONCAT('projects/', b.project)
AND (SELECT value FROM UNNEST(r.labels) WHERE key = 'app') = b.app;
為何重要
- 一個 join 取代散落各團隊的十個臨時查詢。
- 新增欄位(例如
data_class)不會破壞下游儀表板,因為底層labels陣列仍保留。 - 視圖層級的 row-level security(使用 BigQuery row access policies)可以把每個事業單位限縮到自己
cost_center的範圍內。 - 同一個視圖被 Looker Studio(成本儀表板)、Looker(chargeback 報表)以及臨時分析共用,確保所有人看到的數字一致。
這個模式 — CAI 導出 + 帳單導出 + 視圖 — 就是 GCP 上資源治理的標準「單一玻璃面板」,非常值得在 PCA 考試前牢記。
常見問題 — 資源元資料
Q1. 我可以使用 Labels 進行安全控管嗎?
不行。Labels 用於組織和帳單。它們沒有與 IAM 鏈接的執行機制。請使用 Tags 或 IAM 條件 (IAM Conditions) 來處理安全性相關的邏輯。
Q2. Labels 會顯示在 BigQuery 帳單導出中嗎?
會。這是它們的強大之處。你可以執行 SQL 查詢,確切了解 app: legacy-portal 每個月花費你多少錢。
Q3. Labels 的限制是多少?
大多數 GCP 資源允許每個資源最多有 64 個 Labels。
Q4. 什麼是 "Network Tags"?
Network Tags 是專門針對 Compute Engine VM 和 VPC 防火牆的舊版本標記。雖然仍被廣泛使用,但較新的 Resource Manager Tags 功能更強大,因為它們可以跨不同的資源類型和資料夾運作。
Q5. 我該如何更新 1000 台 VM 上的 Labels?
使用 gcloud compute instances add-labels 命令配合腳本,或者最好是更新你的 Terraform 程式碼並執行 plan/apply,使環境達到理想狀態。