SaveMyCert
Log in
5 of 5 free questions left today·for 10 a day
Cloud Native Application Delivery

Cloud Native Application Delivery: GitOps, Helm, and Deployment Strategies

17 min readKCNA · Cloud Native Application DeliveryUpdated

Cloud native application delivery is the process that turns source code into running Pods: build a container image, push it to a registry under an immutable reference, and apply declarative manifests that Kubernetes reconciles into reality. Deployments update workloads with a rolling update by default (paced by maxSurge and maxUnavailable) or with Recreate when versions must never overlap, and patterns such as blue-green and canary layer safer release styles on top. GitOps makes a Git repository the single source of truth and lets an in-cluster controller - Argo CD or Flux - pull and continuously reconcile the cluster to match it. Helm packages applications as charts installed as releases with numbered revisions, while Kustomize customizes plain YAML with overlays and no templates. This lesson covers each piece at the depth KCNA expects, with enough commands and YAML to make the concepts concrete.

What you’ll learn
  • Describe the delivery pipeline from image build to registry to running Pods, and why image references must be immutable
  • Calculate how maxSurge and maxUnavailable bound a rolling update, and identify when only Recreate meets the requirement
  • Distinguish rolling update, recreate, blue-green and canary, including how a plain Service splits traffic
  • Use kubectl rollout and helm history/rollback to observe, gate and reverse a release
  • Name the four OpenGitOps principles and explain how CI, pull requests and an in-cluster agent work together
  • Explain how Argo CD and Flux detect and revert drift, and how the two tools differ
  • Define Helm charts, chart archives, values, releases and revisions, and compare Helm templating with Kustomize overlays
  • Place Cloud Native Buildpacks, Harbor, Helm, Kustomize, Argo CD and Flux in the build, store, define, deliver chain

From source code to running Pods

Every cloud native delivery workflow, whatever tools implement it, follows the same three-step shape. First, build: source code plus a Dockerfile (or a buildpack) produces a container image that conforms to the OCI image standard. Second, push: the image is uploaded to a container registry such as Docker Hub, a cloud provider registry, or a self-hosted registry, under a reference like registry.example.com/shop/web:1.4.2. Third, deploy: Kubernetes manifests that reference that image are applied to the cluster, and controllers reconcile the running state to match them.

Who touches the image matters. The API server never downloads images - it only stores the Pod spec that names one. Once the scheduler places a Pod on a node, that node's kubelet asks the container runtime (containerd or CRI-O) to pull the image from the registry and start the containers. That is why a registry outage or a missing pull secret shows up as an image-pull error on the Pod, not as a rejected manifest.

Why image references must be immutable

A tag is a movable pointer, not a fixed build. If a Deployment uses shop/web:latest and someone pushes a new build under latest, any Pod that is rescheduled or scaled out later pulls whatever latest points to then - so two Pods of the same Deployment can silently run different code, and "roll back" has nothing distinct to roll back to. A per-environment tag such as prod or stable has exactly the same problem: it is as mutable as latest. The fix is to reference a unique, never-reused version tag (for example 1.4.2 or a Git commit SHA) or, strongest of all, an image digest (shop/web@sha256:…), which identifies one exact image content. Changing imagePullPolicy does not fix a moving tag - it only changes when the kubelet pulls (the default is Always for :latest or an untagged image and IfNotPresent otherwise), not which build the tag resolves to.

The deploy step is where Kubernetes differs from older platforms. You do not script a sequence of imperative server commands; you declare the desired state in YAML and hand it to the API server. The Deployment controller then works out the safe order of operations - creating a new ReplicaSet, scaling Pods up and down - on your behalf. Delivery tooling is therefore mostly about two questions: who applies the manifests (a pipeline pushing, or a controller pulling) and how the manifests are produced (written by hand, templated by Helm, or patched by Kustomize).

Keep the vocabulary straight. The image lives in a registry. The manifest describes desired state. The cluster holds actual state. Delivery is the discipline of moving changes through those three layers repeatably, and everything else in this lesson - strategies, GitOps, Helm - is a refinement of that flow.

Rolling updates and recreate: the built-in strategies

