This directory makes the NetShop platform GitOps-managed: the cluster's
desired state lives in git, and a controller (Argo CD) continuously reconciles
the live cluster toward it. Nobody runs helm install or kubectl apply
against production by hand -- you open a pull request, merge it, and Argo CD
rolls it out with automated sync, self-heal, and prune.
Conventions (shared with the rest of the repo): namespace
netshop, image registryghcr.io/netshop-lab, and every object carriesapp.kubernetes.io/part-of: netshop. CRD-backed kinds (argoproj.io/...) are validated withkubeconform -ignore-missing-schemas.
kubectl apply (once, at bootstrap)
|
v
+----------------------+
| netshop-root (App) | <- the only thing applied by hand
| path: gitops/ |
| applications/ |
+----------+-----------+
| syncs the directory of child Applications
+---------------+----------+----------+------------------+
v v v v
+--------------+ +--------------+ +---------------------+ +------------------+
| kyverno | | monitoring | | security-policies | | netshop-gke / |
| (wave -1) | | (wave 0) | | (wave 1) | | -digitalocean |
| policy engine| | kube-prom- | | ClusterPolicies + | | (wave 2) |
| | | stack | | image verification | | the Helm chart |
+--------------+ +--------------+ +---------------------+ +------------------+
appproject.yaml-- theAppProjectnamednetshop. The security boundary: which repos may be deployed from, which (cluster, namespace) pairs may be deployed to, and which cluster-scoped kinds are permitted. Every Application referencesproject: netshop.root-app.yaml-- the rootApplication("app-of-apps"). Its source is thegitops/applications/directory, which contains nothing but moreApplicationmanifests. Apply this one object and the whole platform unfolds.applications/-- the child Applications (leaves):addon-kyverno.yaml-- installs the Kyverno admission controller.addon-monitoring.yaml-- installs kube-prometheus-stack, fed by the project's own values file (k8s/monitoring/...) via the Argo CD multi-source$valuespattern.addon-security-policies.yaml-- applies thesecurity/policies+security/signingmanifests authored by the supply-chain unit.netshop-gke.yaml/netshop-digitalocean.yaml-- deploy the NetShop Helm chart (helm/netshop) with the matching cloud values file layered on top ofvalues.yaml.
rollouts/web-rollout.yaml-- an optional Argo Rollouts example doing a metric-gated canary of thewebedge service (with a commented blue-green alternative).
- One bootstrap command brings up the entire platform.
- Children are declarative + version-controlled -- adding a component is a
single new file in
applications/, reviewed and merged like any code. - Ordering via sync-waves -- the policy engine and policies land before
the first NetShop pod is admitted (
kyvernowave-1,security-policieswave1, workloads wave2).
syncPolicy:
automated:
prune: true # objects deleted from git are deleted from the cluster
selfHeal: true # manual `kubectl edit` drift is reverted to match git
allowEmpty: false # never prune everything if git accidentally goes empty- Automated sync -- merges to the tracked branch deploy themselves; no
out-of-band
helm upgrade. - Self-heal -- the live cluster is forced back to git on any drift, so the repo is always the single source of truth.
- Prune -- removing a manifest from git removes the resource from the
cluster (with
PruneLast=true, only after the new state is healthy).
ServerSideApply=true is used for the CRD-bearing charts (Kyverno,
kube-prometheus-stack) to avoid the client-side apply annotation size limit.
The workload Applications ignoreDifferences on Deployment.spec.replicas so
Argo CD does not fight the HorizontalPodAutoscaler.
# 1. Install Argo CD (one-time, into its own namespace).
kubectl create namespace argocd
kubectl apply -n argocd \
-f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# 2. (Optional) Install the Argo Rollouts controller for the canary demo.
kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts \
-f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml
# 3. Apply the project + the app-of-apps. Everything else follows.
kubectl apply -n argocd -f gitops/appproject.yaml
kubectl apply -n argocd -f gitops/root-app.yaml
# 4. Watch it converge.
argocd app list
argocd app get netshop-root
kubectl argo rollouts get rollout web -n netshop --watch # if using RolloutsPick one cloud target.
netshop-gkeandnetshop-digitaloceanboth deploy into the samenetshopnamespace on the in-cluster API server, so enable exactly one per Argo CD instance (delete the other file, or point itsdestination.serverat a different registered cluster). The chart'sglobal.imageRegistryis overridden back toghcr.io/netshop-labhere; in a real pipeline the CI "image bump" commit pins an immutable digest so the deployed artifact is exactly the one cosign signed.
for f in $(find gitops -name '*.yaml'); do
python3 -c "import yaml; list(yaml.safe_load_all(open('$f')))" # well-formed YAML
kubeconform -strict -ignore-missing-schemas -kubernetes-version 1.30.0 "$f"
done-ignore-missing-schemas is required because Application, AppProject,
Rollout and AnalysisTemplate are CRDs (group argoproj.io) whose schemas
are not in the upstream Kubernetes OpenAPI bundle.
Argo CD is used here, but the same outcome is achievable with Flux. The mapping is roughly:
| Concern | Argo CD | Flux |
|---|---|---|
| Track a git repo | Application.spec.source |
GitRepository (source-controller) |
| Apply Kustomize/manifests | Application (directory) |
Kustomization (kustomize-controller) |
| Deploy a Helm chart | Application (helm) |
HelmRepository + HelmRelease |
| App-of-apps / fan-out | root Application |
a Kustomization pointing at a dir of more Kustomizations |
| Multi-tenancy boundary | AppProject |
per-tenant Kustomization + RBAC + --service-account impersonation |
| Self-heal / prune | automated.selfHeal/prune |
Kustomization.spec.prune: true + wait |
| Progressive delivery | Argo Rollouts | Flagger |
A Flux equivalent of the root app would be a GitRepository pointing at this
repo plus a Kustomization whose path is gitops/applications with
prune: true and interval: 5m. Flux leans on Kustomize as its native
packaging (Helm via HelmRelease), favours pull-based reconciliation with no
built-in UI, and uses Flagger rather than Argo Rollouts for canary/
blue-green. Choose Argo CD for its UI/RBAC/app-of-apps ergonomics; choose Flux
for a lighter, controller-only, GitOps-Toolkit-composable footprint.
rollouts/web-rollout.yaml replaces the web Deployment with a Rollout
that:
- shifts traffic 10% -> 40% -> 70% -> 100% with pauses between steps;
- runs an
AnalysisTemplatethat queries Prometheus for the canary's HTTP success rate and auto-aborts + rolls back if it drops below 95%; - keeps the hardened pod
securityContext(runAsNonRoot, read-only rootfs, drop ALL caps) so canary pods satisfy the same Kyverno policies as everything else.
A blue-green variant (full parallel stack + atomic traffic flip + manual
promotion) is included as a commented block at the bottom of the same file.
The same pattern applies to api-gateway -- copy the file, swap the name,
port (8080), and image (ghcr.io/netshop-lab/api-gateway).