Stop running helm upgrade by hand. Git push is the only deploy. Drift detection is automatic.
Developer edits code → push → CI builds image → push image → SSH or kubectl context → helm upgrade --set image.tag=NEW
Many of those steps are human, with admin perms on the cluster.
GitOps = git is the desired state; a controller in the cluster watches git and makes the cluster match.
helm upgrade. No human needs cluster admin to deploy.git log. Rollback is git revert.kubectl edit’d our Deployment”) becomes a continuous concern of the controller, not a quarterly review.Application CRapiVersion: argoproj.io/v1alpha1
kind: Application
metadata: { name: url-shortener, namespace: argocd }
spec:
source:
repoURL: https://github.com/me/url-shortener
path: cloud-devops/sample-app/helm
targetRevision: main
helm:
valueFiles: [values-prod.yaml]
destination:
server: https://kubernetes.default.svc
namespace: url-shortener
syncPolicy:
automated: { prune: true, selfHeal: true }
A kind: Application is the contract — “here’s a git location and a target cluster, keep them in sync”.
automated.selfHeal — the drift bitWith selfHeal: true, ArgoCD reconciles every 3 min (configurable). If a human kubectl edits the Deployment to bump replicas to 99, ArgoCD reverts it within 3 min back to what git says.
This is the deal: lose kubectl edit access; gain a controller that does it for you, with an audit trail.
spec.replicas, ArgoCD reverts to 3 immediately. Fix covered on the next slide.
ignoreDifferences rulespec:
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas
Tells ArgoCD “the HPA owns replicas; don’t reconcile that field”.
One root Application whose source is a directory full of other Application manifests. Result: one git push that creates 10 child Applications.
Standard pattern for “cluster bootstrap” — a single command brings ingress, cert-manager, monitoring, the app, all in the right order via sync waves.
metadata:
annotations:
argocd.argoproj.io/sync-wave: "-1" # before others
ArgoCD applies manifests in ascending sync-wave order:
Inside a wave, parallel. Across waves, serial — ArgoCD waits for the previous wave to be Healthy before advancing.
ApplicationSet for dev/stage/prodapiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
spec:
generators:
- list:
elements:
- { env: dev, cluster: https://...dev, valueFile: values-dev.yaml }
- { env: stage, cluster: https://...stage, valueFile: values-stage.yaml }
- { env: prod, cluster: https://...prod, valueFile: values-prod.yaml }
template:
metadata: { name: 'url-shortener-{{env}}' }
spec:
source: { ... helm: { valueFiles: ['{{valueFile}}'] } }
destination: { server: '{{cluster}}', namespace: url-shortener }
One template, three rendered Applications. Adding a qa environment is a 4-line YAML change.
values-prod.yaml to point at the new image tag, opens a PR.kubectl, no helm, no SSH.
Open ArgoCD UI → click url-shortener → see the resource tree. Anything OutOfSync is shown in yellow. Click → “Diff” → shows desired vs live side-by-side.
automated.selfHeal: true: already reverted. The diff is historical.selfHeal: false: the “Sync” button is the manual fix.Either way, drift is a 30-second look, not a quarterly audit.
sample-app/argocd/
install.yaml # ArgoCD itself; one-time install
app-url-shortener.yaml # the Application
app-of-apps.yaml # the root Application
appset-environments.yaml # ApplicationSet for dev/stage/prod
children/ # individual Application manifests for
# cert-manager, ingress-nginx, prometheus, the app
argocd-image-updater watches a registry and updates values.yaml on new tags; the canonical next step.AppProject boundaries for team isolation.Application is the contract: git location + target cluster + sync policy.selfHeal: true makes drift a 3-min concern, not a quarterly audit.