Skip to content

Labels, Tags, và tổ chức tài nguyên: Chiến lược metadata

Vì sao tổ chức metadata quan trọng

Khi project tăng quy mô (1000+ resources), không thể quản lý tài nguyên chỉ bằng quy ước đặt tên. Bạn cần metadata có thể truy vấn, lọc được để:

  • Theo dõi chi phí: Gán cost center cho từng resource
  • Kiểm soát truy cập: Cấp quyền dựa trên thuộc tính resource
  • Tự động hóa: Nhắm mục tiêu resource để update/patch dựa trên metadata
  • Tuân thủ: Theo dõi thuộc tính resource theo yêu cầu phân loại dữ liệu/quy định
  • Vận hành: Chuyển hướng alert, query monitoring dựa trên loại resource/chủ sở hữu

Thách thức phổ biến: Hỗn loạn khi không có chiến lược tagging nhất quán:

Google Cloud project với 500 VM:
- Một số có label "env=production"
- Một số có label "environment: prod"
- Một số KHÔNG có label môi trường
- Một số có label "owner"
- Nhiều cái không có label owner

Kết quả: Không thể truy vấn đáng tin cậy "toàn bộ resource production"

Labels vs Tags: Phân biệt quan trọng

GCP có hai hệ thống hoàn toàn khác nhau cho annotation — mục đích rất khác nhau:

1. Labels (Metadata)

Labels là các cặp key-value metadata dùng cho tổ chức, truy vấn, phân bổ chi phí:

yaml
Labels trên resource:
  environment: production
  team: backend
  cost-center: eng-2024
  service: payment-api
  data-sensitivity: pii

Đặc điểm:

  • Tối đa 63 ký tự cho mỗi key/value
  • Được định nghĩa trực tiếp trên từng resource (hoặc kế thừa từ parent trong một số ngữ cảnh)
  • Không phải resource riêng biệt (khác Tags)
  • Miễn phí sử dụng (không có quota)
  • Có thể truy vấn trong GCP Console, APIs, CLI

Cách dùng:

bash
# Lọc resources theo labels
gcloud compute instances list --filter="labels.environment:production"

# Dùng để phân bổ chi phí
gcloud billing projects describe PROJECT_ID \
  --format="value(billingAccountName)" \
  | xargs -I {} gcloud billing accounts export-costs --billing-account={}

2. Tags (Annotation theo phân cấp)

Tags là cặp key-value theo phân cấp dùng cho ép buộc chính sách có điều kiện:

yaml
Tag bindings trên project:
  environment: production    # được kế thừa bởi tất cả child resources
  compliance-level: high
  data-residency: eu-only

Đặc điểm:

  • Tối đa 256 ký tự cho mỗi key/value
  • Định nghĩa ở cấp Organization hoặc Project, được kế thừa xuống dưới
  • Được quản lý như các resource riêng (Tag Key, Tag Value, Tag Binding)
  • Có thể bảo vệ bằng IAM (ai được phép attach/modify)
  • Có thể dùng trong IAM conditionsOrganization Policies
  • Cần role Tag User rõ ràng để attach

Cách dùng:

bash
# Liệt kê tag keys
gcloud resource-manager tags keys list

# Tạo tag key và value
gcloud resource-manager tags keys create "environment"
gcloud resource-manager tags values create "prod" --tag-key="environment"

# Gắn tag vào project
gcloud resource-manager tags bindings create \
  --tag-key="environment" \
  --tag-value="prod" \
  --parent="projects/PROJECT_ID"

# Dùng trong IAM conditions
gcloud projects add-iam-policy-binding PROJECT_ID \
  --member=group:devs@company.com \
  --role=roles/editor \
  --condition='resource.matchTag("environment", "staging")'

So sánh: Labels vs Tags vs Network Tags

┌─────────────┬───────────────────────────────────────────────────────────┐
│ Thuộc tính  │ Labels          │ Tags            │ Network Tags       │
├─────────────┼─────────────────┴─────────────────┴────────────────────┤
│ Mục đích    │ Metadata tổ chức │ Ép buộc policy  │ Firewall routing   │
│             │                 │                 │ (mạng cũ)          │
├─────────────┼─────────────────┴─────────────────┴────────────────────┤
│ Phạm vi     │ Theo resource   │ Theo phân cấp   │ Chỉ networking VM  │
│             │                 │ (org → project) │ (deprecated)       │
├─────────────┼─────────────────┴─────────────────┴────────────────────┤
│ Độ dài max  │ 63 ký tự        │ 256 ký tự       │ 63 ký tự           │
├─────────────┼─────────────────┴─────────────────┴────────────────────┤
│ Quota       │ Không có (free) │ Không có (free) │ 64 tags/VM         │
├─────────────┼─────────────────┴─────────────────┴────────────────────┤
│ IAM-aware   │ Không           │ CÓ (conditions) │ Không              │
├─────────────┼─────────────────┴─────────────────┴────────────────────┤
│ Phân bổ chi phí │ CÓ (billing) │ Hạn chế         │ Không              │
├─────────────┼─────────────────┴─────────────────┴────────────────────┤
│ Kế thừa     │ Không (gắn từng)│ CÓ (tree)       │ Không              │
└─────────────┴───────────────────────────────────────────────────────────┘

