Labels, Tags, và Resource Organization: Metadata Strategy
Tại sao metadata organization quan trọng
Khi project scales (1000+ resources), không thể manage resources bằng naming conventions alone. Bạn cần queryable, filterable metadata để:
- Cost tracking: Chỉ định cost center per resource
- Access control: Grant permissions dựa trên resource attributes
- Automation: Target resources cho updates/patches based on metadata
- Compliance: Track resource attributes tới data sensitivity/regulatory requirements
- Operations: Route alerts, monitoring queries based on resource type/owner
Common challenge: Chaos khi không có consistent tagging strategy:
Google Cloud project with 500 VMs:
- Some have "env=production" labels
- Some have "environment: prod" labels
- Some have NO environment labels
- Some have "owner" labels
- Many don't have owner labels
Result: Cannot reliably query "all production resources"Labels vs Tags: Critical Distinction
GCP có two completely separate systems cho annotation—very different purposes:
1. Labels (Metadata)
Labels là key-value metadata pairs dùng cho organization, querying, cost allocation:
yaml
Labels on resource:
environment: production
team: backend
cost-center: eng-2024
service: payment-api
data-sensitivity: piiCharacteristics:
- 63-char max per key/value
- Defined directly on each resource (or inherited from parent)
- Not a separate resource (unlike Tags)
- Free to use (no quota)
- Queryable trong GCP Console, APIs, CLI
Usage:
bash
# Filter resources by labels
gcloud compute instances list --filter="labels.environment:production"
# Use in cost allocation
gcloud billing projects describe PROJECT_ID \
--format="value(billingAccountName)" \
| xargs -I {} gcloud billing accounts export-costs --billing-account={}2. Tags (Resource Hierarchy Annotations)
Tags là hierarchical key-value pairs dùng cho conditional policy enforcement:
yaml
Tag bindings on project:
environment: production # inherited by all child resources
compliance-level: high
data-residency: eu-onlyCharacteristics:
- 256-char max per key/value
- Defined at Organization or Project level, inherited by descendants
- Managed as separate resources (Tag Key, Tag Value, Tag Binding)
- Can be protected with IAM (who can attach/modify)
- Expressible ở IAM conditions và Organization Policies
- Require explicit Tag User role để attach
Usage:
bash
# List tag keys
gcloud resource-manager tags keys list
# Create tag key and values
gcloud resource-manager tags keys create "environment"
gcloud resource-manager tags values create "prod" --tag-key="environment"
# Attach tag to project
gcloud resource-manager tags bindings create \
--tag-key="environment" \
--tag-value="prod" \
--parent="projects/PROJECT_ID"
# Use ở IAM conditions
gcloud projects add-iam-policy-binding PROJECT_ID \
--member=group:devs@company.com \
--role=roles/editor \
--condition='resource.matchTag("environment", "staging")'Comparison: Labels vs Tags vs Network Tags
┌─────────────┬───────────────────────────────────────────────────────────┐
│ Attribute │ Labels │ Tags │ Network Tags │
├─────────────┼─────────────────┴─────────────────┴────────────────────┤
│ Purpose │ Organization │ Policy │ Firewall routing │
│ │ Metadata │ enforcement │ (legacy network) │
├─────────────┼─────────────────┴─────────────────┴────────────────────┤
│ Scope │ Per-resource │ Hierarchical │ VM networking only │
│ │ │ (org → project) │ (deprecated) │
├─────────────┼─────────────────┴─────────────────┴────────────────────┤
│ Max length │ 63 chars │ 256 chars │ 63 chars │
├─────────────┼─────────────────┴─────────────────┴────────────────────┤
│ Quota │ None (free) │ None (free) │ 64 tags per VM │
├─────────────┼─────────────────┴─────────────────┴────────────────────┤
│ IAM-aware │ No │ YES (conditions)│ No │
├─────────────┼─────────────────┴─────────────────┴────────────────────┤
│ Cost alloc. │ YES (billing) │ Limited │ No │
├─────────────┼─────────────────┴─────────────────┴────────────────────┤
│ Inheritance │ No (attach per) │ YES (tree) │ No │
└─────────────┴───────────────────────────────────────────────────────────┘Labels Strategy
Label Design
Effective label strategy phải balance:
- Cardinality: Không quá nhiều unique values per key
- Consistency: Same labels across different resource types
- Queryability: Labels phải queryable ở GCP Console + APIs
Good label design:
yaml
# Recommended labels
- environment: [prod, staging, dev] # Low cardinality
- team: [backend, frontend, data]
- service: [api, web, batch]
- cost-center: [eng, sales, ops]
- owner: [alice@company.com, bob@company.com] # HIGH cardinality but queryable
# Bad label design
- timestamp: [2024-01-01, 2024-01-02, ...] # TOO HIGH cardinality
- random-id: [uuid-per-resource] # Not useful for filteringLabels on Different Resource Types
python
# Compute Engine instance
instance_labels = {
"environment": "production",
"team": "backend",
"service": "payment-api",
"cost-center": "eng-2024"
}
create_instance(name="payment-vm", labels=instance_labels)
# Cloud Storage bucket
bucket_labels = {
"environment": "production",
"data-sensitivity": "pii",
"cost-center": "data-2024"
}
create_bucket(name="payment-data", labels=bucket_labels)
# BigQuery dataset
dataset_labels = {
"environment": "production",
"team": "data",
"cost-center": "data-2024"
}
create_dataset(dataset_id="analytics", labels=dataset_labels)Querying by Labels
bash
# List all production resources
gcloud compute instances list --filter="labels.environment=production"
# List resources owned by specific team
gcloud compute instances list --filter="labels.team:backend"
# Complex filter
gcloud compute instances list \
--filter="labels.environment=prod AND labels.team:backend"
# Using Cloud Asset Inventory for cross-resource search
gcloud asset search-all-resources \
--scope=organizations/ORG_ID \
--query="labels.cost-center:eng-2024" \
--format="table(name,assetType)"Tags Strategy (Hierarchical)
Tag Key Design
Tag keys should represent organizational/compliance dimensions:
Good tag keys:
- environment (prod, staging, dev)
- compliance (hipaa, pci, sox)
- data-residency (us, eu, apac)
- business-unit (sales, engineering, finance)
Poor tag keys:
- random-attribute (too ad-hoc)
- temporary (defeats purpose of hierarchy)Tag Value Hierarchy
Organization
├── Tag Key: environment
│ ├── Value: production
│ ├── Value: staging
│ └── Value: development
│
├── Tag Key: compliance
│ ├── Value: hipaa
│ ├── Value: pci
│ └── Value: sox
│
└── Tag Key: data-residency
├── Value: us
├── Value: eu
└── Value: apacConditional IAM Policies with Tags
bash
# Example: Only allow production deployments with prod tag
# Create deny policy: Developer cannot modify prod resources
gcloud iam deny-policies create deny-dev-prod-access \
--location=organizations/ORG_ID \
--rules='
deny {
permissions: [
"compute.instances.delete",
"compute.instances.setMetadata"
]
principals: ["group:developers@company.com"]
deny_rule {
deny_condition {
expression: "resource.matchTag(\"environment\", \"production\")"
}
}
}
'
# Result: Developers cannot delete/modify resources tagged with environment=productionConditional Organization Policies with Tags
yaml
# Organization Policy: Enforce encryption on prod resources only
name: organizations/ORG_ID/policies/compute.cmekResources
spec:
rules:
- enforce: true
condition:
expression: "resource.matchTag('environment', 'production')"
title: "Enforce CMEK for production"
- enforce: false
condition:
expression: "resource.matchTag('environment', 'dev')"
title: "Allow default encryption for dev"Cost Allocation with Labels
Export Billing Data with Labels
python
from google.cloud import bigquery
# BigQuery dataset with exported billing data
client = bigquery.Client()
# Query costs by label
query = """
SELECT
labels.key as label_key,
labels.value as label_value,
SUM(cost) as total_cost,
COUNT(*) as resource_count
FROM `project.billing.gcp_billing_export_v1_XXXXXX`
WHERE DATE(usage_start_time) >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
GROUP BY labels.key, labels.value
ORDER BY total_cost DESC
"""
results = client.query(query).result()
for row in results:
print(f"{row.label_key}={row.label_value}: ${row.total_cost:.2f} ({row.resource_count} resources)")Cost Center Chargeback
python
# Calculate costs per cost-center and charge team
billing_data = query_billing_export(
start_date=start_of_month,
end_date=end_of_month
)
cost_by_center = {}
for row in billing_data:
cost_center = row.labels.get('cost-center')
cost = row.cost
cost_by_center[cost_center] = cost_by_center.get(cost_center, 0) + cost
# Generate invoice per cost center
for cost_center, total_cost in cost_by_center.items():
team = get_team_for_cost_center(cost_center)
send_chargeback_notification(team, total_cost)Anti-Patterns to Avoid
| Anti-pattern | Problem | Solution |
|---|---|---|
| Inconsistent labels | Can't query reliably | Define label schema |
| High-cardinality labels | Exploding label combinations | Use low-cardinality keys |
| Labels instead of Tags for policies | No conditional enforcement | Use Tags for org policies |
| No label governance | Labels become chaos | Enforce via resource creation templates |
| Labels on some resources | Incomplete visibility | Mandatory labels on creation |
| Outdated labels | Stale data, wrong decisions | Refresh labels regularly |
Terraform Label Management
hcl
# Define standard labels
locals {
standard_labels = {
terraform = "true"
managed_by = "terraform"
created_date = timestamp()
}
}
# Merge with resource-specific labels
resource "google_compute_instance" "app" {
name = "app-vm"
labels = merge(
local.standard_labels,
{
environment = var.environment
team = var.team
service = var.service
}
)
}
# Enforce labels via policy
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}
# Validation: Ensure required labels present
variable "required_labels" {
type = list(string)
default = ["environment", "team", "cost-center"]
}
resource "null_resource" "label_validation" {
provisioners "local-exec" {
command = <<-EOT
python3 -c "
labels = ${jsonencode(google_compute_instance.app.labels)}
required = ${jsonencode(var.required_labels)}
missing = [k for k in required if k not in labels]
if missing:
raise ValueError(f'Missing required labels: {missing}')
"
EOT
}
}Label Governance
python
def validate_labels(resource_type, labels):
"""Enforce label schema on resource creation"""
required_labels = {
"environment": ["prod", "staging", "dev"],
"team": ["backend", "frontend", "data", "infra"],
"service": None # Any value allowed
}
# Check required labels present
for required_key, allowed_values in required_labels.items():
if required_key not in labels:
raise ValueError(f"Missing required label: {required_key}")
if allowed_values and labels[required_key] not in allowed_values:
raise ValueError(
f"Invalid value for {required_key}: {labels[required_key]}"
)
return True
# Enforce via GKE admission controller / OPA
# Or Terraform validation blocks