Skip to content

Config Sync Sources & Hierarchical Repository — Tổ Chức Cấu Hình Ở Scale

Tại Sao Quan Trọng Trong Production

Config Sync có thể đọc từ ba nguồn khác nhau: Git repository, OCI container image, và Helm chart. Quyết định chọn source type ảnh hưởng đến: cách team versioning configs, thời gian sync, authentication mechanism, và khả năng audit trail. Tổ chức repository ảnh hưởng đến: khả năng phân quyền giữa platform team và app teams, tính rõ ràng của "config nào apply lên cluster nào", và scalability khi fleet lớn lên.

Hai quyết định này (source type và repo structure) phải được đưa ra sớm vì khó thay đổi về sau mà không có migration effort lớn.

Git Source — Cơ Chế Pull và Authentication

Cơ Chế Polling Git

Config Sync không dùng webhook để nhận thông báo từ Git (dù một số implementations khác như Flux dùng cách này). Thay vào đó, reconciler source container poll Git repository theo interval:

yaml
apiVersion: configsync.gke.io/v1beta1
kind: RootSync
metadata:
  name: root-sync
  namespace: config-management-system
spec:
  sourceType: git
  git:
    repo: https://github.com/myorg/fleet-config.git
    branch: main
    dir: clusters/prod-us-east1  # Thư mục trong repo
    period: 15s                   # Poll interval (default 15s)
    auth: gcpserviceaccount       # Authentication method
    gcpServiceAccountEmail: config-sync@my-project.iam.gserviceaccount.com

Quá trình pull:

  1. Source container chạy git fetch (không phải git clone đầy đủ mỗi lần)
  2. So sánh remote commit SHA với local cached SHA
  3. Nếu khác: git checkout branch/commit mới, cập nhật local cache
  4. Signal cho reconciler container rằng có nội dung mới để process

Git source dùng shallow clonesparse checkout khi có thể để giảm bandwidth và thời gian pull. Với large repo (thousands of files), chỉ fetch diff của commits mới, không re-fetch toàn bộ.

Authentication Cho Git

Config Sync hỗ trợ nhiều authentication methods:

gcpserviceaccount (khuyên dùng cho GCP-hosted repos như Cloud Source Repositories):

yaml
git:
  auth: gcpserviceaccount
  gcpServiceAccountEmail: config-sync@project.iam.gserviceaccount.com

Config Sync sử dụng Workload Identity của reconciler pod để authenticate. Service account cần có Source Repository Reader role trên repo.

token (username/password hoặc Personal Access Token):

yaml
git:
  auth: token
  secretRef:
    name: git-credentials   # Secret với keys: username, token

Token được store trong Kubernetes Secret trong config-management-system namespace. Không khuyến khích vì secret rotation phức tạp.

ssh (SSH key pair):

yaml
git:
  auth: ssh
  secretRef:
    name: git-ssh-key       # Secret với key: ssh

Private key stored trong Secret. SSH fingerprint của Git server phải được xác nhận trước hoặc noSSLVerify: true (không khuyến khích production).

none (public repo):

yaml
git:
  auth: none

Chỉ dùng cho public repos, không phù hợp production.

Branch, Tag, và Commit Pinning

yaml
git:
  branch: main           # Track HEAD của branch (default)
  # HOẶC
  revision: v1.2.3      # Pin vào specific tag
  # HOẶC
  revision: a1b2c3d4    # Pin vào specific commit SHA

Branch tracking (dynamic): Mỗi push lên branch được apply tự động sau polling interval. Phù hợp cho GitOps flow liên tục.

Tag pinning (semi-static): Sync theo tag. Để update, phải update spec.git.revision. Phù hợp khi muốn explicit "release" process.

Commit SHA pinning (fully static): Không bao giờ thay đổi trừ khi explicit update. Phù hợp cho environments cần audit trail chặt chẽ (có thể trace chính xác commit nào đang chạy trên cluster).

