Module 1: Why Kubernetes And The Continuity Challenge

8. Project: Andes Cargo's cluster, ready

Description

This project adds no new concept — it reuses, in a single verification pass, everything you built in lessons 4, 5, and 7: the installed tools, the running andes-cargo-cluster cluster, and the andes-cargo-status-api image available inside it. The difference from the previous lessons is discipline: instead of verifying each piece separately, as you built it, this project audits it fully, end to end, with an explicit checklist. By the end, you have exactly what Module 2 needs to start: a real cluster, with the correct image already inside it, ready to run a real Pod.

Connection to the module

Each lesson in this module built one piece: the market criteria (lesson 2), the continuity challenge resolved (lesson 3), the tools installed (lesson 4), the cluster created (lesson 5), its architecture understood (lesson 6), and the image loaded (lesson 7). This project is the first time you see all three executable pieces confirmed in a single pass, right before Module 2 takes this same cluster and deploys the first real workload onto it.


The final checklist: three verifications, one complete lab

Verification 1 — The tools, at the correct version

kind version
kubectl version --client

What to expect (v0.32.0 is the minimum version this guide verified — yours may be the same or newer; go1.26.3 darwin/arm64 is your specific platform, variable; v1.36.1 is the kubectl version matching the Kubernetes version kind v0.32.0 runs by default):

kind v0.32.0 go1.26.3 darwin/arm64
Client Version: v1.36.1
Kustomize Version: v5.8.1

If either version comes out far below expected, go back to lesson 4 before continuing — the rest of this checklist assumes both tools are up to date.


Verification 2 — The cluster exists, and its three nodes are healthy

kind get clusters

What to expect:

andes-cargo-cluster
kubectl get nodes

What to expect (AGE is your variable value — it depends on how long your cluster has been running; the rest is literal for this three-node architecture):

NAME                                STATUS   ROLES           AGE     VERSION
andes-cargo-cluster-control-plane   Ready    control-plane   5m41s   v1.36.1
andes-cargo-cluster-worker          Ready    <none>          5m26s   v1.36.1
andes-cargo-cluster-worker2         Ready    <none>          5m26s   v1.36.1

Three nodes, all three Ready. If any shows NotReady and more than a minute has already passed since you created the cluster, check docker ps to confirm the node's three containers are still running before continuing.


Verification 3 — The image is available, on all three nodes

for node in andes-cargo-cluster-control-plane andes-cargo-cluster-worker andes-cargo-cluster-worker2; do
  echo "--- $node ---"
  docker exec "$node" crictl images | grep andes-cargo-status-api
done

What to expect (the truncated IMAGE ID is your variable value, generated from your build's exact content; identical on all three nodes because it's the same image loaded three times):

--- andes-cargo-cluster-control-plane ---
docker.io/library/andes-cargo-status-api        latest               d07c069076658       186MB
--- andes-cargo-cluster-worker ---
docker.io/library/andes-cargo-status-api        latest               d07c069076658       186MB
--- andes-cargo-cluster-worker2 ---
docker.io/library/andes-cargo-status-api        latest               d07c069076658       186MB

If any node doesn't show the image, go back to lesson 7 and repeat kind load docker-image andes-cargo-status-api:latest --name andes-cargo-cluster — it costs nothing to run it again, and kind simply confirms the image is already present on any node that already has it.


The closing diagram: what exists today, and what's missing

              ANDES CARGO — THE COMPLETE LAB, READY FOR MODULE 2

  ┌──────────────────────────────────────────────────────────────────────┐
  │  andes-cargo-cluster (kind v0.32.0, Kubernetes v1.36.1)                   │
  │                                                                          │
  │  ┌────────────────────────────────────────────────────────────────┐ │
  │  │  NODE: andes-cargo-cluster-control-plane                            │ │
  │  │    kube-apiserver · etcd · kube-scheduler · kube-controller-manager │ │
  │  │    kubelet · kube-proxy                                             │ │
  │  │    andes-cargo-status-api:latest image ── LOADED (lesson 7)           │ │
  │  └────────────────────────────────────────────────────────────────┘ │
  │                                                                          │
  │  ┌──────────────────────────┐  ┌──────────────────────────┐        │
  │  │  NODE: ...-worker            │  │  NODE: ...-worker2          │        │
  │  │    kubelet · kube-proxy        │  │    kubelet · kube-proxy        │        │
  │  │    image LOADED                  │  │    image LOADED                  │        │
  │  └──────────────────────────┘  └──────────────────────────┘        │
  └──────────────────────────────────────────────────────────────────────┘

  What exists today: a real, healthy Kubernetes cluster, with the image
  inherited from aws-serverless-and-containers-guide available on all
  three nodes — no AWS account, no paid-plan limit whatsoever.

  What's missing: no Pod is running yet. The image is available, but
  nobody has asked the cluster to run it.

