Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 

README.md

NetShop GitOps (Argo CD app-of-apps)

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 registry ghcr.io/netshop-lab, and every object carries app.kubernetes.io/part-of: netshop. CRD-backed kinds (argoproj.io/...) are validated with kubeconform -ignore-missing-schemas.


The app-of-apps pattern

                     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 -- the AppProject named netshop. 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 references project: netshop.
  • root-app.yaml -- the root Application ("app-of-apps"). Its source is the gitops/applications/ directory, which contains nothing but more Application manifests. 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 $values pattern.
    • addon-security-policies.yaml -- applies the security/policies + security/signing manifests 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 of values.yaml.
  • rollouts/web-rollout.yaml -- an optional Argo Rollouts example doing a metric-gated canary of the web edge service (with a commented blue-green alternative).

Why app-of-apps?

  1. One bootstrap command brings up the entire platform.
  2. Children are declarative + version-controlled -- adding a component is a single new file in applications/, reviewed and merged like any code.
  3. Ordering via sync-waves -- the policy engine and policies land before the first NetShop pod is admitted (kyverno wave -1, security-policies wave 1, workloads wave 2).

The three GitOps pillars (set on every Application)

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.


Bootstrap

# 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 Rollouts

Pick one cloud target. netshop-gke and netshop-digitalocean both deploy into the same netshop namespace on the in-cluster API server, so enable exactly one per Argo CD instance (delete the other file, or point its destination.server at a different registered cluster). The chart's global.imageRegistry is overridden back to ghcr.io/netshop-lab here; in a real pipeline the CI "image bump" commit pins an immutable digest so the deployed artifact is exactly the one cosign signed.


Validation

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.


Alternative: Flux CD

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.


Progressive delivery (Argo Rollouts)

rollouts/web-rollout.yaml replaces the web Deployment with a Rollout that:

  • shifts traffic 10% -> 40% -> 70% -> 100% with pauses between steps;
  • runs an AnalysisTemplate that 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).