A Deployment has a strategy field with exactly two built-in options: RollingUpdate (the default) and Recreate. When you change the Pod template (most often the image), a RollingUpdate creates a new ReplicaSet and shifts replicas over gradually, keeping the application available throughout. New Pods must pass their readiness probes before they count as available, so the old ReplicaSet only shrinks as replacements actually become ready.

maxSurge and maxUnavailable, worked through

Two settings bound the pace of a rolling update, each an absolute number or a percentage of the desired replicas (both default to 25%):

  • maxSurge sets the ceiling: total Pods may reach replicas + maxSurge. A percentage rounds up to a whole Pod.
  • maxUnavailable sets the floor: available Pods may not drop below replicas - maxUnavailable. A percentage rounds down.

With 10 replicas and the defaults, 25% gives a surge of 3 (2.5 rounded up) and an unavailability of 2 (2.5 rounded down), so the rollout runs with at most 13 Pods and at least 8 available. With 4 replicas, maxSurge: 1 and maxUnavailable: 0, the Deployment adds one new Pod, waits for it to be ready, removes one old Pod, and repeats - never fewer than 4 serving, never more than 5 in total.

SettingEffectWhen to use it
maxSurge: 1, maxUnavailable: 0Full capacity throughout; needs room for one extra PodCapacity must never dip and the cluster has spare resources
maxSurge: 0, maxUnavailable: 1Never exceeds the replica count; capacity dips by one PodThe cluster is full and a surge Pod would sit Pending
Both 0Rejected by API validation - the rollout could never progressNever valid

A surge Pod is a real Pod: it needs a node with free CPU and memory for its requests. On a cluster with no headroom, surge Pods stay Pending and the rollout stalls, so maxSurge: 0 with a non-zero maxUnavailable is the way to update in place.

Recreate: the only no-overlap option

Recreate terminates every old Pod first, then starts the new ones. No RollingUpdate setting - not even maxSurge: 0 or maxUnavailable: 0 - guarantees that old and new Pods never overlap: one ReplicaSet scales down while the other scales up, and RollingUpdate does not wait for old Pods to finish terminating before starting new ones. So when two versions must never run at the same time (an incompatible schema migration, a single-writer lock, a licence limited to one instance), Recreate is the only built-in strategy that guarantees it. The price is downtime for the whole gap between the last old Pod stopping and the first new Pod becoming ready.

Observing and controlling a rollout

kubectl rollout status deployment/web
kubectl rollout history deployment/web
kubectl rollout undo deployment/web
kubectl rollout undo deployment/web --to-revision=3
kubectl rollout pause deployment/web
kubectl rollout resume deployment/web

status blocks until the rollout finishes and exits zero; if the Deployment makes no progress within progressDeadlineSeconds (default 600), it reports the failure and exits non-zero. That makes it the natural gate in a pipeline step - unlike kubectl get deployments, which prints a one-off snapshot and succeeds whatever state it shows. history lists past revisions; undo re-instates the previous (or a named) Pod template, and is fast because the old ReplicaSet still exists, scaled to zero. pause lets you batch several template edits into a single rollout, and resume starts it.

Blue-green and canary: release patterns beyond the built-ins

Blue-green and canary are release patterns, not values of the Deployment strategy field. You assemble them from Kubernetes primitives or use tooling that automates them.

In a blue-green deployment you run two complete environments: blue (live) and green (the new version). Green is deployed alongside blue, tested in place, and then traffic is switched all at once - in plain Kubernetes, typically two Deployments whose Pods carry version: blue or version: green, and a Service whose selector you change from one value to the other. Rollback is the same switch in reverse, which is why it is near-instant. The cost is capacity: you briefly run two full copies of the application.

One detail trips people up: a Service selector is a set of labels that a Pod must match all of (they are ANDed). A selector of app: web, version: green picks only green Pods. Adding a second version value is not possible - a map key appears once - and adding another label that no Pod carries simply matches nothing, leaving the Service with no endpoints. To switch, you replace the version value, not add to it.