Commit SHA pinning đặc biệt có giá trị trong regulated environments: bạn có thể prove "tại thời điểm audit, cluster X đang run commit SHA Y, tương ứng với change Z được approved bởi engineer W".

OCI Source — Container Image Làm Config Bundle

Tại Sao OCI Source

OCI (Open Container Initiative) source là cơ chế pull Kubernetes configs từ container image registry thay vì Git. Ý tưởng: đóng gói configs vào container image và distribute như container images thông thường.

Lợi thế so với Git:

  • Versioning rõ ràng hơn: Container image tags (v1.2.3) là immutable (với digest), không như Git tags có thể bị move
  • Access control tốt hơn: Container Registry/Artifact Registry có fine-grained IAM, không cần SSH keys hay PATs
  • Integration với CI/CD pipeline: Pipeline build Helm chart → render → đóng gói thành OCI image → push → Config Sync pull. Toàn bộ flow là container-native
  • Performance với large configs: OCI layer caching giảm bandwidth so với Git clone cho large repos

Hạn chế:

  • Audit trail kém hơn Git: Git có commit history tự nhiên, OCI images cần additional tools để track history
  • Không thể xem diff "ở giữa": Phải compare layer-by-layer hoặc unpack images
  • Config Sync chỉ support tar hoặc tar+gzip format cho OCI layers — không phải mọi OCI image đều work

Cấu Hình OCI Source

yaml
apiVersion: configsync.gke.io/v1beta1
kind: RootSync
metadata:
  name: root-sync
  namespace: config-management-system
spec:
  sourceType: oci
  oci:
    image: us-docker.pkg.dev/my-project/config-registry/fleet-config:v1.2.3
    dir: /    # Thư mục trong image để mount configs
    auth: gcpserviceaccount
    gcpServiceAccountEmail: config-sync@my-project.iam.gserviceaccount.com

OCI image structure cần chứa configs theo flat hoặc hierarchical structure. Config Sync unpack image và read configs từ dir path được chỉ định.

Để push config image:

bash
# Đóng gói configs
tar -czvf configs.tar.gz -C ./k8s-configs .

# Build OCI image từ configs
crane append -f configs.tar.gz \
  -t us-docker.pkg.dev/my-project/config-registry/fleet-config:v1.2.3

# Hoặc dùng ko tool của Google
ko build ./config-bundle --image-refs=output

Digest vs Tag Reference

Khuyến nghị production: dùng digest thay vì tag:

yaml
oci:
  image: us-docker.pkg.dev/my-project/config-registry/fleet-config@sha256:abc123...

Image digest là immutable — cùng digest luôn trỏ đến cùng content. Tag có thể bị overwrite (mutable tags như latest là anti-pattern đặc biệt nguy hiểm). Dùng digest đảm bảo predictability và audit trail rõ ràng.

Helm Source — Chart Rendering Bên Trong Config Sync

Helm Pipeline Trong Config Sync

Config Sync có thể pull Helm charts và render chúng thành Kubernetes manifests trước khi apply. Khác với helm install truyền thống, Config Sync render Helm chart thành static manifests rồi manage chúng như bình thường — không dùng Helm tiller hay Helm release state.

yaml
apiVersion: configsync.gke.io/v1beta1
kind: RootSync
metadata:
  name: monitoring-sync
  namespace: config-management-system
spec:
  sourceType: helm
  helm:
    repo: https://prometheus-community.github.io/helm-charts
    chart: kube-prometheus-stack
    version: "55.5.0"    # Phải pin version cho reproducibility
    releaseName: monitoring
    namespace: monitoring
    values:
      prometheus:
        retention: 30d
      grafana:
        adminPassword:
          secretKeyRef:
            name: grafana-credentials
            key: password
    auth: none           # Public chart repos thường là none

Hydration pipeline cho Helm:

  1. Source container fetch chart từ Helm repo (OCI hoặc HTTP)
  2. Hydration container run helm template với values
  3. Output: rendered Kubernetes manifests (plain YAML)
  4. Reconciler apply manifests như bình thường

