Đặ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-prodbackend-api-v2data-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:
# ❌ 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:
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ệ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ạiQuy ướ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:tagHệ 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.
{
"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.comHệ quả: Nếu automation dùng project number thay vì project ID trong tham chiếu service account, sẽ bị hỏng:
# ❌ 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 servicesCả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
# 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.
# Đổ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 - ProductionVấn đề: Script/automation thường giả định project name là duy nhất:
# ❌ 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:
# 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:
# 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: backendimport 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
# .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 listTốt hơn: Dùng Terraform
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
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
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:
raiseTruy vấn project đúng cách
# ❌ 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óngKịch bản production: Blue-Green deployments
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àyQuản lý state với Terraform
# 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-prodCác anti-pattern thường gặp cần tránh
| Anti-pattern | Vấn đề | Giải pháp |
|---|---|---|
| Dùng project number trong tham chiếu project | API yêu cầu project ID | Luôn dùng project ID |
| Giả định project name là duy nhất | Tên có thể trùng | Dùng project ID để truy vấn |
| Hard-code project IDs | Không portable giữa các môi trường | Dùng biến/file config |
| Không làm sạch đầu vào người dùng cho project ID | Ký tự không hợp lệ làm hỏng automation | Thêm validation |
| Không xử lý cửa sổ soft-delete | Trùng project ID sau khi xóa | Chờ 30 ngày hoặc dùng ID khác |
| Đổi tên project trực tiếp | Làm hỏng script phụ thuộc | Lên kế hoạch đặt tên ngay từ đầu |