A canary release sends a small slice of real traffic to the new version while the rest stays on the stable one; if error rates and latency stay healthy, the slice grows until the canary takes all traffic. In plain Kubernetes the only lever is Pod count: if one Service selects both the stable and canary Pods, kube-proxy spreads connections roughly evenly across ready endpoints, so the traffic share follows the Pod ratio - 1 canary Pod beside 4 stable Pods receives about 20%, and 1% would need 99 stable Pods. Precise percentage splits independent of replica counts need a service mesh such as Istio or Linkerd (or Gateway API weighted routes), and Argo Rollouts automates the whole progression, with analysis steps that can abort a bad release.

StrategyHow it worksOld and new overlap?DowntimeRollbackExtra cost
Rolling updateGradually replaces old Pods with new (default)Yes, during the rolloutNonekubectl rollout undoSurge capacity (if maxSurge > 0)
RecreateStops all old Pods, then starts new onesNoYeskubectl rollout undo (with downtime)None
Blue-greenFull new environment, switch all traffic at onceBoth run; only one gets trafficNoneSwitch the selector backDouble capacity during release
CanarySmall traffic share to new version, grown in stepsYes, both serve trafficNoneRoute traffic back to stableTraffic-splitting tooling for fine-grained shares

Match the description to the name: two environments and an instant switch is blue-green; a small share of users on the new version, grown gradually, is a canary.

CI/CD fundamentals: the push model

Continuous integration (CI) is the automated path from a code change to a tested artifact: every push to the repository triggers a pipeline that runs tests, builds the container image, scans it, and pushes it to the registry. Continuous delivery (CD) keeps every change in a deployable state and automates the release up to production, where a human approval usually gates the last step. Continuous deployment removes that gate: every change that passes the pipeline goes to production automatically.

In the classic push model, the pipeline itself performs the deployment: a job at the end of the pipeline holds credentials for the cluster and runs kubectl apply or helm upgrade against it, often followed by kubectl rollout status so the job fails if the rollout does not complete. Familiar CI systems - Jenkins, GitHub Actions, GitLab CI - work this way, and the cloud native ecosystem includes Tekton (a Continuous Delivery Foundation project), a Kubernetes-native CI/CD framework whose pipeline steps run as Pods. Tekton builds and runs pipelines; it is not a GitOps reconciler.

The push model is simple and universal, but it has structural weaknesses. Cluster credentials must live in the CI system, widening the attack surface. The pipeline only knows the cluster state at the moment it pushes; if someone later edits a Deployment by hand with kubectl edit, nothing notices or corrects the drift. And reconstructing what is deployed means digging through pipeline logs rather than reading a single declarative source.

Those weaknesses are what the GitOps pull model, covered next, removes. The distinguishing feature of push is a pipeline outside the cluster that holds credentials and applies changes; the distinguishing feature of GitOps is an agent inside the cluster that pulls from Git.

GitOps: Git as the single source of truth

GitOps manages infrastructure and applications by keeping the entire desired state in a Git repository and letting software agents continuously reconcile the live system to match it. The OpenGitOps project, started by the CNCF GitOps Working Group, defines four principles. Each one is easiest to remember by what breaks it:

PrincipleMeaningExample that violates it
DeclarativeThe system's desired state is expressed declarativelyA runbook of imperative kubectl scale and kubectl set image commands
Versioned and immutableDesired state is stored so that it keeps a complete version history and past versions cannot be alteredManifests kept on a shared network drive and edited in place
Pulled automaticallySoftware agents fetch the desired state from the sourceA CI job that holds cluster credentials and pushes manifests in
Continuously reconciledAgents keep observing actual state and act to match the desired stateA tool that applies once at deploy time and never checks again

How CI and GitOps fit together

GitOps does not replace CI - it replaces the deploy step at the end of it. CI still tests, builds and pushes the image; instead of applying it to the cluster, CI (or a person, or an image-automation controller) opens a pull request that bumps the image tag in the configuration repository. Review and merge of that pull request is the approval, and the Git history is the audit record of who changed what and when. A controller running inside the cluster notices the new commit and applies it. Deploying is a merge; rolling back is git revert followed by the same reconciliation. Cluster credentials never leave the cluster.

