65

Argo CD GitOps: From Git to the Cluster

Reconcile Ziba’s Kubernetes application from an auditable Git revision while keeping Terraform responsible for the underlying infrastructure it owns.

The enterprise problem and today’s slice

Enterprise problem: Ziba’s GKE infrastructure can be reproducible while the live Deployment still drifts after emergency kubectl edits, and a push-based pipeline with broad cluster credentials can deploy an unreviewed image or silently leave production different from Git. Whole-course context: Terraform mapped reviewed configuration and state to GCP resources; today consumes an existing reachable cluster and moves Kubernetes application delivery into a continuous GitOps reconciliation loop. Today’s slice: Argo CD compares Git target state with Kubernetes live state, reports drift, and optionally syncs Ziba under bounded policy; Terraform continues to own the VPC, GKE cluster, node pools, and Argo CD installation boundary. End-of-day evidence: A reviewer sees commit-to-sync provenance, health, served image digest, a detected drift, a denied unauthorized change, and recovery. Still unsolved: Multi-cluster promotion, secret delivery, disaster recovery, progressive traffic shifting, and production auto-sync approval remain environment decisions.

Customer use cases

GitOps is not merely deploying from Git; it must continuously expose whether the approved revision and live application differ. These use cases cover intended release and out-of-band drift recovery.

Use case IDActorCustomer jobSuccess outcomeDenial or recovery evidence
D65-UC-01Ziba release engineerPromote a reviewed Ziba manifest revision to GKEArgo CD reports Synced and Healthy, and the public blog serves the pinned image digestInvalid manifest or unauthorised repository revision is rejected while the previous healthy revision remains served
D65-UC-02Ziba operations leadDetect and recover a manual live-cluster changeArgo CD reports OutOfSync, attributes the diff, and restores or adopts it through reviewed GitUnauthorised actor cannot mutate protected resources; drift and unaffected positive-control request remain visible

Actor-centred user stories

Sync status alone can be green while an application is unhealthy, so acceptance must include both configuration convergence and customer behavior. These stories retain positive and negative evidence across Git, Argo CD, Kubernetes, and the request path.

Story IDUse case IDsUser storyObservable acceptance conditions
D65-US-01D65-UC-01As a Ziba release engineer, I want Argo CD to reconcile a pinned Git revision, so that production manifests are traceable without giving CI direct cluster mutation credentialsCommit, repository/path, Application revision, sync result, health, Kubernetes rollout, image digest, HTTP result, environment, timestamp, and operation ID are captured
D65-US-02D65-UC-02As a Ziba operations lead, I want drift detected before it is corrected, so that emergency changes are visible and the chosen recovery is reviewableLive diff is recorded, unauthorized mutation is denied, self-heal or Git adoption is approved, and the customer positive control remains healthy

End-to-end product flows

Continuous reconciliation can also continuously apply a bad decision, so the flow separates review, comparison, policy, sync, and health. The recovery path preserves drift evidence before correcting it.

Flow IDUse case IDsPathTriggerNumbered stepsTerminal evidence
D65-FLOW-01D65-UC-01HappyRelease engineer merges a reviewed manifest change1. CI validates and signs/pins the artifact reference.
2. Git stores the reviewed target revision.
3. Argo CD repository server renders manifests.
4. Application controller compares target and live state.
5. Policy permits sync.
6. Kubernetes controllers roll out Ziba.
7. Argo CD observes health and the release probe reads the served digest.
Actor, repo/path, commit, target/live diff, cluster/namespace, sync operation ID, resource UIDs, image digest, health, HTTP result, environment, and time
D65-FLOW-02D65-UC-02RecoveryA manual replica or image edit changes live state1. Kubernetes authorisation denies an unapproved actor or records an approved emergency actor.
2. Argo CD refresh detects OutOfSync.
3. Preserve the diff and audit event.
4. Decide self-heal to Git or adopt through a reviewed commit.
5. Sync the chosen target.
6. Observe Synced, Healthy, and customer response.
Denial/audit identity, drift diff, recovery decision, commit, sync operation ID, final resource values, and unaffected request trace

System design derived from the flows

Argo CD owns application reconciliation, not the servers beneath the cluster. Its repository server renders manifests, while the application controller continuously compares live Kubernetes resources with the target in Git and can take corrective action (Argo CD architecture).

Use case IDEntry pointResponsible servicesAuthoritative storeFailure evidence
D65-UC-01Reviewed manifest merge and Argo CD ApplicationGit host, repository server, application controller, Argo CD policy/RBAC, Kubernetes API/controllers, Ziba health probeGit commit for target manifests; Kubernetes API for live state; Argo CD Application status/history for comparison and operationsRender error, invalid revision, policy denial, sync failure, degraded health, rollout timeout, wrong image digest, or failed HTTP probe
D65-UC-02Drift alert and recovery decisionKubernetes admission/RBAC/audit, Argo CD refresh/diff, application controller, Git review, evidence storeKubernetes API/audit for changed live state; Git for chosen target; Argo CD status/history and immutable incident evidenceUnauthorized mutation, ignored material diff, self-heal disabled, repeated drift, prune hazard, or customer regression