The rest of the guide's map, on this same diagram

  M2  Pods, Deployments, Services       ── the first real Pod, with replicas
                    │                       and a Service that exposes it
                    ▼                       inside the cluster
  M3  Configuration, health, autoscaling ── externalized ConfigMap/Secret,
                    │                       probes, HorizontalPodAutoscaler
                    ▼
  M4  Networking, Ingress, NetworkPolicy ── the service, exposed over real
                    │                       HTTP, and protected with deny-by-default
                    ▼
  M5  GitOps with ArgoCD                 ── a change in Git, reflected on
                    │                       its own, no manual kubectl apply
                    ▼
  M6  Admission control, scanning        ── Gatekeeper + Kyverno + trivy image,
                    │                       the gatekeeper before etcd
                    ▼
  M7  What's specific to EKS             ── what changes with a real managed
                    │                       control plane (representative)
                    ▼
  M8  Capstone                          ── the complete system, one change
                                            that passes and one the gate stops

Every module in this list builds directly on the cluster you just confirmed healthy in this project — none starts from scratch, none requires you to come back to this module unless something breaks.


What to carry into Module 2

Three concrete pieces, none hypothetical, all confirmed by this project:

  • The andes-cargo-cluster cluster, with one control plane and two worker nodes, really running Kubernetes v1.36.1 — not a reduced version, not a simulator.
  • The andes-cargo-status-api:latest image, available in the internal containerd of all three nodes, ready for any manifest to reference without depending on any external registry.
  • kubectl's active context (kind-andes-cargo-cluster), already configured, needing no additional flag to talk to this cluster from Module 2 onward.

Common mistakes

Considering the module closed without running the three verifications, because "I already saw each piece work in its lesson" (workflow, the easiest one to skip). What happens: someone mentally confirms each lesson went well at the time, and assumes the project is just a summary, without running this checklist's real commands. Why it happens: each individual verification already felt "done" when it first ran, in its own lesson. How to spot it: if you can't paste, right now, the real output of this project's three verifications. How to fix it: this checklist's purpose isn't to repeat the obvious — it's to confirm your lab's current state (not the state from hours ago, when you might have closed the terminal or restarted your machine) is still the expected one. It's the same discipline you already saw in aws-serverless-and-containers-guide's Module 6 project.

Shutting down Docker Desktop (or stopping the docker service) between this module and the next, without realizing that also shuts down the cluster (operational, with real consequences when you pick the guide back up). What happens: someone closes their machine or Docker Desktop at the end of this module, and when picking the guide back up for Module 2, kubectl get nodes fails with a connection error, as if the cluster had disappeared. Why it happens: each node in andes-cargo-cluster is a Docker container (lesson 6) — if Docker stops, all three containers stop with it. How to spot it: docker ps -a --filter "name=andes-cargo-cluster" shows all three containers in Exited state, not Up. How to fix it: unlike LocalStack in previous guides (where losing the container meant losing the entire state), kind's containers survive a Docker restart if they were never explicitly deleted — starting Docker Desktop back up is enough, and all three containers (and the whole cluster) become available again, with no need to run kind create cluster again. Confirm it with kind get clusters.