Drift: what gets reverted, and when

Drift is any difference between the live cluster and Git - typically a hand edit with kubectl edit or kubectl scale. Whether drift is corrected automatically depends on how the controller is configured:

  • Argo CD always detects drift and marks the application OutOfSync. It only reverts live edits on its own when the application uses automated sync with selfHeal enabled; otherwise someone must trigger a sync.
  • Flux: the kustomize-controller re-applies the desired state from Git on every reconciliation interval, so manual changes to the objects a Kustomization manages are overwritten at the next run. For objects rendered by a HelmRelease, in-cluster drift correction is opt-in: the helm-controller only detects or reverts manual edits when the release's driftDetection mode is enabled.

A consequence that surprises people: an imperative rollback on a GitOps-managed object - kubectl rollout undo, or helm rollback on a release the controller owns - is itself drift. Git still declares the new version, so a self-healing controller puts it straight back. The durable rollback is to revert the commit in Git.

Argo CD and Flux

Both are graduated CNCF projects and both pull from Git and reconcile, rendering plain YAML, Kustomize or Helm. They differ in shape:

Argo CDFlux
ArchitectureAn application controller built around the Application resource, plus an API serverA toolkit of separate controllers - source, kustomize, helm, notification (plus optional image automation)
InterfaceWeb UI showing sync and health status per application, plus a CLIConfigured through Kubernetes custom resources (GitRepository, Kustomization, HelmRelease) and the flux CLI; dashboards come from separate add-on projects
AspectPush (pipeline CD)Pull (GitOps)
Who applies changesExternal pipeline jobController inside the cluster
Cluster credentialsStored in the CI systemStay inside the cluster
Drift handlingUndetected until the next pipeline runDetected continuously; reverted when self-healing/periodic reapply is on
RollbackRe-run an old pipelinegit revert, controller reconciles
Audit trailPipeline logsGit commit and pull request history

Helm: the Kubernetes package manager

Helm, a graduated CNCF project, is the package manager for Kubernetes. A real application is a bundle of many manifests - Deployment, Service, ConfigMap, Ingress - that you want to install, upgrade and remove as one unit, with environment-specific settings injected. Helm's vocabulary maps onto that problem, and each noun is a different thing:

TermWhat it is
ChartThe package: a directory with Chart.yaml (name and chart version), values.yaml (default configuration) and templates/ (manifest templates)
Chart archiveA chart packaged by helm package into a versioned .tgz file, the form published to chart repositories and OCI registries
Values fileA YAML file of configuration (for example prod-values.yaml) passed with -f/--values, overriding the chart's defaults; --set overrides single keys
ReleaseOne installed instance of a chart in a cluster, under a name; the same chart can be installed many times as separate releases
RevisionA numbered version of a release; every install, upgrade and rollback creates a new one

Templates use Go templating: placeholders such as {{ .Values.replicaCount }} or {{ .Release.Name }} are filled in when the chart is rendered, so one chart serves dev, staging and production with different values files. Because of those placeholders, a template file is not valid Kubernetes YAML until Helm renders it - helm template renders the manifests locally without installing anything, which is how you inspect or lint the real output.

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install monitoring prometheus-community/kube-prometheus-stack -f prod-values.yaml
helm list
helm upgrade monitoring prometheus-community/kube-prometheus-stack -f prod-values.yaml --set grafana.enabled=false
helm history monitoring
helm rollback monitoring 2
helm uninstall monitoring

install creates a release (revision 1); upgrade moves it to a new chart version or new values (revision 2, 3, …). helm history lists each revision with its status and chart version, which is how you find the last good one. helm rollback NAME REVISION re-deploys that revision's configuration - and records the rollback as a new revision rather than deleting later ones, so the history stays complete (rolling back to revision 2 from revision 4 produces revision 5). helm uninstall removes every resource the release owns. Public charts are shared through chart repositories and OCI registries and discovered on Artifact Hub, which is how you install third-party software - databases, ingress controllers, monitoring stacks - without writing their manifests yourself.

Kustomize and the wider application definition landscape