Lưu ý quan trọng: Theo documentation, "Config Sync doesn't support configuring helm as the source type using the Google Cloud console or the Google Cloud CLI." Phải dùng kubectl apply trực tiếp với RootSync/RepoSync manifest.

Giới Hạn Của Helm Source

Không có Helm hooks: helm.sh/hook annotations bị ignored. Config Sync không chạy pre-install, post-upgrade hooks. Nếu chart phụ thuộc vào hooks (ví dụ database migration), cần giải quyết theo cách khác.

Không có Helm rollback: Config Sync không có concept "helm rollback". Rollback = cập nhật version trong RootSync spec và commit lên Git.

Values complexity: Helm values phức tạp (nested references, conditional values) có thể gây khó khăn khi quản lý qua RootSync spec. Khuyến nghị: dùng valuesFileRefs trỏ đến ConfigMap chứa values thay vì inline values khi values phức tạp.

Chart version pinning là bắt buộc: Không dùng version: "*" hay version: "latest" — không phải Helm repo concept. Phải pin exact version. Thiếu version pinning = non-deterministic sync behavior.

Hierarchical vs Unstructured Repository

Unstructured Repository

Đơn giản nhất: tất cả Kubernetes manifests nằm trong một thư mục (hoặc ít thư mục), Config Sync apply tất cả lên cluster.

fleet-config-repo/
├── cluster-a/
│   ├── namespaces.yaml
│   ├── clusterroles.yaml
│   └── apps/
│       ├── payments.yaml
│       └── auth.yaml
└── cluster-b/
    ├── namespaces.yaml
    └── apps/
        └── payments.yaml

RootSync của cluster-a point vào cluster-a/, cluster-b point vào cluster-b/. Simple, nhưng không scale tốt khi:

  • Nhiều clusters share config (duplicate YAML)
  • Platform config và app config cần different RBAC (ai được edit gì)

Hierarchical Repository

Hierarchical repo của Config Sync là một khái niệm cụ thể: directory structure phản ánh Kubernetes resource hierarchy, cho phép Config Sync áp dụng special semantics:

hierarchical-repo/
├── cluster/                  # Cluster-scoped resources
│   ├── clusterroles.yaml
│   └── clusterrolebindings.yaml
├── namespaces/               # Namespace-level configs (apply cho mọi namespace)
│   ├── default/
│   │   └── limitrange.yaml
│   └── _hierarchy/           # Templates áp cho tất cả namespaces (nếu dùng HNC)
└── system/                   # System configs (ví dụ Config Sync configuration itself)

Trong hierarchical mode, files trong namespaces/ được automatically scoped đến namespace tương ứng, và một số configs trong namespaces/_default/ được apply như default cho tất cả namespaces.

Tuy nhiên, theo Config Sync documentation, hierarchical mode có limitations và Google hiện khuyến nghị unstructured mode cho hầu hết use cases vì linh hoạt hơn. Hierarchical mode chủ yếu useful khi tích hợp với Hierarchical Namespace Controller.

Pattern Tổ Chức Cho Fleet Ở Scale

Pattern 1: Mono-Repo Với Cluster Directories

Phù hợp với: fleet nhỏ đến trung bình (< 20 clusters), platform team trung tâm quản lý tất cả.

fleet-config-repo/
├── base/                          # Shared configs
│   ├── networking/
│   │   ├── network-policies.yaml
│   │   └── pod-security.yaml
│   └── rbac/
│       └── common-roles.yaml
├── clusters/
│   ├── prod-us-east1/
│   │   ├── kustomization.yaml     # Extends base + regional overrides
│   │   └── overrides/
│   │       └── resource-limits.yaml
│   ├── prod-eu-west1/
│   │   ├── kustomization.yaml
│   │   └── overrides/
│   │       └── gdpr-policies.yaml
│   └── staging-us-central1/
│       └── kustomization.yaml
└── teams/
    ├── payments/                  # App team configs (managed by payments team)
    │   └── ...
    └── auth/
        └── ...

Mỗi cluster có RootSync trỏ vào clusters/{cluster-name}/. Kustomize overlays cho phép base configs được reused với cluster-specific overrides.