Argo CD defines target state as the application state represented in Git, live state as the deployed resources, refresh as comparison, sync as moving live resources toward target, and health as whether the application is operating (Argo CD core concepts). These are related but distinct signals: Synced does not necessarily mean Healthy.

Data model and ownership

Git, Argo CD status, and Kubernetes live objects cannot be one authority because each records a different part of the control loop. Ziba’s content database, if any, remains outside declarative manifest reconciliation and needs its own backup and migration lifecycle.

Generated-application database: Not created in this slice — Git revisions, Argo CD Application state/history, Kubernetes resources, audit events, and HTTP probes provide the durable deployment evidence required here.

Record or entityStore and ownerPrimary keyForeign key or opaque referenceTenant keyMaterial constraintLifecycle and deletionUse case IDs
GitTargetRevisionGit host, owned by Ziba release engineeringCommit SHA plus repository pathOpaque artifact digest and environment configuration referencesEnvironment/repository IDImmutable reviewed commit; manifest references pinned; no plaintext secret materialProposed, merged/promoted, reverted by new commit, retained by repository policyD65-UC-01, D65-UC-02
ArgoApplicationKubernetes API in Argo CD control namespace, owned by platform GitOps teamApplication UIDRepository URL, target revision/path, destination cluster and namespaceArgo CD projectSource and destination must satisfy project allowlists and sync policyDeclaratively created, updated, disabled, or deleted; finalizers/prune policy reviewed before resource deletionD65-UC-01, D65-UC-02
LiveWorkloadResourceDestination Kubernetes API, owned by Kubernetes controllers and Ziba teamResource UIDArgo CD tracking reference and image digestNamespaceLive spec should match rendered target except documented ignored differences; readiness controls trafficCreated/updated by sync, rolled back via Git, pruned only under explicit policy, deleted with app retirementD65-UC-01, D65-UC-02
SyncAndDriftEvidenceArgo CD history, Kubernetes audit, and immutable CI/incident storeOperation or incident IDCommit, Application UID, resource UID, audit event, request traceEnvironmentComparison, action, actor, observed result, and customer proof refer to one environment and revisionAppended per operation/drift, retained immutably, expired under audit policyD65-UC-01, D65-UC-02

Expand reconciliation one block at a time

The phrase “Git is the source of truth” is incomplete unless a controller repeatedly compares it with live state and reports what happened. Expand the three boxes into render, compare, policy, apply, observe, and repeat.

Automatic sync can act when target and live state differ, but pruning and self-healing are separate choices; automatic pruning is off by default, and self-heal must be explicitly enabled (Argo CD automated sync policy). Treat each as a deletion or mutation authority decision, not a convenience checkbox.

Declare the Ziba Application and workload

A GitOps controller cannot infer ownership from a directory name, so the Application must bind source, destination, and policy explicitly. Keep the workload manifest in the referenced path and pin production images by digest.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: ziba-prod
  namespace: argocd
spec:
  project: ziba-production
  source:
    repoURL: https://github.com/example/ziba-deployments.git
    targetRevision: main
    path: environments/production
  destination:
    server: https://kubernetes.default.svc
    namespace: blog
  syncPolicy:
    automated:
      enabled: true
      prune: false
      selfHeal: true
    syncOptions:
      - CreateNamespace=false
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ziba
  namespace: blog
spec:
  replicas: 2
  selector:
    matchLabels:
      app: ziba
  template:
    metadata:
      labels:
        app: ziba
    spec:
      containers:
        - name: blog
          image: europe-west2-docker.pkg.dev/ziba-prod/blog/site@sha256:REPLACE_ME
          readinessProbe:
            httpGet:
              path: /healthz
              port: 3000

Terraform and Argo CD complement each other

Treating Terraform and Argo CD as substitutes creates overlapping ownership and control loops. Give each resource exactly one primary reconciler, and connect them through explicit outputs and bootstrap dependencies.

LayerPrimary ownerTypical resourcesWhy not the other tool by default?
Cloud foundationTerraformProject services, VPC/subnets, GKE cluster, node pools, IAM, DNS foundationArgo CD needs a running Kubernetes API and should not implicitly own all provider lifecycle/state
GitOps control plane bootstrapTerraform or a dedicated platform bootstrapArgo CD installation, initial project/repository/cluster accessArgo CD cannot reconcile itself before a minimal controller exists; ownership must avoid a bootstrap loop
Kubernetes application desired stateArgo CDNamespace-scoped Deployments, Services, ConfigMaps, policy-approved CRDsTerraform apply is episodic and does not provide Argo CD’s continuous application comparison/health model
Application/domain dataApplication and data platformPosts, users, sessions, migrations, backupsNeither an HCL state file nor Git manifests should become the application database

