Skip to content

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:

yaml
# RootSync trên prod cluster
spec:
  git:
    dir: clusters/prod-us-central1

Nhượ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:

yaml
# RootSync trên prod cluster
spec:
  git:
    dir: overlays/prod

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

yaml
# 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:

bash
# 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
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.com

Fleet 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

bash
# 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

yaml
# 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 admission

Deploy Policies qua Config Sync

Sau khi Policy Controller được enable, bạn có thể commit ConstraintTemplateConstraint 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
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])
      }
yaml
# constraints/deny-privileged-prod.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: NoPrivilegedContainers
metadata:
  name: deny-privileged-prod
spec:
  enforcementAction: deny
  match:
    namespaces:
    - production
    - staging

Config 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):

yaml
spec:
  policyController:
    enabled: true
    policyBundles:
    - baseline     # Kubernetes Pod Security Standards Baseline
    - restricted   # Kubernetes Pod Security Standards Restricted
    - cis-k8s-v1.5.1  # CIS Kubernetes Benchmark

Bundles đượ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:

yaml
# 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 apply

Cloud Deploy quản lý: Application Deployment

Git (app-code repo):
└── Application source code + Dockerfile → CI builds image → Cloud Deploy releases
Cloud Deploy flow:
  CI build image → create Release → rollout dev → promote staging → approve prod

Ví dụ thực tế: Phân Chia Rõ Ràng

yaml
# 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]
yaml
# 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 Deploy

Overlap 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 đây

Khi 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 vet hoặc kpt fn validate trên mọi PR

Official References