CGOA — GitOps Certified Associate
Building the Skeleton of a GitOps Repository
Goal
You build the standard layout of a GitOps config repository yourself, and see with your own eyes that the same base renders to a different final manifest for each environment. At the end, you complete the files that attach that repository to Argo CD: the Application, the AppProject, and the app-of-apps root.
Why it matters
In GitOps, repository structure is not a matter of taste but a design that determines operating cost. If the base and overlays are properly separated, the diff of the PR that promotes from stage to prod becomes a single image-tag line, and the reviewer can see with their own eyes that "the configuration stays the same and only the version goes up." Conversely, if you copy the whole YAML for each environment, dozens of lines change in every promotion PR, and nobody knows which lines are the intended differences. The Application and AppProject you create in this lab are written as files only, because the lab environment has no Argo CD CRDs, and grading works by reading those files. In real operations too, these files are artifacts committed to Git, so learning their exact form is directly what the job requires.
Steps
- Create the directories
/root/cgoa-repo/apps/checkout/base,/root/cgoa-repo/apps/checkout/overlays/dev,/root/cgoa-repo/apps/checkout/overlays/stage,/root/cgoa-repo/apps/checkout/overlays/prod, and/root/cgoa-repo/bootstrap. - In
base/deployment.yaml, write a Deployment namedcheckout—spec.replicas: 1,app: checkoutfor both the selector and the Pod labels, container nameapp, imageghcr.io/labhub/checkout:1.4.0, containerPort8080. Inbase/service.yaml, write a Service namedcheckout—spec.ports[0].port: 80,targetPort: 8080, selectorapp: checkout. Inresourcesofbase/kustomization.yaml, list the two files. - In
overlays/dev/kustomization.yaml, writeresources: [../../base],namespace: cgoa-dev, andnamePrefix: dev-. Inoverlays/stage/kustomization.yaml, writeresources: [../../base],namespace: cgoa-stage,namePrefix: stage-, and setcheckoutto2withreplicas. - In
overlays/prod/kustomization.yaml, writeresources: [../../base],namespace: cgoa-prod, andnamePrefix: prod-, setcheckoutto3withreplicas, and set thenewTagofghcr.io/labhub/checkoutto1.5.0withimages. - Save the output of
kubectl kustomize /root/cgoa-repo/apps/checkout/overlays/prodto/root/cgoa-repo/render-prod.yaml. - In
/root/cgoa-repo/bootstrap/app-checkout-prod.yaml, write an Argo CD Application —apiVersion: argoproj.io/v1alpha1,kind: Application,metadata.name: checkout-prod,metadata.namespace: argocd,spec.project: checkout,spec.source.repoURL: http://10.0.0.200/labhub/gitops.git,spec.source.path: apps/checkout/overlays/prod,spec.source.targetRevision: main,spec.destination.server: https://kubernetes.default.svc,spec.destination.namespace: cgoa-prod,spec.syncPolicy.automated.prune: true,spec.syncPolicy.automated.selfHeal: true, andCreateNamespace=trueas the first item ofspec.syncPolicy.syncOptions. - In
/root/cgoa-repo/bootstrap/root-app.yaml, write the app-of-apps root Application — nameroot, namespaceargocd,spec.project: checkout,spec.source.path: bootstrap,spec.source.directory.recurse: true,spec.destination.namespace: argocd, and automated sync (both prune and selfHeal set to true). - In
/root/cgoa-repo/bootstrap/project-checkout.yaml, write an AppProject namedcheckout—metadata.namespace: argocd,http://10.0.0.200/labhub/gitops.gitas the first item ofspec.sourceRepos,spec.destinations[0].server: https://kubernetes.default.svc,spec.destinations[0].namespace: cgoa-*, and forspec.clusterResourceWhitelist[0], group""with kindNamespace.
Notes
- To check the render, use
kubectl kustomize <경로>(the placeholder is the path). Unlikekubectl apply -k, it does not touch the cluster. - In kustomization.yaml,
replicastakes the form- name: checkout/count: 3, andimagestakes the form- name: <이미지>/newTag: "1.5.0"(the placeholder is the image name). - Common mistake 1: listing file names again in the overlay's
resourcesinstead of../../base, which copies the base. Then the promotion PR does not end at one line. - Common mistake 2: putting
namePrefixin the base. The base must be environment-independent, so the prefix is the overlay's job.
Repository directory skeleton
Create the directories /root/cgoa-repo/apps/checkout/base, /root/cgoa-repo/apps/checkout/overlays/dev, /root/cgoa-repo/apps/checkout/overlays/stage, /root/cgoa-repo/apps/checkout/overlays/prod, and /root/cgoa-repo/bootstrap.
You need one base, three environment overlays, and a bootstrap directory to hold the Application manifests. Use the mkdir option that creates parent paths in one go.
Base manifests and kustomization
In base/deployment.yaml, write a Deployment named checkout — spec.replicas: 1, app: checkout for both the selector and the Pod labels, container name app, image ghcr.io/labhub/checkout:1.4.0, containerPort 8080. In base/service.yaml, write a Service named checkout — spec.ports[0].port: 80, targetPort: 8080, selector app: checkout. In resources of base/kustomization.yaml, list the two files.
The base holds only the common denominator that is independent of the environment. It is best to decide neither the namespace nor replicas here. List the file names in the resources of kustomization.yaml.
dev and stage overlays
In overlays/dev/kustomization.yaml, write resources: [../../base], namespace: cgoa-dev, and namePrefix: dev-. In overlays/stage/kustomization.yaml, write resources: [../../base], namespace: cgoa-stage, namePrefix: stage-, and set checkout to 2 with replicas.
An overlay's resources points to the base by a relative path. In kustomization.yaml you can set a prefix field that prevents name collisions and a namespace field.
prod overlay — replicas and image tag
In overlays/prod/kustomization.yaml, write resources: [../../base], namespace: cgoa-prod, and namePrefix: prod-, set checkout to 3 with replicas, and set the newTag of ghcr.io/labhub/checkout to 1.5.0 with images.
kustomize has a separate field that changes replicas by name and a field that finds an image name and changes only its tag. You can finish inside kustomization.yaml without a patch file.
Save the render result with kubectl kustomize
Save the output of kubectl kustomize /root/cgoa-repo/apps/checkout/overlays/prod to /root/cgoa-repo/render-prod.yaml.
kubectl kustomize <디렉터리> (the placeholder is the directory) prints the final manifest with the overlay applied to standard output. You can see the desired state with your own eyes without applying anything to the cluster.
Argo CD Application manifest
In /root/cgoa-repo/bootstrap/app-checkout-prod.yaml, write an Argo CD Application — apiVersion: argoproj.io/v1alpha1, kind: Application, metadata.name: checkout-prod, metadata.namespace: argocd, spec.project: checkout, spec.source.repoURL: http://10.0.0.200/labhub/gitops.git, spec.source.path: apps/checkout/overlays/prod, spec.source.targetRevision: main, spec.destination.server: https://kubernetes.default.svc, spec.destination.namespace: cgoa-prod, spec.syncPolicy.automated.prune: true, spec.syncPolicy.automated.selfHeal: true, and CreateNamespace=true as the first item of spec.syncPolicy.syncOptions.
An Application has three chunks: source (from where), destination (to where), and syncPolicy (how). Check where the automated sync, self-heal, and automatic namespace creation options go.
app-of-apps root Application
In /root/cgoa-repo/bootstrap/root-app.yaml, write the app-of-apps root Application — name root, namespace argocd, spec.project: checkout, spec.source.path: bootstrap, spec.source.directory.recurse: true, spec.destination.namespace: argocd, and automated sync (both prune and selfHeal set to true).
The root points at the directory holding the child Application files with source.path and is configured to read that directory recursively. The destination is the namespace where Argo CD itself lives.
Draw the boundary with an AppProject
In /root/cgoa-repo/bootstrap/project-checkout.yaml, write an AppProject named checkout — metadata.namespace: argocd, http://10.0.0.200/labhub/gitops.git as the first item of spec.sourceRepos, spec.destinations[0].server: https://kubernetes.default.svc, spec.destinations[0].namespace: cgoa-*, and for spec.clusterResourceWhitelist[0], group "" with kind Namespace.
An AppProject restricts which repositories, which clusters and namespaces, and which cluster-scoped resources the Applications in this project may use. Its name must match the project value of the Application you created in step 6.