Skip to content

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: pii

Characteristics:

  • 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-only

Characteristics:

  • 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 conditionsOrganization 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 filtering

Labels 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: apac

Conditional 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=production

Conditional 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-patternProblemSolution
Inconsistent labelsCan't query reliablyDefine label schema
High-cardinality labelsExploding label combinationsUse low-cardinality keys
Labels instead of Tags for policiesNo conditional enforcementUse Tags for org policies
No label governanceLabels become chaosEnforce via resource creation templates
Labels on some resourcesIncomplete visibilityMandatory labels on creation
Outdated labelsStale data, wrong decisionsRefresh 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

References