Multi-Cluster GitOps & Policy Controller
Vấn Đề Khi Scale: Một Cluster vs. Nhiều Clusters
Config Sync cho một cluster là đơn giản. Thực tế production thường có nhiều clusters:
- Dev, staging, production clusters (per environment)
- Regional clusters (us-central1, europe-west1, asia-east1)
- Tenant clusters (per team hoặc per customer)
- Specialized clusters (GPU clusters cho ML, standard clusters cho web services)
Mỗi cluster cần riêng một Config Sync RootSync object trỏ đến source of truth. Câu hỏi thiết kế quan trọng: Tổ chức Git repositories thế nào để vừa share common configs, vừa allow per-cluster customization?
Repository Structure Patterns
Pattern 1: Monorepo với Cluster Directories
k8s-configs/
├── clusters/
│ ├── dev-us-central1/ # Configs cho cluster dev
│ │ ├── namespaces/
│ │ └── cluster/
│ ├── staging-us-central1/ # Configs cho cluster staging
│ │ ├── namespaces/
│ │ └── cluster/
│ └── prod-us-central1/ # Configs cho cluster prod
│ ├── namespaces/
│ └── cluster/
└── base/ # Shared base configs
├── namespaces/
└── cluster/Mỗi cluster có RootSync trỏ đến thư mục riêng:
# RootSync trên prod cluster
spec:
git:
dir: clusters/prod-us-central1Nhược điểm: duplicate nhiều configs giữa các environments nếu không dùng Kustomize.
Pattern 2: Kustomize Overlays
k8s-configs/
├── base/ # Base configurations (shared)
│ ├── namespace.yaml
│ ├── network-policy.yaml
│ └── kustomization.yaml
└── overlays/
├── dev/
│ ├── kustomization.yaml # patches cho dev
│ └── patches/
├── staging/
│ ├── kustomization.yaml
│ └── patches/
└── prod/
├── kustomization.yaml
└── patches/Mỗi cluster trỏ đến overlay tương ứng:
# RootSync trên prod cluster
spec:
git:
dir: overlays/prodConfig Sync hydration-controller chạy kustomize build overlays/prod và apply kết quả. Base configs chỉ viết một lần, overlay chỉ contain diffs.
Pattern 3: Multi-Repo (Delegated Authority)
platform-repo/ # Managed by platform team
├── RootSync configs # One per cluster, applied via Config Connector or manual
└── ClusterRoles, CRDs # Cluster-admin level resources
team-a-repo/ # Managed by Team A
├── deployment.yaml
├── service.yaml
└── configmap.yaml
team-b-repo/ # Managed by Team B
└── ...Platform team maintain một RootSync per cluster (trỏ vào platform-repo). RootSync này có thể bootstrap RepoSync objects cho các teams:
# platform-repo: tạo RepoSync cho Team A
apiVersion: configsync.gke.io/v1beta1
kind: RepoSync
metadata:
name: team-a
namespace: team-a
spec:
git:
repo: https://github.com/my-org/team-a-repo
branch: main
auth: token
secretRef:
name: team-a-git-credsÝ nghĩa: RootSync tạo RepoSync → RepoSync tạo team resources. Đây là delegation model: platform team quản lý cluster-level resources và bootstrap team sync; teams quản lý namespace-level resources.
Fleet Integration — Config Sync qua GKE Fleet
Khi quản lý nhiều clusters với Fleet, Config Sync có thể được enable và configure ở fleet level:
# Enable Config Sync cho fleet
gcloud container fleet config-management enable
# Apply fleet config (áp dụng cho tất cả fleet members)
gcloud container fleet config-management apply \
--membership=prod-cluster \
--config=config-management.yaml# config-management.yaml
applySpecVersion: 1
spec:
configSync:
enabled: true
sourceFormat: unstructured
git:
syncRepo: https://github.com/my-org/k8s-configs
syncBranch: main
policyDir: clusters/prod
secretType: token
gcpServiceAccountEmail: config-sync-sa@my-project.iam.gserviceaccount.comFleet management cho phép:
- Apply cùng Config Sync configuration cho nhiều clusters qua một command
- Fleet dashboard hiển thị sync status của tất cả clusters
- Detect clusters có configs out-of-sync
Fleet Dashboard và Sync Status
# Xem sync status của tất cả fleet clusters
gcloud container fleet config-management status
# Output:
# Cluster Status Last Sync Commit
# dev-cluster SYNCED 2m ago abc123
# prod-cluster SYNCED 5m ago abc123
# dr-cluster ERROR 10m ago abc123 → KNV2009: ...Policy Controller với GitOps
Policy Controller là GKE Fleet managed version của OPA Gatekeeper. Khi kết hợp với Config Sync, bạn có thể deploy policies qua GitOps — không cần manual apply ConstraintTemplates và Constraints, chúng được manage trong Git như mọi resource khác.
Enable Policy Controller
# config-management.yaml — enable Policy Controller trong fleet config
spec:
policyController:
enabled: true
auditIntervalSeconds: 60 # Audit cluster mỗi 60s
templateLibraryInstalled: true # Cài pre-built policy templates
logDeniesEnabled: true # Log mọi denied admissionDeploy Policies qua Config Sync
Sau khi Policy Controller được enable, bạn có thể commit ConstraintTemplate và Constraint vào Git repo và Config Sync sẽ apply chúng:
k8s-configs/
└── cluster/
└── policies/
├── constraint-templates/
│ ├── no-privileged-containers.yaml # ConstraintTemplate
│ └── required-labels.yaml
└── constraints/
├── deny-privileged-prod.yaml # Constraint (enforce template)
└── require-app-label.yaml# constraint-templates/no-privileged-containers.yaml
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: noprivilegedcontainers
spec:
crd:
spec:
names:
kind: NoPrivilegedContainers
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package noprivilegedcontainers
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
container.securityContext.privileged == true
msg := sprintf("Container %v chạy với privileged mode", [container.name])
}# constraints/deny-privileged-prod.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: NoPrivilegedContainers
metadata:
name: deny-privileged-prod
spec:
enforcementAction: deny
match:
namespaces:
- production
- stagingConfig Sync apply ConstraintTemplate trước (CRD creation), sau đó apply Constraint (instance của CRD). Policy Controller tự động register webhook để enforce policies.
Policy Bundles qua Fleet
Policy Controller cung cấp pre-built policy bundles (collections của ConstraintTemplates và Constraints cho common security requirements):
spec:
policyController:
enabled: true
policyBundles:
- baseline # Kubernetes Pod Security Standards Baseline
- restricted # Kubernetes Pod Security Standards Restricted
- cis-k8s-v1.5.1 # CIS Kubernetes BenchmarkBundles được apply tự động không cần viết ConstraintTemplates custom.
Namespace Sameness — Hiểu Đúng trong Multi-Cluster
Khi nhiều clusters thuộc cùng một Fleet, Fleet enforce namespace sameness: namespace cùng tên ở các clusters khác nhau được coi là "cùng namespace" cho các purposes như Multi-Cluster Services.
Config Sync integrate với namespace sameness bằng cách ensure namespace configs được sync nhất quán qua tất cả clusters:
# Namespace được define trong platform-repo:
apiVersion: v1
kind: Namespace
metadata:
name: team-a
labels:
fleet.gke.io/managed: "true"Khi namespace này được sync lên tất cả clusters, Fleet hiểu namespace team-a là "same namespace" across clusters — cho phép Multi-Cluster Services export/import services trong namespace đó.
Cloud Deploy + Config Sync — Kết Hợp Đúng Cách
Đây là câu hỏi thường gặp: "Tôi nên dùng Cloud Deploy hay Config Sync để deploy application?"
Câu trả lời: cả hai, cho different concerns.
Config Sync quản lý: Infrastructure Configs
Git (cluster-configs repo):
├── CRDs → Config Sync apply
├── ClusterRoles, ClusterRoleBindings → Config Sync apply
├── StorageClasses → Config Sync apply
├── NetworkPolicies → Config Sync apply
├── Namespaces → Config Sync apply
├── RBAC policies → Config Sync apply
└── Policy Controller configs → Config Sync applyCloud Deploy quản lý: Application Deployment
Git (app-code repo):
└── Application source code + Dockerfile → CI builds image → Cloud Deploy releasesCloud Deploy flow:
CI build image → create Release → rollout dev → promote staging → approve prodVí dụ thực tế: Phân Chia Rõ Ràng
# cluster-configs/namespaces/team-a.yaml (Config Sync quản lý)
apiVersion: v1
kind: Namespace
metadata:
name: team-a
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-a-quota
namespace: team-a
spec:
hard:
cpu: "20"
memory: 40Gi
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: team-a
spec:
podSelector: {}
policyTypes: [Ingress, Egress]# app-configs/team-a/deployment.yaml (Cloud Deploy quản lý qua Skaffold)
apiVersion: apps/v1
kind: Deployment
metadata:
name: team-a-app
namespace: team-a
spec:
replicas: 3
template:
spec:
containers:
- name: app
image: team-a-app # Placeholder — replaced by Cloud DeployOverlap không phải là vấn đề nếu phân chia rõ ràng:
- Config Sync manage namespace, quota, network policies → infrastructure layer
- Cloud Deploy manage Deployment, Service (application artifacts) → application layer
- Với Server-Side Apply và field ownership, các tool không conflict nếu chúng manage different objects
Anti-Patterns Multi-Cluster GitOps
Anti-pattern 1: Shared mutable branch per environment
# SAI — mỗi environment commit vào main branch riêng mình
git push origin main # dev environment push đây
git push origin staging # staging environment push đây
git push origin production # production push đâyKhi dev branch bị merged vào production branch nhầm → config của dev lên production. Dùng Kustomize overlays trong cùng một branch với Cluster Selector thay vì separate branches per environment.
Anti-pattern 2: Commit secrets vào Git
Config Sync sync bất cứ gì trong repo, bao gồm cả Secret objects. Kubernetes Secrets trong Git là plaintext base64 — không có bảo mật thực sự.
Giải pháp:
- Dùng External Secrets Operator để sync từ Secret Manager
- Dùng Sealed Secrets (encrypt secret trước khi commit, decrypt trong cluster)
- Dùng Workload Identity để không cần secrets cho GCP services
Anti-pattern 3: Không có approval trước khi merge vào production branch
GitOps shifts drift mitigation sang Git — nhưng cũng shift security vào Git access control. Production Git config phải có:
- Branch protection rules: Không ai push trực tiếp vào production directory/branch
- Required reviews: Ít nhất một người review trước khi merge
- CI validation:
nomos vethoặckpt fn validatetrên mọi PR