LA NINA // BLOG
Kembali ke Indeks Catatan

Rekayasa GitOps Multi-Cluster Skala Enterprise: Konvergensi ArgoCD vs Flux v2, Zero-Trust Supply-Chain via Sigstore Cosign, dan Modular Terragrunt IaC

Paradigma Rekayasa GitOps & Kebutuhan Deklaratif Skala Fleet

Pada era komputasi cloud-native terdistribusi, mengelola puluhan kluster Kubernetes menggunakan pendekatan CI/CD berbasis push tradisional (di mana runner CI seperti GitLab CI atau GitHub Actions mengeksekusi kubectl apply secara langsung) menciptakan celah keamanan dan kelemahan arsitektural yang fatal:

  1. Eksfiltrasi Kredensial Berhak Tinggi: Runner CI luar harus memegang token cluster-admin atau sertifikat TLS kubeconfig dari setiap kluster target. Apabila satu runner atau pipeline dependency disusupi, penyerang memperoleh akses penuh (blast radius total) ke seluruh infrastruktur.
  2. Ketiadaan Rekonsiliasi Otomatis (Configuration Drift): Apabila terjadi intervensi manual darurat (hotfix ad-hoc via kubectl edit) atau anomali controller internal, pipeline push tidak menyadari perubahan tersebut sampai eksekusi pipeline berikutnya.
  3. Konkurensi & Mutasi parsial: Eksekusi simultan antar-developer dapat menyebabkan state overwrites yang merusak integritas desired state.

Paradigma GitOps merevolusi tata kelola ini dengan menetapkan repositori Git yang terenkripsi dan terlindungi (protected branch) sebagai satu-satunya representasi sah (single source of truth). Melalui pendekatan pull-based reconciliation, agen di dalam kluster secara otonom menyinkronkan live state di etcd dengan target state di Git tanpa membuka port ingress kube-apiserver ke internet publik.

Artikel ini mengupas arsitektur internal GitOps multi-cluster, komparasi mendalam reconciliation engine ArgoCD versus Flux v2, integrasi Zero-Trust Software Supply Chain berbasis kriptografi Sigstore Cosign & Kyverno, orkestrasi rahasia dinamis External Secrets Operator (ESO) bersama HashiCorp Vault, serta orkestrasi infrastruktur modular menggunakan Terragrunt Directed Acyclic Graph (DAG)β€”seluruhnya disajikan dengan data sintetis sesuai standar RFC 5737.


1. Anatomi Mesin Rekonsiliasi: ArgoCD Controller vs Flux v2 Micro-Controllers

Meskipun ArgoCD dan Flux v2 memiliki tujuan konseptual yang sejalanβ€”yakni Continuous State Convergenceβ€”arsitektur internal dan interaksi keduanya dengan Linux kernel memory serta Kubernetes API server memiliki perbedaan fundamental.

TEXT
========================================================================================
                      ARGOCD CONTROLLER RECONCILIATION ENGINE
========================================================================================
[ Git Repo (Source of Truth) ] ── (Webhook / 3m Resync) ──► [ argocd-repo-server ]
                                                                   β”‚
                                                                   β–Ό (Kustomize / Helm Build)
                                                          [ Manifest Memory Stream ]
                                                                   β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ argocd-application-controller ─────────▼─────────────────────┐
β”‚                                                                                       β”‚
β”‚  [ SharedIndexInformer (Kube-API Cache) ] ◄── (WATCH) ── [ Target K8s Clusters ]      β”‚
β”‚                     β”‚                                                                 β”‚
β”‚                     β–Ό                                                                 β”‚
β”‚       [ Three-Way Diffing Engine ] ◄─── (Target Manifests vs Live State vs Last-Applied)
β”‚                     β”‚                                                                 β”‚
β”‚                     β”œβ”€β”€ Identical? ───────► Status: Synced (Emit Prometheus Metrics)  β”‚
β”‚                     └── Out of Sync?                                                  β”‚
β”‚                              β”‚                                                        β”‚
β”‚                              β–Ό                                                        β”‚
β”‚                 [ Sync Task Execution Engine ]                                        β”‚
β”‚                 - Phase PreSync (Hooks & Migrations)                                  β”‚
β”‚                 - Apply Sync Waves (Wave 0 -> Wave 1 -> Wave n)                       β”‚
β”‚                 - Dynamic Client Server-Side Apply (SSA)                              β”‚
β”‚                 - Phase PostSync (Health Check Validation)                            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

