Assembling an Application Manifest
Goal
Build an Argo CD Application manifest to completion one field at a time, then put the namespace and Deployment that the app will deploy into a real cluster, and confirm by hand even how Argo CD recognizes its own resources.
Why it matters
An Application is a declaration of "what (source) to align, where (destination), and by what rules (syncPolicy)." It matters more to know what incident each field exists to prevent than to memorize field names. If you do not turn on prune, resources deleted from Git remain in the cluster forever; if you turn on selfHeal, values changed by hand are reverted; and if you use an HPA without ignoreDifferences, Argo CD and the HPA fight endlessly over replicas. The file you build in this lab is stacked up in the order that prevents those incidents one by one.
Steps
- Create the
/root/capa-app/directory and createapplication.yamlin it. The apiVersion isargoproj.io/v1alpha1, the kind isApplication,metadata.nameisguestbook, andmetadata.namespaceisargocd. - In the same file, fill in
spec.projectascapa-demo,spec.source.repoURLashttps://gitea.homelab.internal/platform/guestbook.git,spec.source.targetRevisionasmain, andspec.source.pathasoverlays/prod. - Set
spec.destination.servertohttps://kubernetes.default.svcandspec.destination.namespacetocapa-guestbook. Do not usedestination.name. - Set both
spec.syncPolicy.automated.pruneandspec.syncPolicy.automated.selfHealtotrue, and put two items,CreateNamespace=trueandPruneLast=true, inspec.syncPolicy.syncOptions. - Set
spec.syncPolicy.retry.limitto5,spec.syncPolicy.retry.backoff.durationto5s,factorto2, andmaxDurationto3m. - In the first item of
spec.ignoreDifferences, putgroup: apps,kind: Deployment, and/spec/replicasinjsonPointers, to exclude the field managed by the HPA from the comparison. - Actually create the namespace
capa-guestbookin the cluster and attach the labelapp.kubernetes.io/part-of=capa. - Actually create the Deployment
guestbook-uiin the namespacecapa-guestbook. Setreplicasto2, the container image tonginx:1.27, and attachargocd.argoproj.io/tracking-idas the Deployment's own annotation in Argo CD's format. The app name isguestbook.
Notes
- For file verification, it is quick to extract just a part, as in
yq '.spec.syncPolicy' /root/capa-app/application.yaml. - For the real resources, it is less error-prone to extract a skeleton with
kubectl create ... --dry-run=client -o yamland then edit it. - Common mistake 1: writing
syncOptionsas a map. It is an array of strings. - Common mistake 2: how to write the core group in the tracking ID. The form for a resource in the apps group differs from the form for a resource in the core group.
Working directory and the Application skeleton
Create the /root/capa-app/ directory and create application.yaml in it. The apiVersion is argoproj.io/v1alpha1, the kind is Application, metadata.name is guestbook, and metadata.namespace is argocd.
An Application is a custom resource in the argoproj.io group. metadata.namespace is where the Application object itself lives, not the deployment target, and it is usually the namespace where Argo CD is installed.
source — what to fetch
In the same file, fill in spec.project as capa-demo, spec.source.repoURL as https://gitea.homelab.internal/platform/guestbook.git, spec.source.targetRevision as main, and spec.source.path as overlays/prod.
Under spec.source go three things: the repository address, the revision, and the path. targetRevision accepts a branch, a tag, or a commit SHA, and in production it is safer to use a fixed name than HEAD.
destination — where to put it
Set spec.destination.server to https://kubernetes.default.svc and spec.destination.namespace to capa-guestbook. Do not use destination.name.
There is a fixed address used when deploying inside the same cluster. server and name are two ways of pointing at the same thing, so you must not use them at the same time.
Automated sync and syncOptions
Set both spec.syncPolicy.automated.prune and spec.syncPolicy.automated.selfHeal to true, and put two items, CreateNamespace=true and PruneLast=true, in spec.syncPolicy.syncOptions.
The two booleans under automated decide, respectively, "should what was deleted from Git also be deleted from the cluster?" and "should values changed by hand be reverted?" syncOptions is an array of strings written in key=value form.
Designing the retry backoff
Set spec.syncPolicy.retry.limit to 5, spec.syncPolicy.retry.backoff.duration to 5s, factor to 2, and maxDuration to 3m.
The backoff starts at duration, grows by a factor each time, and stops at maxDuration. Multiply it out yourself and see whether the required values give 5, 10, 20, 40, and 80 seconds.
Exclude the HPA-managed field from the diff
In the first item of spec.ignoreDifferences, put group: apps, kind: Deployment, and /spec/replicas in jsonPointers, to exclude the field managed by the HPA from the comparison.
ignoreDifferences is an array, and each item narrows the target with group/kind and then points at fields with jsonPointers. A JSON Pointer writes the path with slashes, not dots.
Actually create the target namespace
Actually create the namespace capa-guestbook in the cluster and attach the label app.kubernetes.io/part-of=capa.
From here on it is a real cluster, not files. You can create it with kubectl create namespace and then attach the label, or write a manifest and apply it.
A Deployment with the tracking annotation attached
Actually create the Deployment guestbook-ui in the namespace capa-guestbook. Set replicas to 2, the container image to nginx:1.27, and attach argocd.argoproj.io/tracking-id as the Deployment's own annotation in Argo CD's format. The app name is guestbook.
The tracking ID format is APP_NAME:GROUP/KIND:NAMESPACE/NAME. Note that because this is a Deployment in the apps group, the GROUP slot is not empty.