Chiến lược Labels

Thiết kế label

Chiến lược label hiệu quả phải cân bằng:

  • Cardinality: Không quá nhiều giá trị duy nhất cho mỗi key
  • Consistency: Cùng bộ label trên nhiều loại resource
  • Queryability: Label phải truy vấn được trong GCP Console + APIs

Thiết kế label tốt:

yaml
# Labels khuyến nghị
- environment: [prod, staging, dev]  # Cardinality thấp
- team: [backend, frontend, data]
- service: [api, web, batch]
- cost-center: [eng, sales, ops]
- owner: [alice@company.com, bob@company.com]  # Cardinality CAO nhưng vẫn truy vấn được

# Thiết kế label kém
- timestamp: [2024-01-01, 2024-01-02, ...]  # Cardinality QUÁ CAO
- random-id: [uuid-per-resource]  # Không hữu ích cho lọc

Labels trên các loại resource khác nhau

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)

Truy vấn theo labels

bash
# Liệt kê tất cả resource production
gcloud compute instances list --filter="labels.environment=production"

# Liệt kê resource thuộc team cụ thể
gcloud compute instances list --filter="labels.team:backend"

# Bộ lọc phức tạp
gcloud compute instances list \
  --filter="labels.environment=prod AND labels.team:backend"

# Dùng Cloud Asset Inventory để tìm kiếm trên nhiều loại resource
gcloud asset search-all-resources \
  --scope=organizations/ORG_ID \
  --query="labels.cost-center:eng-2024" \
  --format="table(name,assetType)"

Chiến lược Tags (theo phân cấp)

Thiết kế Tag Key

Tag keys nên đại diện cho khía cạnh tổ chức/tuân thủ:

Tag keys tốt:
- environment (prod, staging, dev)
- compliance (hipaa, pci, sox)
- data-residency (us, eu, apac)
- business-unit (sales, engineering, finance)

Tag keys kém:
- random-attribute (quá tùy hứng)
- temporary (phá vỡ mục đích của phân cấp)

Phân cấp Tag Value

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

IAM policy có điều kiện với Tags

bash
# Ví dụ: Chỉ cho phép triển khai production với tag prod

# Tạo deny policy: Developer không được sửa resource prod
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\")"
        }
      }
    }
  '

# Kết quả: Developer không thể xóa/sửa resource gắn environment=production

Organization Policies có điều kiện với Tags

yaml
# Organization Policy: Ép mã hóa trên resource prod בלבד
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"

Phân bổ chi phí với Labels

Xuất dữ liệu billing có labels

python
from google.cloud import bigquery

# BigQuery dataset có dữ liệu billing export
client = bigquery.Client()

# Truy vấn chi phí theo 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)")

Chargeback theo cost center

python
# Tính chi phí theo cost-center và tính chargeback cho 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

# Tạo invoice cho từng 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)

Các anti-pattern cần tránh

Anti-patternVấn đềGiải pháp
Labels không nhất quánKhông thể truy vấn đáng tin cậyĐịnh nghĩa schema label
Labels có cardinality caoTổ hợp label bùng nổDùng key cardinality thấp
Dùng Labels thay Tags cho policyKhông ép buộc theo điều kiện đượcDùng Tags cho org policies
Không có governance cho labelsLabels biến thành hỗn loạnÉp qua template tạo resource
Chỉ gắn labels ở một số resourceThiếu khả năng quan sátBắt buộc gắn labels khi tạo
Labels lỗi thờiDữ liệu cũ, quyết định saiLàm mới labels định kỳ

Quản lý Labels với Terraform

hcl
# Định nghĩa labels chuẩn
locals {
  standard_labels = {
    terraform   = "true"
    managed_by  = "terraform"
    created_date = timestamp()
  }
}

# Gộp với labels riêng của resource
resource "google_compute_instance" "app" {
  name = "app-vm"
  
  labels = merge(
    local.standard_labels,
    {
      environment = var.environment
      team        = var.team
      service     = var.service
    }
  )
}

# Ép labels bằng policy
terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 5.0"
    }
  }
}

# Validation: đảm bảo labels bắt buộc có mặt
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
  }
}

Governance cho labels

python
def validate_labels(resource_type, labels):
    """Ép schema label khi tạo resource"""
    
    required_labels = {
        "environment": ["prod", "staging", "dev"],
        "team": ["backend", "frontend", "data", "infra"],
        "service": None  # Cho phép bất kỳ giá trị nào
    }
    
    # Kiểm tra label bắt buộc
    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

# Ép qua GKE admission controller / OPA
# Hoặc Terraform validation blocks

Tham khảo