1.1 ArgoCD: Centralized Hub-and-Spoke Control Plane

ArgoCD mengadopsi model centralized management. Satu kontrol kluster ArgoCD mengawasi puluhan hingga ratusan kluster beban kerja (workload clusters):

  • Informer Cache Footprint: Komponen argocd-application-controller menginstansiasi objek SharedIndexInformer untuk setiap API resource pada setiap kluster yang terhubung. Pada 50 kluster dengan masing-masing 2.000 resources, cache internal di memori controller dapat melampaui 16–32 GB RAM.
  • Three-Way Merge Algorithm: Perbedaan dihitung secara matematis antara manifest target dari argocd-repo-server, manifest riil di etcd, dan metadata anotasi kubectl.kubernetes.io/last-applied-configuration.
  • Sync Waves & Phases: ArgoCD memiliki fitur bawaan untuk mengatur urutan provisioning (misalnya: CRD dan Namespace pada Wave -1, Secret dan Database pada Wave 0, Web Application pada Wave 1).

1.2 Flux v2: Modular Micro-Controllers & GitOps Toolkit

Flux v2 memecah monolitik controller menjadi sekumpulan Kubernetes-native custom controllers mandiri:

  • source-controller: Mengunduh Git repository, Helm repository, atau OCI registry, mengekstraknya ke tarball kompresi in-memory, dan menyajikannya via HTTP server internal bertarif hemat CPU.
  • kustomize-controller: Bertanggung jawab atas rendering Kustomize dan menerapkan manifest langsung via Kubernetes Server-Side Apply (SSA) tanpa perantara kubectl binary.
  • helm-controller: Menangani siklus hidup Helm Release deklaratif dengan dry-run testing otomatis.
  • Decentralized Security Model: Setiap kluster menjalankan pod Flux lokal. Tidak ada pengiriman master token kluster target ke satu hub sentral, secara drastis mengeliminasi risiko hub-compromise.

2. Arsitektur Zero-Trust Supply-Chain: Cryptographic Attestation Pipeline

Dalam model Zero-Trust sejati, sebuah container image tidak boleh diizinkan berjalan di kluster hanya karena tersimpan di private registry. Container image harus membawa bukti integritas kriptografis yang tak terbantahkan (cryptographic provenance) sejak proses kompilasi hingga eksekusi pod.

TEXT
[ Developer Git Commit ] 
       β”‚
       β–Ό
[ Hardened CI Runner (Rootless Buildah / Kaniko) ]
       β”‚
       β”œβ”€β”€β–Ί 1. Build OCI Container (Deterministic Layer Hash sha256:7a9f...)
       β”œβ”€β”€β–Ί 2. Generate SBOM (Software Bill of Materials) via Syft
       β”œβ”€β”€β–Ί 3. Vulnerability Scan (Grype) ──► Fail on Critical CVE
       β”‚
       β–Ό
[ OCI Registry Push (Subnet RFC 5737: 198.51.100.50:5000) ]
       β”‚
       β”œβ”€β”€β–Ί 4. Cosign Sign Digest (Keyless Sigstore OIDC / Private Key)
       β”œβ”€β”€β–Ί 5. Cosign Attest SBOM (In-toto Predicate Format)
       └──► 6. Rekor Transparency Log Append
                               β”‚
                               β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ Kubernetes Cluster Edge ──────────────────────────────────────┐
