Skip to content

Đặt tên project, định danh, và tự động hóa

Vì sao định danh project là yếu tố quan trọng

Một trong những lỗi thường gặp trong triển khai GCP production là nhầm lẫn hoặc sử dụng sai ba loại định danh khác nhau:

  • Project ID: Định danh duy nhất trên toàn cầu (do người dùng đặt)
  • Project Number: Định danh được gán tự động
  • Project Name: Tên hiển thị (dễ đọc với con người)

Lỗi trong production:

  • Script dùng project number thay vì project ID → automation bị hỏng
  • Giả định sai về định dạng project ID → validation tên thất bại
  • Đổi tên project → script vẫn tham chiếu tên cũ → lỗi âm thầm
  • Cố tái sử dụng project ID sau khi xóa → chạm quota/soft-delete

Project ID: Định danh chính

Định nghĩa & ràng buộc

Project ID là định danh duy nhất toàn cầu của project trên toàn bộ nền tảng GCP.

Định dạng: 6-30 ký tự
           chữ thường [a-z]
           số [0-9]
           dấu gạch nối [-]
           Phải bắt đầu bằng chữ cái
           Không được kết thúc bằng dấu gạch nối
           Không được chứa "google" hoặc "ssl" (được dành riêng)

Ví dụ hợp lệ:

  • my-webapp-prod
  • backend-api-v2
  • data-pipeline-2024

Ví dụ không hợp lệ:

my_webapp_prod           # không cho phép underscore
myWebappProd             # không cho phép chữ hoa
my-webapp-prod--         # kết thúc bằng dấu gạch nối
-my-webapp-prod          # bắt đầu bằng dấu gạch nối
3my-webapp-prod          # bắt đầu bằng số
my-google-webapp         # chứa "google"

Không thể thay đổi & tính lâu dài

Project ID là không thể thay đổi sau khi tạo — không thể đổi tên, không thể tái sử dụng:

bash
# ❌ Không thể đổi Project ID
gcloud projects update my-project --name="new-name"
# Lệnh này chỉ đổi display name, KHÔNG đổi project ID

# Project ID vẫn là: my-project
# Display name đổi thành: new-name

Điều này có hệ quả lớn trong production:

  1. Tên artifact triển khai phải bám theo yêu cầu định dạng của project ID:

    python
    # ❌ Vấn đề: automation sinh ra project ID không hợp lệ
    def create_project_for_customer(customer_name):
        project_id = f"customer_{customer_name}"  # dấu gạch dưới không được phép!
        # Sẽ thất bại
    
    # ✅ Giải pháp: Làm sạch đầu vào
    import re
    def create_project_for_customer(customer_name):
        # Chuẩn hóa: chữ thường, thay ký tự không phải chữ/số bằng dấu gạch nối
        sanitized = re.sub(r'[^a-z0-9-]', '-', customer_name.lower())
        # Xóa các dấu gạch nối liên tiếp
        sanitized = re.sub(r'-+', '-', sanitized)
        # Xóa dấu gạch nối ở đầu/cuối
        sanitized = sanitized.strip('-')
        
        project_id = f"cust-{sanitized}"
        # Hợp lệ
  2. Chặn tái tạo trong soft-delete:

    T+0: delete_project("my-project")
    T+0 đến T+30 ngày: "my-project" ở trạng thái soft-delete (vẫn tính quota)
    T+30 ngày: Xóa vĩnh viễn, có thể tái sử dụng "my-project"
    
    Vấn đề: Nếu automation tạo lại ngay → thất bại
  3. Quy ước đặt tên phải chặt chẽ: Công ty cần thiết lập chính sách đặt tên project ID ngay từ đầu:

    Mẫu: <team>-<service>-<environment>
    Ví dụ:
    - backend-api-prod
    - backend-api-staging
    - data-pipeline-prod
    - frontend-web-dev

Lộ thông tin qua URL

Project ID xuất hiện ở nhiều nơi có thể nhìn thấy công khai:

# Tên bucket Cloud Storage
gs://project-id-bucket/

# Địa chỉ instance Compute Engine
instance.zone.c.project-id.internal

# URL Cloud Functions
https://region-project-id.cloudfunctions.net/function-name

# ID dataset BigQuery
project-id:dataset_name

# Đường dẫn registry image Docker
gcr.io/project-id/image-name:tag

Hệ quả bảo mật: Project ID không phải secret, nhưng nó tiết lộ cấu trúc tổ chức và cách đặt tên môi trường. Không đưa thông tin nhạy cảm (customer IDs, API keys) vào project ID.

Project Number: Định danh nội bộ

Project number là định danh duy nhất được gán tự động — dùng trong các hệ thống nội bộ của GCP.

json
{
  "projectId": "my-webapp-prod",
  "projectNumber": "123456789012"
}

Đặc điểm:

  • Số 12 chữ số (không bao giờ thay đổi)
  • Được gán tự động khi project được tạo
  • Duy nhất trên toàn GCP (giống project ID)
  • Chủ yếu dùng trong naming của service account

