Module 8: Capstone Andes Cargo On Kubernetes
2. Architecture review: the full cluster
Description
This lesson applies no new manifest — it gathers, in a single diagram, the eight namespaces that coexist today inside andes-cargo-cluster, confirmed against the real cluster before writing this lesson, and the complete flow connecting three of them: Gitea, ArgoCD, and the cluster itself. It's the map lessons 3 and 4 — the change that passes and the change the gate stops — take as already known.
Connection to the module
Lesson 1 listed, in a per-module table, what each one left running. This lesson reorganizes that same information by namespace, not by module — the view that actually matters to whoever operates the cluster day to day, because a namespace groups objects that get administered together, regardless of which lesson of this guide built them.
The eight namespaces, confirmed against the real cluster
kubectl get namespaces
What to expect (literal, executed — AGE is your variable value; the rest, including the namespace count, is literal at this point in the guide):
NAME STATUS AGE
andes-cargo Active 2h58m
argocd Active 85m
default Active 2h58m
gatekeeper-system Active 48m
gitea Active 88m
ingress-nginx Active 2h57m
kube-node-lease Active 2h58m
kube-public Active 2h58m
kube-system Active 2h58m
kyverno Active 47m
local-path-storage Active 2h58m
Four of these eleven namespaces are kind's own infrastructure (kube-system, kube-node-lease, kube-public, local-path-storage) — they existed since Module 1, lesson 5, before Andes Cargo had a single object of its own, and they don't change between modules. default also has no Andes Cargo resource — it's the namespace Kubernetes uses when no one specifies one, and this guide never used it on purpose since Module 2. The six namespaces that do matter for this capstone are: andes-cargo (the only one with business resources), and five of lab infrastructure (gitea, argocd, gatekeeper-system, kyverno, ingress-nginx) — each built in a different module, none ever considered "Andes Cargo's" in itself.
The complete diagram: six namespaces, one cluster
andes-cargo-cluster (kind v0.32.0, Kubernetes v1.36.1)
┌──────────────────────────────────────────────────────────────────────────────────┐
│ │
│ namespace: gitea (M5) namespace: argocd (M5) │
│ ┌─────────────────────┐ ┌───────────────────────────────────┐ │
│ │ gitea (Pod) │◄────────│ argocd-server │ │
│ │ repo: │ watch │ argocd-repo-server │ │
│ │ andes-cargo/ │ every │ argocd-application-controller │ │
│ │ andes-cargo-k8s │ ~3 min │ (compares Git vs. cluster, applies │ │
│ │ (10 manifests) │ │ the difference, no kubectl apply) │ │
│ └─────────────────────┘ └───────────────┬───────────────────┘ │
│ │ applies the desired state │
│ ▼ │
│ namespace: gatekeeper-system (M6) namespace: kyverno (M6) │
│ ┌──────────────────────┐ ┌──────────────────────────┐ │
│ │ gatekeeper- │ │ kyverno-admission- │ │
│ │ controller-manager │ │ controller │ │
│ │ (Constraint: │ │ (ClusterPolicy: │ │
│ │ require-resource-limits) │ │ require-resource-limits) │ │
│ └───────────┬──────────┘ └───────────┬──────────────┘ │
│ │ admission (both, in parallel) │ │
│ └────────────────┬───────────────┘ │
│ ▼ │
│ namespace: andes-cargo (M1-M4) │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Deployment andes-cargo-status-api (2-6 replicas, HPA) │ │
│ │ Service status-api-service (ClusterIP) │ │
│ │ ConfigMap + Secret (LocalStack endpoint, dummy credentials) │ │
│ │ Ingress status-api-ingress (host andes-cargo.local) │ │
│ │ NetworkPolicy × 2 (default-deny-ingress, allow-from-ingress-nginx) │ │
│ └───────────────────────────────┬────────────────────────────────┘ │
│ │ exposed by │
│ ▼ │
│ namespace: ingress-nginx (M4) │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ ingress-nginx-controller — the cluster's only HTTP door │
│ │ (http://andes-cargo.local, via kind-config.yaml's extraPortMappings) │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────────┘
▲
│ kubectl (kind-andes-cargo-cluster context) — observation
│ and initial-install tool; NEVER the path an
│ andes-cargo-status-api change reaches the cluster by (M5-M8)
your terminal
Three of this diagram's arrows deserve a slow read, because they're this entire guide's complete thesis drawn as a picture:
- Gitea → ArgoCD, labeled "watch every ~3 min" — this isn't a one-time arrow. ArgoCD compares the repository against the real cluster continuously, in a loop that never stops (Module 5, lesson 2). That arrow is why lessons 3 and 4 of this module never need a
kubectl apply. - ArgoCD → Gatekeeper/Kyverno, labeled "applies the desired state" — when ArgoCD decides the cluster needs to change, it holds no special privilege in front of the two policy engines. Every object ArgoCD tries to create or modify goes through exactly the same admission phase it would go through if you ran
kubectl applyby hand yourself — Module 6's gatekeeper doesn't distinguish who's knocking at the door. - kubectl, with the dotted arrow pointing up, marked "NEVER the path" — this is the line that sets this capstone apart from Modules 1-4: there,
kubectl applywas the normal mechanism for changing the cluster. From Module 5 onward, and specifically in this module's lessons 3-4,kubectlgets used exclusively to observe (get,describe,logs) — never to apply anandes-cargo-status-apichange.
The GitOps flow, as a sequence
sequenceDiagram
participant You as You (git push)
participant Gitea as Gitea (andes-cargo-k8s)
participant ArgoCD as ArgoCD (application-controller)
participant Admission as Gatekeeper + Kyverno
participant Etcd as etcd (andes-cargo-cluster)
You->>Gitea: git push origin main
Note over Gitea: the commit already exists in the repository
loop every ~3 minutes (or on webhook detection)
ArgoCD->>Gitea: compares main's HEAD vs. cluster state
end
ArgoCD->>Admission: tries to apply the object that changed
alt the object satisfies the policies
Admission->>Etcd: allowed: true — the object gets saved
Etcd-->>ArgoCD: Sync Status: Synced
else the object violates a policy
Admission-->>ArgoCD: allowed: false — admission webhook denied
Note over ArgoCD: the previous object keeps running, with no change
end
This diagram is literally lessons 3 and 4's script: the alt branch above ("satisfies the policies") is lesson 3; the else branch ("violates a policy") is lesson 4. The difference between the two isn't in any step before Admission — Git receives the commit the same in both cases, ArgoCD detects it the same in both cases. The difference is, exclusively, in the decision Gatekeeper and Kyverno make at the exact moment ArgoCD tries to apply the object.
Analogy: the factory's complete floor plan
If lesson 1 presented the automated factory as a single image, this lesson is its floor plan. Each namespace is a different area of the factory, with its own team and its own function, none redundant: the order-receiving area (gitea), the line-control area (argocd), the two quality-check stations that review every piece in parallel before it enters the assembly line (gatekeeper-system and kyverno), the assembly line itself where the finished product lives (andes-cargo), and the front counter for public dispatch (ingress-nginx). No one in this factory reports directly to a human operator standing at the door with a wrench (a manual kubectl apply) — every area reports to the blueprint Git declares, and the blueprint only changes when someone edits it and pushes it, never when someone walks into the factory and moves something by hand.
Common mistakes
Confusing "lab infrastructure namespace" with "Andes Cargo namespace" (scope). What happens: someone, seeing gitea/argocd/gatekeeper-system/kyverno in the namespace list, assumes they're Andes Cargo business resources, just like andes-cargo. How to spot it: if your answer to "what does Andes Cargo do in the gitea namespace?" is anything other than "nothing — Gitea is the tool that serves the repository, not a business-case resource." How to fix it: review this lesson's diagram — only andes-cargo contains objects that represent the business (Deployment, Service, ConfigMap, etc.); the other five relevant namespaces are, together, "the factory," never "the product."
Assuming kubectl apply is still the normal change mechanism, because it was in Modules 1-4 (early-acquired habit). What happens: someone, arriving at this module's lessons 3-4, out of habit opens a terminal and prepares a kubectl apply -f deployment.yaml before touching Git. How to spot it: if your first instinct in front of "I want to change something about andes-cargo-status-api" is to write a kubectl command that doesn't start with get, describe, or logs. How to fix it: since Module 5, the only legitimate change path for any resource managed by ArgoCD's Application is a commit in andes-cargo-k8s, followed by git push — this lesson's diagram's dotted arrow exists exactly to mark that distinction clearly.
Reading the mermaid diagram as if Admission were a single component (oversimplification). What happens: someone reads the sequence's Gatekeeper + Kyverno box and assumes the two engines act as a single unit, with one joint decision. How to spot it: if you expect a single rejection message when an object violates the policy, without considering that the two engines evaluate completely independently. How to fix it: this module's lesson 4 revisits this distinction with real evidence — the two engines aren't a single box, they evaluate separately, and (new compared to Module 6) they don't always cover exactly the same object scope.
Exercises
Exercise 1 — Classify the eleven namespaces without looking at the lesson. Without going back to the "The eight namespaces" section, classify from memory each of the cluster's eleven namespaces into one of three categories: "kind infrastructure" (existed since Module 1), "lab infrastructure" (Gitea/ArgoCD/Gatekeeper/Kyverno/ingress-nginx), or "Andes Cargo resources."
See solution
kind infrastructure: kube-system, kube-node-lease, kube-public, local-path-storage, default (the latter isn't infrastructure per se, but it also contains no resource from this guide). Lab infrastructure: gitea, argocd, gatekeeper-system, kyverno, ingress-nginx. Andes Cargo resources: only andes-cargo. Nine of the cluster's eleven namespaces never contain an object representing the business — only one does.
Exercise 2 — Trace a git push's complete path, without looking at the mermaid diagram. List, in order, each component a change to deployment.yaml crosses from the moment you run git push until the change ends up running (or rejected) in the cluster.
See solution
(1) The commit arrives at the repository in the gitea Pod, gitea namespace. (2) argocd-application-controller, argocd namespace, detects the difference on its next comparison cycle (up to a few minutes later). (3) ArgoCD tries to apply the object against kube-apiserver. (4) kube-apiserver invokes, in parallel, Gatekeeper's ValidatingWebhookConfiguration (gatekeeper-system namespace) and Kyverno's (kyverno namespace). (5) If both allow the object, it gets saved in etcd and the cluster converges; if either denies it, the object never gets saved, and the previous state keeps running in andes-cargo.
Exercise 3 — Explain why the diagram's kubectl arrow is dotted, not solid. In one sentence, explain the visual difference between the diagram's solid arrows (Gitea→ArgoCD, ArgoCD→Admission, etc.) and kubectl's dotted arrow.
See solution
A reasonable explanation: "The solid arrows represent the real flow by which an andes-cargo-status-api change reaches the cluster from Module 5 onward — none of them depend on a human running a command at the moment of the change. kubectl's dotted arrow represents a different relationship: the tool you use to observe the result (get, describe, logs), not to produce it — it's dotted, and marked 'never the path,' precisely so it doesn't get confused with the real change flow's arrows."
Summary and next step
This lesson confirmed, against the real cluster, the eleven namespaces that exist today in andes-cargo-cluster — four of kind infrastructure, five of lab infrastructure, and only one with business resources (andes-cargo) — and presented two diagrams of the same system from different angles: the spatial map (what lives in each namespace) and the temporal sequence (what happens, step by step, from a git push until the cluster converges or rejects the change). kubectl's dotted arrow, marked "never the path," is the detail the next two lessons put to the test with literal evidence.
Before moving on you should be able to: classify any of the eleven namespaces into its correct category; trace a git push's complete path to the cluster from memory; and explain why kubectl apply stopped being the normal change mechanism since Module 5.
Next lesson: a change that crosses the whole gate. There you're going to push a real change to Git, and you're going to confirm, with literal evidence, every arrow of this diagram working in sequence — with not a single kubectl apply involved.
Resources
- Kubernetes — Namespaces — official reference for the logical isolation mechanism that organizes this diagram's eleven namespaces.
- Argo CD — How it Works — the official description of the continuous comparison loop this lesson's mermaid sequence represents.
- Kubernetes — Dynamic Admission Control — the exact point in the lifecycle where
Gatekeeper + Kyvernointercept every object in the sequence diagram. kubernetes-and-eks-in-production-guide(NIEVA), Modules 1-6 — the source for every namespace and every component of this diagram, built one by one.