β”‚                                                                                     β”‚
β”‚  [ GitOps Engine Sync ] ──► [ Kube-Apiserver (VIP: 198.51.100.10:6443) ]            β”‚
β”‚                                       β”‚                                             β”‚
β”‚                                       β–Ό (AdmissionReview HTTPS Webhook)             β”‚
β”‚                        [ Kyverno Admission Controller ]                             β”‚
β”‚                                       β”‚                                             β”‚
β”‚              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                    β”‚
β”‚              β”‚                                                 β”‚                    β”‚
β”‚              β–Ό                                                 β–Ό                    β”‚
β”‚   [ Verify Cosign Signature ]                       [ Verify In-Toto SBOM ]         β”‚
β”‚   - Check Rekor Log & Fulcio Cert                  - Validate Vulnerability Report  β”‚
β”‚   - Match Public Key PEM payload                   - Assert CycloneDX Schema 1.4    β”‚
β”‚              β”‚                                                 β”‚                    β”‚
β”‚              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                    β”‚
β”‚                                       β”‚                                             β”‚
β”‚                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                        β”‚
β”‚                  β–Ό                                         β–Ό                        β”‚
β”‚          [ Signature Match ]                       [ Invalid / Tampered ]           β”‚
β”‚          Allow Pod Creation                        Reject Pod (403 Forbidden)       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  1. Rootless Image Construction: Memutus ketergantungan pada /var/run/docker.sock. Pemanfaatan Buildah atau Kaniko mencegah serangan container escape ke hak akses root host selama fase kompilasi CI.
  2. Sigstore Cosign Signing: Image OCI ditandatangani secara kriptografis menggunakan algoritma kurva eliptik (ECDSA P-256). Signature disimpan sebagai layer artefak OCI berdampingan dengan digest image.
  3. Kyverno Admission Enforcement: Sebelum Kubelet menarik layer image dari registry, admission webhook Kyverno mencegat request Pod/CREATE. Jika signature tidak terverifikasi atau payload SBOM memuat pelanggaran compliance, pembuatan Pod dibatalkan secara atomik.

3. Secret Flow Architecture: External Secrets Operator (ESO) & HashiCorp Vault

Penyimpanan kredensial di Git repositoryβ€”sekalipun dikaburkan menggunakan enkripsi dasarβ€”merupakan anti-pattern pada lingkungan teregulasi tinggi (seperti PCI-DSS atau SOC2). Pendekatan enterprise memisahkan definisi deployment dari storage credential aktual melalui External Secrets Operator (ESO) yang berkomunikasi secara asynchronous dengan HashiCorp Vault.

TEXT
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    HASHICORP VAULT CLUSTER (198.51.100.20:8200)                     β”‚
β”‚                    Path: secret/data/production/financial/payment                   β”‚
β”‚                    Payload: { "DB_PASS": "Encrypted#Synth99" }                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                           β”‚
                                           β”‚ TLS 1.3 / Projected ServiceAccount JWT
                                           β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ KUBERNETES WORKLOAD CLUSTER ─────────────────────┐
β”‚                                                                                     β”‚
β”‚   [ Git Repository ]                                                                β”‚
β”‚          β”‚ (ArgoCD Sync)                                                            β”‚
β”‚          β–Ό                                                                          β”‚
β”‚   [ ExternalSecret CRD ]                                                            β”‚
β”‚     - Spec: storeRef: enterprise-vault-backend                                      β”‚
β”‚             remoteRef: key=production/financial/payment, property=DB_PASS           β”‚
β”‚          β”‚                                                                          β”‚
β”‚          β–Ό                                                                          β”‚
β”‚   [ External Secrets Operator (ESO) Controller ]                                    β”‚
β”‚          β”‚                                                                          β”‚
β”‚          β”œβ”€β”€ 1. Request Vault Token via K8s Auth (`/v1/auth/kubernetes/login`)      β”‚
β”‚          β”œβ”€β”€ 2. Fetch Decrypted Secret Payload over Mutual TLS                      β”‚
β”‚          β”œβ”€β”€ 3. Render Native K8s Secret (`kind: Secret`)                           β”‚
β”‚          └── 4. Rotate Secret secara periodik (interval: 1h)                        β”‚
β”‚                     β”‚                                                               β”‚
β”‚                     β–Ό                                                               β”‚
β”‚   [ Native Kubernetes Secret: "payment-db-secret" ]                                 β”‚
β”‚          β–²                                                                          β”‚
β”‚          └── (Mounted as volume / injected envFrom) ── [ Microservice Pods ]        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

4. Konfigurasi Produksi & Manifests Siap Pakai

Berikut adalah implementasi manifest deklaratif berbobot enterprise menggunakan data sintetis RFC 5737 (198.51.100.0/24, 192.0.2.0/24, dan 203.0.113.0/24).

4.1 ArgoCD Enterprise ApplicationSet: Matrix Generator & Progressive Sync