Đặt tên service account

Service account trong GCP được đặt tên:

PROJECT_NUMBER@PROJECT-ID.iam.gserviceaccount.com

Ví dụ:
123456789012@my-webapp-prod.iam.gserviceaccount.com

Hệ quả: Nếu automation dùng project number thay vì project ID trong tham chiếu service account, sẽ bị hỏng:

python
# ❌ Sai: dùng project number
service_account = f"{project_number}@{project_number}.iam.gserviceaccount.com"

# ✅ Đúng: dùng project ID
service_account = f"{project_number}@{project_id}.iam.gserviceaccount.com"

Default service accounts

GCP tự động tạo các default service accounts:

PROJECT_NUMBER-compute@developer.gserviceaccount.com  # Compute Engine default
PROJECT_NUMBER@cloudservices.gserviceaccount.com      # Default cho GCP services

Cảnh báo: Default service accounts thường có role Owner (quá rộng). Best practice trong production:

  • Tắt default service accounts
  • Tạo custom service accounts với quyền tối thiểu
bash
# Lấy default service account
DEFAULT_SA=$(gcloud iam service-accounts list \
  --filter="email~-compute@" \
  --format="value(email)")

# Tắt nó (giữ để tham chiếu, nhưng không dùng được)
gcloud iam service-accounts disable $DEFAULT_SA

# Thay vào đó tạo custom service account
gcloud iam service-accounts create app-sa \
  --display-name="Application Service Account"

Project Name: Chỉ là tên hiển thị

Project name là display name dễ đọc — có thể thay đổi hoàn toàn, không duy nhất.

bash
# Đổi display name (không đổi project ID)
gcloud projects update my-webapp-prod --name="My Web Application - Production"

# Truy vấn project
gcloud projects describe my-webapp-prod --format='value(name)'
# Output: My Web Application - Production

Vấn đề: Script/automation thường giả định project name là duy nhất:

python
# ❌ Vấn đề: Tìm project theo display name
def find_project_by_name(org_id, display_name):
    projects = list_projects(org_id)
    for project in projects:
        if project['name'] == display_name:
            return project
    # Vấn đề: Có thể có nhiều project cùng tên!

# ✅ Đúng: Dùng project ID
def find_project_by_id(project_id):
    return get_project(project_id)

Quy ước đặt tên cho vận hành

Mặc dù project ID không thể đổi, project display name có thể cập nhật để phản ánh trạng thái hiện tại:

bash
# T+0: Giai đoạn phát triển
gcloud projects update backend-api-staging \
  --name="Backend API - Staging (Team Lead: Alice)"

# T+6 tháng: Team thay đổi
gcloud projects update backend-api-staging \
  --name="Backend API - Staging (Team Lead: Bob)"

Best practices cho tự động hóa

Mẫu 1: Registry project tập trung

Duy trì nguồn chân lý cho metadata project:

yaml
# projects.yaml
projects:
  - id: backend-api-prod
    name: "Backend API - Production"
    folder_id: folders/1234567890
    environment: production
    team: backend
  
  - id: backend-api-staging
    name: "Backend API - Staging"
    folder_id: folders/1234567890
    environment: staging
    team: backend
  
  - id: backend-api-dev
    name: "Backend API - Development"
    folder_id: folders/9876543210
    environment: development
    team: backend
python
import yaml

def load_project_registry(filepath):
    with open(filepath) as f:
        config = yaml.safe_load(f)
    return {p['id']: p for p in config['projects']}

projects = load_project_registry('projects.yaml')

# Tham chiếu project an toàn
for project_id, config in projects.items():
    print(f"Project: {project_id} ({config['name']})")
    create_resources(project_id=project_id, config=config)

Mẫu 2: Biến môi trường

bash
# .env.prod
GCP_PROJECT_ID=backend-api-prod
GCP_PROJECT_NUMBER=123456789012
GCP_REGION=us-central1

# application.sh
#!/bin/bash
source .env.prod

gcloud config set project $GCP_PROJECT_ID

# Lúc này mọi lệnh gcloud đều dùng đúng project ID
gcloud compute instances list

Tốt hơn: Dùng Terraform

hcl
variable "environment" {
  default = "prod"
}

locals {
  project_config = {
    prod = {
      project_id = "backend-api-prod"
      region     = "us-central1"
    }
    staging = {
      project_id = "backend-api-staging"
      region     = "us-central1"
    }
  }
}

provider "google" {
  project = local.project_config[var.environment].project_id
  region  = local.project_config[var.environment].region
}

Mẫu 3: Tạo project bằng chương trình