Kustomize is the template-free alternative for customizing manifests, and it is built into kubectl. Instead of templates with placeholders, you keep a base of complete, valid YAML manifests and write overlays that patch the base for each environment. An overlay is itself plain YAML: a kustomization.yaml file that references the base and declares the differences.

Your team runs the same web app in staging and production. The base holds the Deployment and Service; the production overlay raises replicas and pins an image version:

resources:
- ../../base
replicas:
- name: web
  count: 5
images:
- name: shop/web
  newTag: 1.4.2

Three kubectl commands work with that directory:

  • kubectl kustomize overlays/production renders base plus patches and prints the result - nothing is sent to the cluster.
  • kubectl diff -k overlays/production previews what applying would change in the live cluster.
  • kubectl apply -k overlays/production renders and applies.

Because no file ever contains a placeholder, every layer of a Kustomize setup is valid YAML that standard YAML linters and policy tools can check as it sits in the repository. Helm templates cannot be checked that way until they are rendered. That trade - less templating power, but always-valid YAML - is the core of the choice:

  • Choose Helm when you need packaging and distribution: versioned charts, install and rollback of releases, shipping software to other teams, or consuming third-party charts.
  • Choose Kustomize when you own plain manifests and only need per-environment variation without a templating language, or when policy requires every committed file to be plain YAML.
  • They also combine: Argo CD and Flux can render either, and Kustomize can post-process Helm output.

The image and registry landscape

Delivery tooling also covers how images are built and stored, and these projects do different jobs:

ProjectJob
Cloud Native Buildpacks (CNCF)Builds OCI images from source code without a Dockerfile, detecting the language and assembling the layers
Harbor (CNCF graduated)A registry: stores images and charts, scans them for vulnerabilities, supports signing and replication between registries
Helm / KustomizeProduce the manifests that reference those images
Argo CD / FluxApply those manifests from Git (GitOps)

Recognition is the goal here: know what each tool is for and which stage of build, store, define, deliver it belongs to.

Tip. KCNA tests this topic mostly with recognition and short scenario questions: which strategy is the Deployment default and which one guarantees old and new versions never overlap, how many Pods maxSurge and maxUnavailable allow, which release pattern runs two full environments versus a small traffic share, and how a plain Service splits traffic between versions. GitOps questions cover the four OpenGitOps principles, the pull model versus a pipeline that pushes with cluster credentials, what happens to drift and to an imperative rollback under Argo CD or Flux, and how CI and pull requests fit in. Helm questions use its vocabulary - chart, values, release, revision - and rollback behaviour; Kustomize questions contrast template-free overlays with Helm. Expect landscape questions placing Buildpacks, Harbor, Tekton, Argo CD and Flux, and image-reference questions on mutable tags versus digests.

Key takeaways
  • RollingUpdate is the default Deployment strategy; no RollingUpdate setting guarantees old and new versions never overlap, so only Recreate does - at the cost of downtime
  • maxSurge sets the ceiling (replicas + surge, percentages round up) and maxUnavailable the floor (replicas - unavailable, percentages round down); both cannot be 0, and on a full cluster use maxSurge 0
  • kubectl rollout status blocks and exits non-zero when progressDeadlineSeconds passes, making it a pipeline gate; kubectl rollout undo returns to the previous revision
  • Blue-green switches a Service selector between full environments; without a mesh, a canary's traffic share follows its Pod ratio
  • Pin images by a unique version tag or digest - latest and per-environment tags like prod move, and imagePullPolicy only changes when the kubelet pulls
  • The four OpenGitOps principles: declarative, versioned and immutable, pulled automatically, continuously reconciled; deploys are merged pull requests and rollback is git revert
  • Drift is reverted only when the controller is set to do so (Argo CD automated sync with selfHeal; Flux Kustomizations reapply each interval, while HelmRelease drift correction is opt-in) - an imperative rollback on a GitOps-managed object is itself drift
  • Helm: chart (package), chart archive (.tgz), values, release (installed instance), revision; helm rollback records a new revision
  • Kustomize keeps every file valid YAML: kubectl kustomize renders, kubectl diff -k previews, kubectl apply -k applies
  • Cloud Native Buildpacks build images without a Dockerfile; Harbor stores, scans and signs them; Tekton runs CI pipelines, Argo CD and Flux do GitOps