Assuming "the cluster is ready" means "Andes Cargo's service is already running" (conceptual, the mistake this lesson's diagram corrects explicitly). What happens: someone, after completing this project with all three verifications green, expects to be able to curl andes-cargo-status-api somehow, and is surprised there's no port or address to connect to yet. Why it happens: "the image is loaded" sounds, by the name, like "the service is running." How to spot it: if you try running kubectl get pods expecting to see something related to andes-cargo-status-api, and the result comes back empty (outside the kube-system namespace). How to fix it: review this lesson's closing diagram — "loaded" means the image is available for a Pod to use, not that any Pod is already using it. Nobody has yet asked the cluster to run anything with that image; that's exactly what Module 2 starts.


Exercises

Exercise 1 — Reconstruct the checklist from memory. Without going back to the "The final checklist" section, write the three commands (or command groups) that would verify, against any lab with these same characteristics, each of the three pieces this project confirmed.

See solution
  1. kind version and kubectl version --client — confirm the tools are installed, at the expected version.
  2. kind get clusters and kubectl get nodes — confirm the cluster exists and its nodes are in Ready state.
  3. docker exec <node-name> crictl images | grep <image-name>, repeated on each node — confirms the image is available inside each one's internal containerd, not only in the host's Docker.

If you reconstructed all three without looking, you've internalized the complete audit pattern for a Kubernetes lab ready for workload production.

Exercise 2 — Explain why the cluster survives a Docker restart, but LocalStack (previous guides) doesn't. With what you know from this lesson and previous ecosystem guides, explain in two or three sentences the underlying difference.

See solution

The difference isn't in Docker — the same container engine in both cases — but in which container stops and what it held. A stopped Docker container (docker stop, or Docker Desktop itself closing) keeps its internal filesystem until someone explicitly deletes it (docker rm); starting it back up, everything that was inside is still there, including the cluster's etcd state. LocalStack, in previous guides, lost its state with a simple restart because, without the PERSISTENCE=1 flag (an explicit setting those guides never enabled by default), the container kept everything in memory, not on disk — a container restart, not even a full Docker restart, was enough to lose everything. A kind cluster, by contrast, keeps its state in the container's own internal filesystem as long as the container is never deleted.

Exercise 3 — Design your own "cluster ready" verification for a new case. Imagine that, instead of a single image, you needed to verify that two different images are available on a four-node kind cluster. Describe, without running it, how you'd adapt this project's Verification 3 pattern to that case.

See solution

The pattern extends directly: instead of a single grep per node, you'd run the same for loop over the four node names, and inside each iteration, run crictl images once and use grep -E "image-one|image-two" (or two separate grep lines) to confirm both images in the same pass. The underlying structure doesn't change: for each node, you explicitly confirm which images its internal containerd has — never assuming that because one image is on a node, the others are too, or that because it's on one node, it's on all of them.


Summary and next step

In this project you verified, with three audit commands, that this module's complete lab is healthy: kind v0.32.0 and kubectl v1.36.1 installed and confirmed; the andes-cargo-cluster cluster running, with its three nodes at Ready; and the andes-cargo-status-api:latest image available in the internal containerd of all three nodes. The closing diagram left you with today's exact state — a healthy cluster, with no Pod running yet — and the complete map of the seven remaining modules, each one building directly on this same cluster.

Before moving on you should be able to: run the complete checklist from memory; explain why a kind cluster survives a Docker restart; and describe, using this lesson's diagram, the exact difference between "the image is loaded" and "the service is running."

Next module: Pods, Deployments, and Services. Module 2 takes exactly this cluster — healthy, with the image already available — and creates this guide's first real Pod: andes-cargo-status-api, really running inside Kubernetes, with replicas managed by a Deployment and exposed by a Service under the same name, status-api-service, that the previous guide left documented and never ran. With the same depth the cluster's architecture got in this module.

Resources

  1. aws-serverless-and-containers-guide (NIEVA), Module 7, lessons 4 and 8 — the documented-never-executed ECS cluster and status-api-service service this module starts replacing with a real orchestrator.
  2. Kubernetes — Workloads — the official gateway to Module 2: Pods, Deployments, and the rest of the workload primitives.
  3. kind — User Guide — complete kind reference, useful as a general lookup for the rest of this guide.
  4. Kubernetes — kubectl Reference Docs — complete official command reference, the tool you're going to use in every "hands-on" lesson for the rest of the guide.