Terraform changes infrastructure through plans and stateful provider mappings; Argo CD continuously reconciles rendered Kubernetes application state. If both manage the same Deployment, each can undo the other, destroy provenance, and create perpetual drift.

Code versus actual effects

A Git commit changes no running bytes until reconciliation is authorised and Kubernetes controllers act. This table separates source, control-plane, software, hardware, and customer consequences.

Code or actionGit/Argo control stateKubernetes software effectHardware effectZiba deployment outcome
Merge new image digestTarget revision changesNo live pod changes before syncNo immediate CPU/RAM allocationRelease is reviewable but not yet served
Argo refreshRe-renders and compares target/liveReads Kubernetes API; reports Synced/OutOfSyncNo intended hardware mutationDetects drift; does not prove health
Argo sync DeploymentRecords operation and applies desired specDeployment creates a ReplicaSet; scheduler/kubelet start replacement podsContainers consume existing node CPU/RAM; Pending pods may trigger separate node scalingNew digest serves only after readiness and Service endpoints update
selfHeal: trueAuthorises corrective sync for live driftManual spec edits can be overwritten by target stateMay restart/reschedule pods depending on changed fieldRestores reviewed target but can erase an emergency workaround unless adopted in Git
prune: trueAuthorises deletion of resources absent from targetKubernetes objects can be deleted during syncAssociated cloud load balancers or volumes may be released by their controllersCan retire stale objects or cause outage/data loss if ownership is wrong
Scale replicas from 2 to 4Target/live replica count changesScheduler creates two additional podsAdds per-pod CPU/RAM demand but does not add nodes itselfIncreases parallel capacity only if nodes can fit the pods

Prove sync, drift, and recovery

A screenshot of a green Argo CD application cannot prove the served revision or the negative path. Capture machine-readable status, Kubernetes rollout, customer response, then introduce a safe staging drift and preserve the OutOfSync diff before recovery.

argocd app get ziba-prod -o json
argocd app diff ziba-prod
argocd app sync ziba-prod
argocd app wait ziba-prod --sync --health --timeout 300
kubectl -n blog rollout status deployment/ziba
kubectl -n blog get deployment ziba -o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'
curl --fail --show-error "${ZIBA_URL}/healthz"

For the staging-only drift exercise, change a harmless annotation with an authorised emergency identity, capture the audit event and OutOfSync diff, then choose self-heal or a reviewed Git adoption. Also prove that an unapproved identity receives a Kubernetes RBAC denial. Never induce production drift merely to satisfy a course exercise.

Failure modes and decision rules

An OutOfSync status is a comparison result, not a diagnosis, and Healthy is not proof that the intended revision is live. Use both signals plus rollout and request evidence.

EvidenceInterpretationDecision
Unknown or render errorTarget manifests could not be obtained or generatedFix repository access/revision/rendering before any sync
OutOfSync after manual editLive state differs from rendered targetPreserve diff; self-heal if Git is correct, or adopt through review if emergency state is correct
Synced but DegradedDesired spec applied, workload health failedInspect rollout, readiness, events, logs, resources, and dependencies; do not call release complete
Repeated immediate driftAnother controller or defaulting webhook owns/mutates the fieldEstablish one owner or configure a narrowly justified diff rule
Prune proposes persistent resource deletionTarget removed an object with durable consequencesStop, export/backup, confirm ownership and retention, then approve explicit retirement

Decision rule: use Argo CD for continuous Kubernetes application reconciliation when Git review and drift visibility are desired; keep infrastructure in Terraform where stateful provider lifecycle is required. Never assign the same field to two reconcilers.

Key takeaways

GitOps closes a control loop between reviewed target state and live Kubernetes objects. Argo CD supplies that application loop; it does not replace the infrastructure, identity, data, or customer-health layers around it.

  • Git records target state; Argo CD renders, compares, syncs, and observes; Kubernetes controllers run the workload.
  • Synced and Healthy answer different questions, and neither alone proves the customer response.
  • Auto-sync, self-heal, and prune grant different mutation/deletion authorities.
  • Terraform and Argo CD complement each other when their resource ownership is disjoint.
  • Drift must produce evidence and a decision, not silent correction without attribution.

Checklist

A GitOps release is complete only when provenance, convergence, health, and denial are all observable. Retain the evidence under the same release identifier.

  • [ ] Application binds an allowlisted repository/path/revision to an allowlisted cluster/namespace
  • [ ] Production image is digest-pinned and manifests pass pre-merge validation
  • [ ] Auto-sync, self-heal, prune, and allow-empty are separately reviewed
  • [ ] Argo CD and Terraform ownership tables contain no overlapping resource fields
  • [ ] Captured commit, diff, operation ID, resource UIDs, health, image digest, HTTP result, time, and environment
  • [ ] Proved safe staging drift recovery and unauthorised Kubernetes mutation denial