Frequently asked questions

What is the default deployment strategy in Kubernetes?

RollingUpdate is the default strategy for a Deployment. It replaces old Pods with new ones gradually, bounded by maxSurge and maxUnavailable (both default 25%), and only counts new Pods once they pass readiness probes, so the application stays available during the update. Old and new versions run side by side during the rollout. The only other built-in strategy is Recreate, which terminates all old Pods before starting new ones, so versions never overlap but the application is down in between.

What is GitOps and how is it different from traditional CI/CD?

GitOps is an operating model where the entire desired state of a system lives in a Git repository and an agent inside the cluster pulls and continuously reconciles the live state to match it. Traditional CI/CD uses a push model: an external pipeline holds cluster credentials and applies changes at the end of a run. With GitOps, CI still builds and pushes images but then opens a pull request that changes the manifests; merging it is the approval and the audit record, credentials stay in the cluster, and rollback is a git revert.

What are Argo CD and Flux?

Argo CD and Flux are the two main GitOps tools in the cloud native ecosystem, and both are graduated CNCF projects. Each runs inside a Kubernetes cluster, watches a Git repository of plain manifests, Helm charts or Kustomize overlays, and keeps the cluster synchronized with it. Argo CD is built around an Application controller and offers a web UI showing per-application sync and health status. Flux is a toolkit of separate controllers - source, kustomize, helm and notification - configured entirely through Kubernetes custom resources.

What is the difference between Helm and Kustomize?

Helm is a package manager: it bundles an application's manifests into a versioned chart, fills Go templates with values at render time, and tracks each installation as a release you can upgrade and roll back. Kustomize is a template-free customization tool built into kubectl: you keep a base of plain, valid YAML and apply per-environment overlays that patch it. Because Helm templates are not valid YAML until rendered, teams that require every committed file to be lintable plain YAML favour Kustomize; teams that need to package and distribute software favour Helm.

What is a Helm release?

A Helm release is a single installed instance of a chart in a cluster, created by helm install under a release name; the same chart can be installed many times as independent releases. Every install, upgrade and rollback records a new numbered revision, which helm history lists. helm rollback NAME REVISION re-deploys an earlier revision's configuration and records that as a new revision, and helm uninstall removes every resource the release owns.

What is a canary deployment?

A canary deployment releases a new version to a small share of real traffic while most users stay on the stable version, then grows the share in steps if error rates and latency stay healthy. In plain Kubernetes, a Service that selects both versions spreads traffic roughly evenly across ready Pods, so the share follows the Pod ratio - one canary Pod beside four stable Pods gets about 20%. Precise percentages independent of replica counts need a service mesh such as Istio or Linkerd, or a progressive delivery controller such as Argo Rollouts.

What do maxSurge and maxUnavailable do in a Kubernetes rolling update?

maxSurge is how many Pods a Deployment may run above its desired replica count during a rolling update, and maxUnavailable is how many may be unavailable below it; both default to 25%, with maxSurge rounding up and maxUnavailable rounding down. With 10 replicas the defaults allow up to 13 Pods and require at least 8 available. They cannot both be 0. On a cluster with no spare capacity, set maxSurge to 0 so no extra Pod has to be scheduled.

Why should Kubernetes manifests avoid the latest image tag?

A tag such as latest, or a per-environment tag such as prod, is a movable pointer: when a new build is pushed under it, any Pod scheduled afterwards pulls the new build while older Pods keep the old one, so one Deployment can run mixed code and rollbacks become ambiguous. Referencing a unique version tag that is never reused, or an image digest, pins one exact image. Changing imagePullPolicy does not solve this; it only changes when the kubelet pulls the image.

Source

This lesson covers the "Cloud Native Application Delivery" domain of the official KCNA exam guide. Vendors revise their guides — check the source for the current version.

Test yourself on this topic
Practice now

Sign up free to mark lessons complete, bookmark topics and track your exam readiness.

Spotted a mistake in this lesson?