Manifest ini mengimplementasikan generator matriks yang menggabungkan direktori Git microservice dengan kluster produksi terdaftar, lengkap dengan strategi peluncuran bertahap (Rolling Progressive Sync):

YAML
# /infrastructure/gitops/argocd/applicationset-matrix-enterprise.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: fleet-microservices-matrix
  namespace: argocd
  labels:
    tier: production
    managed-by: gitops-control-plane
spec:
  goTemplate: true
  goTemplateOptions: ["missingkey=error"]
  strategy:
    type: RollingSync
    rollingSync:
      steps:
        - matchExpressions:
            - key: environment
              operator: In
              values:
                - staging
          maxUpdate: 100%
        - matchExpressions:
            - key: environment
              operator: In
              values:
                - production
          maxUpdate: 25% # Canary deployment antar cluster bertahap
  generators:
    - matrix:
        generators:
          # Generator 1: Memetakan struktur direktori microservices di Git
          - git:
              repoURL: https://git.example.net/enterprise-core/cloud-platform-manifests.git
              revision: HEAD
              directories:
                - path: apps/services/*
          # Generator 2: Memilih target cluster berdasarkan metadata labels
          - clusters:
              selector:
                matchLabels:
                  fleet.argoproj.io/tier: tier-1-production
              values:
                region: '{{ index .metadata.labels "topology.kubernetes.io/region" }}'
                environment: '{{ index .metadata.labels "environment" }}'
  template:
    metadata:
      name: '{{ .path.basename }}-{{ .name }}'
      namespace: argocd
      labels:
        app.kubernetes.io/name: '{{ .path.basename }}'
        env: '{{ .values.environment }}'
        region: '{{ .values.region }}'
    spec:
      project: default
      source:
        repoURL: https://git.example.net/enterprise-core/cloud-platform-manifests.git
        targetRevision: HEAD
        path: '{{ .path.path }}/overlays/{{ .values.environment }}'
        kustomize:
          images:
            - 'release-image=198.51.100.50:5000/microservices/{{ .path.basename }}:v2.4.1'
      destination:
        server: '{{ .server }}'
        namespace: '{{ .path.basename }}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
          allowEmpty: false
        syncOptions:
          - CreateNamespace=true
          - PrunePropagationPolicy=foreground
          - PruneLast=true
          - ApplyOutOfSyncOnly=true
          - ServerSideApply=true
        retry:
          limit: 5
          backoff:
            duration: 10s
            factor: 2
            maxDuration: 3m
      ignoreDifferences:
        - group: apps
          kind: Deployment
          jsonPointers:
            - /spec/replicas # Membiarkan HorizontalPodAutoscaler mengontrol replikasi
        - group: ""
          kind: Service
          jsonPointers:
            - /spec/clusterIP
            - /spec/ports/0/nodePort

4.2 Kyverno ClusterPolicy: Enforce Cosign Cryptographic Signature & Attestation

Kebijakan berikut memblokir unauthenticated image dan kontainer non-root pada namespace operasional:

YAML
# /infrastructure/gitops/policies/kyverno-verify-supply-chain.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: enforce-supply-chain-signature-sbom
  annotations:
    policies.kyverno.io/title: "Verify Container Signature & SBOM Attestation"
    policies.kyverno.io/category: "Zero-Trust Supply Chain"
    policies.kyverno.io/severity: "critical"
spec:
  validationFailureAction: Enforce
  background: false
  failurePolicy: Fail
  webhookTimeoutSeconds: 30
  rules:
    - name: verify-cosign-image-signature
      match:
        any:
          - resources:
              kinds:
                - Pod
              namespaces:
                - production-*
                - financial-*
      exclude:
        any:
          - resources:
              namespaces:
                - kube-system
                - kyverno
                - monitoring
      verifyImages:
        - imageReferences:
            - "198.51.100.50:5000/microservices/*"
          attestors:
            - entries:
                - keys:
                    publicKeys: |
                      -----BEGIN PUBLIC KEY-----
                      MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE24F7sX3Bv6jCqR4E/rU2sK8QpQZf
                      G3s9e4R6sYw/5jZqH9kE3tP4Q7uL1nB9w0yV3xR8oF7kL5sN4aB9cD1eFg==
                      -----END PUBLIC KEY-----
          attestations:
            - predicateType: https://cyclonedx.org/schema/1.4
              conditions:
                - all:
                    - key: "{{ cyclonedx.metadata.component.type }}"
                      operator: Equals
                      value: "application"
    - name: block-root-and-privileged-containers
      match:
        any:
          - resources:
              kinds:
                - Pod
              namespaces:
                - production-*
      validate:
        message: "Menjalankan container sebagai root atau privileged dilarang pada tier produksi!"
        pattern:
          spec:
            =(securityContext):
              =(runAsNonRoot): true
            containers:
              - securityContext:
                  allowPrivilegeEscalation: false
                  readOnlyRootFilesystem: true
                  capabilities:
                    drop:
                      - ALL

4.3 External Secrets Operator: Integrasi HashiCorp Vault

Penyambungan workload cluster dengan Vault cluster yang terisolasi via Kubernetes ServiceAccount Token Projector:

YAML
# /infrastructure/gitops/secrets/eso-vault-store.yaml
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: enterprise-vault-backend
spec:
  provider:
    vault:
      server: "https://vault.internal.example.net:8200"
      path: "secret"
      version: "v2"
      auth:
        kubernetes:
          mountPath: "kubernetes"
          role: "k8s-platform-workload-role"
          serviceAccountRef:
            name: eso-vault-auth-sa
            namespace: external-secrets
      caProvider:
        type: ConfigMap
        name: internal-root-ca
        key: ca.crt
        namespace: external-secrets
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: payment-gateway-credentials
  namespace: payment-system
spec:
  refreshInterval: 1h
  secretStoreRef:
    kind: ClusterSecretStore
    name: enterprise-vault-backend
  target:
    name: payment-live-secret
    creationPolicy: Owner
    template:
      engineVersion: v2
      metadata:
        labels:
          security.enterprise.net/classification: pci-dss-regulated
      data:
        DATABASE_URL: "postgresql://{{ .DB_USER }}:{{ .DB_PASS }}@192.0.2.30:5432/transact?sslmode=verify-full"
        STRIPE_SECRET_KEY: "{{ .STRIPE_KEY }}"
  data:
    - secretKey: DB_USER
      remoteRef:
        key: production/financial/payment
        property: username
    - secretKey: DB_PASS
      remoteRef:
        key: production/financial/payment
        property: password
    - secretKey: STRIPE_KEY
      remoteRef:
        key: production/financial/payment
        property: stripe_live_token

4.4 Terragrunt Production Architecture: Global DRY Engine

Terragrunt mengelola dependensi antar-modul infrastruktur sebagai Directed Acyclic Graph (DAG), mengisolasi kegagalan state file, dan menyediakan distributed locking via DynamoDB.

HCL
# /infrastructure/live/terragrunt.hcl (Root Terragrunt Configuration)
locals {
  path_segments = split("/", path_relative_to_include())
  environment   = local.path_segments[0] # e.g. "prod", "staging"
  region        = local.path_segments[1] # e.g. "ap-southeast-1"
  component     = local.path_segments[2] # e.g. "vpc", "eks", "gitops"

  global_vars = yamldecode(file(find_in_parent_folders("global_vars.yaml")))
  env_vars    = yamldecode(file("${get_parent_terragrunt_dir()}/environments/${local.environment}.yaml"))
}

# Dynamic Provider Injection ke seluruh child modules
generate "provider" {
  path      = "provider_override.tf"
  if_exists = "overwrite_terragrunt"
  contents  = <<EOF
terraform {
  required_version = ">= 1.8.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.50"
    }
  }
}

provider "aws" {
  region = "${local.region}"
  default_tags {
    tags = {
      Environment = "${local.environment}"
      ManagedBy   = "Terragrunt"
      CostCenter  = "${local.env_vars.cost_center}"
      Repository  = "https://git.example.net/enterprise-core/infrastructure-live"
    }
  }
}
EOF
}

# Remote State Backend Configuration (S3 + DynamoDB State Lock)
remote_state {
  backend = "s3"
  generate = {
    path      = "backend.tf"
    if_exists = "overwrite_terragrunt"
  }
  config = {
    bucket         = "enterprise-tf-state-${local.global_vars.account_id}-${local.region}"
    key            = "${path_relative_to_include()}/terraform.tfstate"
    region         = local.region
    encrypt        = true
    dynamodb_table = "enterprise-tf-state-locks"
    s3_bucket_tags = {
      Owner       = "DevOps Core Team"
      PurgePolicy = "Never"
    }
  }
}

inputs = merge(
  local.global_vars,
  local.env_vars,
  {
    environment = local.environment
    region      = local.region
  }
)

Child module eks/terragrunt.hcl yang mengonsumsi output dari modul vpc secara otomatis:

HCL
# /infrastructure/live/prod/ap-southeast-1/eks/terragrunt.hcl
include "root" {
  path = find_in_parent_folders()
}

terraform {
  source = "git::git@git.example.net:terraform-modules/eks.git?ref=v20.8.5"
}

dependency "vpc" {
  config_path = "../vpc"
  mock_outputs = {
    vpc_id          = "vpc-00000000000000000"
    private_subnets = ["subnet-00000001", "subnet-00000002", "subnet-00000003"]
  }
  mock_outputs_allowed_terraform_commands = ["validate", "plan"]
}

inputs = {
  cluster_name    = "enterprise-k8s-prod-sg"
  cluster_version = "1.30"

  vpc_id     = dependency.vpc.outputs.vpc_id
  subnet_ids = dependency.vpc.outputs.private_subnets

  cluster_endpoint_public_access  = false
  cluster_endpoint_private_access = true

  enable_cluster_creator_admin_permissions = false
  authentication_mode                      = "API_AND_CONFIG_MAP"

  eks_managed_node_groups = {
    core_system = {
      min_size       = 3
      max_size       = 10
      desired_size   = 3
      instance_types = ["m6i.2xlarge"]
      capacity_type  = "ON_DEMAND"
      labels = {
        tier = "platform-core"
      }
    }
  }
}

5. Skrip Otomasi Validasi, Audit, dan Benchmarking

5.1 Pipeline Validation & Supply-Chain Attestation Runner

Skrip ini memverifikasi bahwa container image memenuhi kriteria Zero-Trust sebelum didaftarkan ke manifest GitOps:

BASH
#!/usr/bin/env bash
# ==============================================================================
# verify-supply-chain.sh β€” Zero-Trust Supply-Chain Attestation Validator
# Author: La Nina
# ==============================================================================
set -euo pipefail

IMAGE_REF="${1:-198.51.100.50:5000/microservices/payment-engine:v2.4.1}"
COSIGN_KEY_FILE="${2:-/etc/cosign/cosign.pub}"

echo "======================================================================"
echo "    ZERO-TRUST SUPPLY-CHAIN ATTESTATION & SECURITY AUDITOR"
echo "    Target OCI Artifact: ${IMAGE_REF}"
echo "======================================================================"

TMP_DIR=$(mktemp -d)
trap 'rm -rf "${TMP_DIR}"' EXIT

# 1. Verifikasi Cosign Cryptographic Signature
echo "[STEP 1] Memvalidasi signature kriptografis image..."
if [ -f "${COSIGN_KEY_FILE}" ]; then
    if cosign verify --key "${COSIGN_KEY_FILE}" "${IMAGE_REF}" > "${TMP_DIR}/cosign_verify.json" 2>&1; then
        echo "  [OK] Signature valid terotentikasi oleh: ${COSIGN_KEY_FILE}"
    else
        echo "  [FATAL] SIGNATURE INVALID! Citra kontainer dimanipulasi atau tidak sah!"
        exit 1
    fi
fi

# 2. Verifikasi In-toto SBOM Attestation
echo "[STEP 2] Memeriksa attestation format CycloneDX..."
if [ -f "${COSIGN_KEY_FILE}" ]; then
    if cosign verify-attestation --key "${COSIGN_KEY_FILE}" --type cyclonedx "${IMAGE_REF}" > "${TMP_DIR}/attestation.json" 2>&1; then
        echo "  [OK] Attestation CycloneDX SBOM terverifikasi sah."
    else
        echo "  [WARN] Attestation SBOM tidak ditemukan atau tidak match."
    fi
fi

echo "======================================================================"
echo "[SUCCESS] Citra OCI memenuhi seluruh syarat keamanan Zero-Trust."
echo "======================================================================"

5.2 GitOps Reconciliation Latency & Admission Benchmark

Skrip untuk mengukur latensi end-to-end dari saat manifest diterapkan hingga pod mencapai status Running di bawah beban konkurensi webhook:

BASH
#!/usr/bin/env bash
# ==============================================================================
# benchmark-gitops-perf.sh β€” High-Concurrency Admission & Convergence Benchmark
# Author: La Nina
# ==============================================================================
set -euo pipefail

NAMESPACE="benchmark-gitops-$(date +%s)"
ITERATIONS="${1:-20}"
CONCURRENCY="${2:-5}"

echo "======================================================================"
echo "    KUBERNETES GITOPS RECONCILIATION & ADMISSION BENCHMARK"
echo "    Namespace: ${NAMESPACE} | Iterasi: ${ITERATIONS} | Concurrency: ${CONCURRENCY}"
echo "======================================================================"

kubectl create namespace "${NAMESPACE}"
trap 'kubectl delete namespace "${NAMESPACE}" --wait=false >/dev/null 2>&1 || true' EXIT

START_TIME=$(date +%s%3N)

for i in $(seq 1 "${ITERATIONS}"); do
    cat <<EOF | kubectl apply -f - >/dev/null &
apiVersion: apps/v1
kind: Deployment
metadata:
  name: bench-app-${i}
  namespace: ${NAMESPACE}
spec:
  replicas: 1
  selector:
    matchLabels:
      app: bench-app-${i}
  template:
    metadata:
      labels:
        app: bench-app-${i}
    spec:
      containers:
        - name: pause
          image: 198.51.100.50:5000/microservices/pause:3.9
          resources:
            limits: { cpu: 20m, memory: 32Mi }
            requests: { cpu: 5m, memory: 16Mi }
EOF
    if (( i % CONCURRENCY == 0 )); then wait; fi
done
wait

END_APPLY=$(date +%s%3N)
APPLY_DURATION=$(( END_APPLY - START_TIME ))

echo "[METRIC] Selesai menerapkan ${ITERATIONS} deployments dalam ${APPLY_DURATION} ms"

# Tunggu konvergensi pod
while true; do
    READY_COUNT=$(kubectl get pods -n "${NAMESPACE}" --field-selector=status.phase=Running --no-headers 2>/dev/null | wc -l || echo 0)
    if [ "${READY_COUNT}" -ge "${ITERATIONS}" ]; then break; fi
    sleep 1
done

END_WATCH=$(date +%s%3N)
TOTAL_LATENCY=$(( END_WATCH - START_TIME ))

echo "======================================================================"
echo " Total Manifests     : ${ITERATIONS}"
echo " Client Apply Latency: ${APPLY_DURATION} ms"
echo " Total Convergence   : ${TOTAL_LATENCY} ms"
echo " Rata-rata per-pod   : $(( TOTAL_LATENCY / ITERATIONS )) ms / pod"
echo "======================================================================"

6. Skenario Troubleshooting Kasus Nyata (Root Cause Analysis & SOP)

Kasus 1: ArgoCD Application Out-of-Sync Loop Akibat Mutating Webhook

  • Gejala: Aplikasi terus berganti status antara Synced dan OutOfSync setiap beberapa detik. Log argocd-application-controller melaporkan perbedaan pada field /spec/template/metadata/annotations/vault.hashicorp.com/agent-inject-token.
  • Akar Masalah (RCA): ArgoCD menyinkronkan manifest bersih dari Git. Sesaat setelah objek Deployment dibuat, mutating admission webhook HashiCorp Vault Agent Injector mencegat request dan menyuntikkan annotation serta sidecar container secara dinamis. Informer ArgoCD mendeteksi perbedaan antara live object di etcd dan target state di Git, lalu opsi selfHeal: true memicu perbaikan kembali yang tiada henti (infinite reconciliation thrashing).
  • Resolusi: Tambahkan direktif ignoreDifferences dengan pointer JSON spesifik pada Application CRD atau secara terpusat di argocd-cm ConfigMap:
    YAML
    spec:
      ignoreDifferences:
        - group: apps
          kind: Deployment
          jsonPointers:
            - /spec/template/metadata/annotations/vault.hashicorp.com~1agent-inject-token
            - /spec/template/metadata/annotations/vault.hashicorp.com~1agent-init-status
            - /spec/template/spec/initContainers
            - /spec/template/spec/volumes

Kasus 2: Controller OOMKilled & Kube-Apiserver CPU Throttling pada Skala 50+ Kluster

  • Gejala: Pod argocd-application-controller mengalami crash berulang dengan exit code 137 (OOMKilled). Latensi kube-apiserver pada kluster target melonjak hingga mengalami HTTP 504 Gateway Timeout.
  • Akar Masalah (RCA): Informer cache ArgoCD memantau seluruh resource secara default, termasuk entitas dengan churn rate sangat tinggi seperti Event, Endpoints, dan EndpointSlice. Cache snapshot di RAM membengkak eksponensial seiring pertambahan kluster.
  • Resolusi: Terapkan resource exclusion pada argocd-cm dan aktifkan controller sharding:
    YAML
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: argocd-cm
      namespace: argocd
    data:
      resource.exclusions: |
        - apiGroups: [""]
          kinds: ["Event", "Endpoints"]
        - apiGroups: ["discovery.k8s.io"]
          kinds: ["EndpointSlice"]
      timeout.reconciliation: "600s"
    Kemudian ubah deployment menjadi StatefulSet ber-sharding:
    BASH
    kubectl scale statefulset argocd-application-controller -n argocd --replicas=3
    kubectl set env statefulset/argocd-application-controller -n argocd ARGOCD_CONTROLLER_REPLICAS=3

Kasus 3: Kegagalan Verifikasi Cosign pada Lingkungan Air-Gapped Private Registry

  • Gejala: Kyverno webhook menolak pembuatan Pod dengan pesan: rule verify-cosign-image-signature failed: no matching signatures found.
  • Akar Masalah (RCA): Prosedur migrasi image menggunakan docker pull dan docker push standar hanya menyalin image manifest utama. Metadata signature Cosign disimpan sebagai tag referensial terpisah (berakhiran .sig dan .att). Akibatnya, file signature tidak ada di registry lokal internal.
  • Resolusi: Pergunakan tool mirroring OCI-native seperti crane atau skopeo yang mendukung penyalinan tag turunan secara rekursif:
    BASH
    DIGEST=$(crane digest "198.51.100.50:5000/microservices/payment:v2.0")
    crane copy "198.51.100.50:5000/microservices/payment:v2.0" "192.0.2.80:5000/microservices/payment:v2.0"
    crane copy "198.51.100.50:5000/microservices/payment:${DIGEST/:/-}.sig" "192.0.2.80:5000/microservices/payment:${DIGEST/:/-}.sig"
    crane copy "198.51.100.50:5000/microservices/payment:${DIGEST/:/-}.att" "192.0.2.80:5000/microservices/payment:${DIGEST/:/-}.att"

7. Matriks Evaluasi & Ringkasan Best Practices

Domain RekayasaPendekatan Enterprise TerkompilasiPraktik Buruk (Anti-Pattern)
Delivery EngineArgoCD ApplicationSet dengan Server-Side Apply & Progressive Rollouts.Menjalankan client-side kubectl apply secara langsung dari runner CI pihak ketiga.
Topologi KlusterDesentralisasi Informer Sharding atau agen lokal Flux v2 micro-controllers.Satu instance controller monolitik tanpa sharding mengelola lebih dari 100 remote kluster.
Supply ChainRootless container build (Buildah) + Cosign ECDSA Signature + Kyverno Webhook.Menjalankan build container dengan me-mount /var/run/docker.sock ke daemon host.
Manajemen RahasiaExternal Secrets Operator (ESO) dengan integrasi dinamis ke HashiCorp Vault.Menyimpan token atau credentials terenkripsi base64 mentah di dalam repositori Git.
Infrastructure as CodeTerragrunt DRY hierarchy, isolasi modular state per-komponen, S3 + DynamoDB locking.Monolitik main.tf raksasa yang menggabungkan deklarasi VPC hingga workload Kubernetes.

Melalui implementasi terintegrasi antara ArgoCD / Flux v2, penegakan Zero-Trust Supply-Chain, dan orkestrasi Terragrunt DAG, tim platform enterprise dapat menjamin keandalan, keterlacakan audit (traceability), dan integritas keamanan operasional tanpa mengorbankan kecepatan delivery aplikasi modern.

Kembali ke Atas ↑