Limitation: Với 50+ clusters, clusters/ directory trở nên khó navigate. Cross-cluster diffs phức tạp.

Pattern 2: Policy Repo Riêng + App Repos Riêng (Delegation Model)

Phù hợp với: fleet lớn, platform team và app teams tách biệt, clear ownership boundaries.

Platform Repo (managed by platform team):
  fleet-platform-config/
  ├── cluster-policies/           # Apply cho mọi cluster
  │   ├── psp-restricted.yaml
  │   ├── network-policies-default.yaml
  │   └── opa-constraints/
  └── namespaces/                 # Tạo namespaces, tạo RepoSync delegation
      ├── payments/
      │   ├── namespace.yaml
      │   └── reposync.yaml      # RepoSync trỏ vào payments team repo
      └── auth/
          ├── namespace.yaml
          └── reposync.yaml      # RepoSync trỏ vào auth team repo

App Repos (managed by app teams):
  payments-config/                # Payments team quản lý
  ├── deployment.yaml
  ├── service.yaml
  └── configmap.yaml

  auth-config/                    # Auth team quản lý
  ├── deployment.yaml
  └── ...

Platform team's RootSync sync fleet-platform-config/ lên mọi cluster — bao gồm cả reposync.yaml objects. Kubernetes apply những RepoSync objects này, sau đó Config Sync operator tạo reconciler pods cho chúng, và payments-config được synced bởi reconciler đó.

Đây là "meta-GitOps": dùng Config Sync để deploy Config Sync configuration. Platform team control namespace và RepoSync creation; app teams control content của namespace.

yaml
# Ví dụ reposync.yaml được platform team manage
apiVersion: configsync.gke.io/v1beta1
kind: RepoSync
metadata:
  name: payments-sync
  namespace: payments
spec:
  sourceType: git
  git:
    repo: https://github.com/myorg/payments-config.git
    branch: main
    auth: gcpserviceaccount
    gcpServiceAccountEmail: payments-config-sync@my-project.iam.gserviceaccount.com

Pattern 3: OCI-Based Fleet Config Distribution

Phù hợp với: tổ chức đã có container registry infrastructure mạnh, muốn immutable config bundles.

CI Pipeline:
  1. Platform team commit policy changes → trigger CI
  2. CI build Kustomize overlays cho mọi cluster variant
  3. Render thành static YAML
  4. Bundle thành OCI image: config-registry/fleet-policies:v1.2.3@sha256:...
  5. Push đến Artifact Registry

RootSync Configuration:
  spec:
    sourceType: oci
    oci:
      image: us-docker.pkg.dev/platform/config-registry/fleet-policies@sha256:abc

Update cluster config = update image digest trong RootSync spec. Audit trail: xem Artifact Registry image history.

Trade-off: Immutability và performance tốt, nhưng "git history" của configs phức tạp hơn — phải track image builds, không phải git commits trực tiếp.

Kết Hợp Sources Trên Cùng Cluster

Một cluster có thể có nhiều RootSync và RepoSync objects, mỗi cái pull từ source khác nhau:

cluster-prod-us-east1:
├── RootSync "platform-policies"     → Git: fleet-platform-config (platform team)
├── RootSync "monitoring-stack"      → Helm: kube-prometheus-stack (infra team)
├── RepoSync "payments" (namespace)  → Git: payments-config (payments team)
└── RepoSync "auth" (namespace)      → OCI: auth-config-image (auth team)

Config Sync operator quản lý independent reconciler pods cho mỗi sync. Chúng chạy song song, không phụ thuộc vào nhau. Nếu payments-config sync fail, monitoring-stack không bị ảnh hưởng.

Conflict resolution khi nhiều syncs: Nếu RootSync "platform-policies" apply một NetworkPolicy trong namespace payments, và RepoSync "payments" cũng try apply NetworkPolicy với cùng name — SSA conflict sẽ xảy ra. Platform team phải thiết kế clear ownership boundary để tránh overlap.

References