python
def sanitize_project_id(user_input, prefix="proj"):
    """Chuyển đầu vào người dùng thành project ID hợp lệ của GCP"""
    # Chuyển về chữ thường
    clean = user_input.lower()
    
    # Thay khoảng trắng, underscore bằng dấu gạch nối
    clean = re.sub(r'[^a-z0-9-]', '-', clean)
    
    # Xóa các dấu gạch nối liên tiếp
    clean = re.sub(r'-+', '-', clean)
    
    # Xóa dấu gạch nối ở đầu/cuối
    clean = clean.strip('-')
    
    # Thêm prefix
    project_id = f"{prefix}-{clean}"
    
    # Đảm bảo không vượt giới hạn độ dài
    project_id = project_id[:30]
    
    return project_id

# Kiểm thử
assert sanitize_project_id("My_Web App!") == "proj-my-web-app"
assert sanitize_project_id("Customer Name 2024") == "proj-customer-name-2024"

Mẫu 4: Phát hiện trùng project ID

python
def is_project_id_available(project_id, organization_id):
    """Kiểm tra project ID có sẵn hay không (không nằm trong soft-delete)"""
    try:
        resource_manager_client.get_project(project_id)
        # Project tồn tại và có thể truy cập
        return False
    except google.api_core.exceptions.NotFound:
        # Project không tồn tại - kiểm tra thêm xem có nằm trong soft-delete không
        pass
    
    # Truy vấn Cloud Asset Inventory để tìm project đã xóa
    query = f'''
    resource.type = "cloudresourcemanager.googleapis.com/Project"
    AND name = "projects/{project_id}"
    '''
    
    # Nếu tìm thấy trong asset inventory → đang soft-delete
    # Nếu không tìm thấy → có thể dùng
    return check_asset_inventory_for_project(organization_id, project_id)

def safe_create_project(project_id, organization_id, retry_count=3):
    """Tạo project với kiểm tra trùng lặp"""
    for attempt in range(retry_count):
        if not is_project_id_available(project_id, organization_id):
            raise ValueError(f"Project ID {project_id} không khả dụng")
        
        try:
            return create_project(project_id, organization_id)
        except google.api_core.exceptions.AlreadyExists:
            if attempt < retry_count - 1:
                time.sleep(5 * (2 ** attempt))  # exponential backoff
            else:
                raise

Truy vấn project đúng cách

bash
# ❌ Vấn đề: phân biệt hoa thường
gcloud projects describe MY-WEBAPP-PROD
# Error: Project 'MY-WEBAPP-PROD' not found
# (Project ID thực tế là my-webapp-prod)

# ✅ Đúng: dùng project ID chữ thường
gcloud projects describe my-webapp-prod

# ✅ Đúng: dùng --project flag
gcloud compute instances list --project=my-webapp-prod

# ✅ Truy vấn theo display name (nếu cần)
gcloud projects list --filter="name:*Production*"

# ✅ Truy vấn theo folder
gcloud projects list --filter="parent.id:folders/FOLDER_ID"

Xóa project & tái sử dụng

Dòng thời gian soft-delete

T+0: gcloud projects delete my-project
     → Trạng thái: DELETE_REQUESTED
     → Có thể khôi phục bằng: gcloud projects undelete

T+7 ngày: Project chuyển sang "deleted state"
     → Không thể khôi phục nữa
     → Vẫn tính vào quota

T+30 ngày: Xóa vĩnh viễn
     → Project ID trở thành có thể dùng lại
     → Quota được giải phóng

Kịch bản production: Blue-Green deployments

python
def create_temporary_project(base_name, lifetime_days=1):
    """Tạo project tạm cho testing, tự xóa sau thời gian sống"""
    import datetime
    
    timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M")
    project_id = f"{base_name}-tmp-{timestamp}"
    
    # Tạo project
    project = create_project(project_id)
    
    # Lên lịch xóa (dùng Cloud Scheduler + Cloud Functions)
    schedule_project_deletion(project_id, lifetime_days)
    
    return project_id

# Cách dùng:
temp_project = create_temporary_project("backend-api")
# Sẽ tự động bị xóa sau 1 ngày

Quản lý state với Terraform

hcl
# Khi dùng Terraform, project IDs được theo dõi trong state file
resource "google_project" "prod" {
  name       = "My Application - Production"
  project_id = "my-app-prod"
}

# Xuất định danh project
output "project_id" {
  value = google_project.prod.project_id
}

output "project_number" {
  value = google_project.prod.number
}

# terraform refresh đảm bảo state chính xác
terraform refresh
terraform output project_id
# Output: my-app-prod

Các anti-pattern thường gặp cần tránh

Anti-patternVấn đềGiải pháp
Dùng project number trong tham chiếu projectAPI yêu cầu project IDLuôn dùng project ID
Giả định project name là duy nhấtTên có thể trùngDùng project ID để truy vấn
Hard-code project IDsKhông portable giữa các môi trườngDùng biến/file config
Không làm sạch đầu vào người dùng cho project IDKý tự không hợp lệ làm hỏng automationThêm validation
Không xử lý cửa sổ soft-deleteTrùng project ID sau khi xóaChờ 30 ngày hoặc dùng ID khác
Đổi tên project trực tiếpLàm hỏng script phụ thuộcLên kế hoạch đặt tên ngay từ đầu

Tham khảo