This repo contains hands-on exercises for the Kubernetes workshop — the sequel to the Containerisation workshop. Workshop #1 was about building and running one container. This one is about deploying, scaling, exposing, and continuously running containers on a cluster.
- Kubernetes_workshop.pptx — theory parts presented during the workshop.
- setup/ — do this first. Setup a local cluster and a namespace for this workshop.
- kubectl-demo/ — live-demo showing the power of Kubernetes
via
kubectl. <num>-<name>/— exercise modules for you to work through, in order. Each has aREADME.mdwithTASKblocks and starter manifests withTODOs to fill in.edu-<name>/— finished examples to read and run, not exercises.- helm-teaser/ — demo teasing the Helm workshop.
- live-demo/ — facilitator cheat-sheet for demo on a real cluster.
- deck-notes/ — supporting notes for the presentation.
🤓 Skip these if you already have a working local Kubernetes! (with host ingress and a working storage-class). Just make sure to check the setup guide section 4.
- Install Rancher Desktop (Recommended!)
- Enable Kubernetes under Preferences.
- Make sure
~/.rd/binis on yourPATHsokubectlresolves to the Rancher provided one. - It ships a container runtime,
kubectl, a Traefik ingress controller, and alocal-pathstorage class — everything these exercises need. - If you prefer kind or k3d, that works too; see notes in setup/ and section 4.
- Confirm
kubectl get nodesshows aReadynode. - Ensure below CLI tools are available on your shell/terminal (can skip optional).
- Then follow setup/ to create and select your namespace.
- kubectl (included with Rancher Desktop)
- helm (included with Rancher Desktop)
- Optional: k9s (NOT included, but priceless tool. Install guide).
- Optional: Use my aliases and functions, for shorter less verbose kubectl commands 🗣️
- Kubernetes by JetBrains link
Keep these open while you work:
- https://kubernetes.io/docs
- Pods & Deployments: https://kubernetes.io/docs/concepts/workloads/
- Services & Ingress: https://kubernetes.io/docs/concepts/services-networking/
kubectlreference: https://kubernetes.io/docs/reference/kubectl/- An LLM (ChatGPT/Claude) knows a lot about Kubernetes — use it as a tutor.
Work through them in order — each builds on the last.
Create an nginx Deployment, scale it, exec in, kill a Pod and watch it heal, port-forward, roll out a change. Then write it as a YAML manifest.
Move content and config out of the image: ConfigMaps (incl. subPath), Secrets, and
environment variables — set directly and imported from a ConfigMap.
emptyDir shared inside a Pod, then a PersistentVolumeClaim that survives Pod
deletion. StorageClass → PVC → PV explained.
Put a Service in front of your Pods, then an Ingress so it's reachable from your
browser at *.localhost.
Wire the Spring Boot + Postgres app from Workshop #1 together yourself: two cooperating Deployments, a Service for discovery, a ConfigMap + Secret, a PVC, and an Ingress — everything from modules 1–4 in one real system.
A finished two-tier app (nginx → http-echo) wired by Services and an Ingress —
service discovery in action. Plus short notes on StatefulSets, DaemonSets, and
kubectl debug.
Each module teaches the same idea twice, mirroring Workshop #1's "single-stage then multi-stage" progression:
- Imperative first —
kubectl create/scale/expose/...for instant feedback. - Declarative second — write the same object as a YAML manifest and
kubectl apply -fit. This is how real systems are managed: versioned, reviewable, idempotent. Fill in theTODOs in each module's starter manifests.
If you're stuck or want to compare, completed manifests will be on the
solutions branch:
git checkout -t origin/solutions# What's in my namespace?
kubectl get all
kubectl get pods -o wide
kubectl get <type>/<name> -o yaml # full object; -o wide for a table
# Inspect & troubleshoot
kubectl describe <type>/<name> # events + current state
kubectl logs <pod> -f # stream stdout/stderr (-p for previous crash)
kubectl exec -it deployment/<name> -- sh # shell into a running Pod
# CRUD
kubectl create deployment <name> --image=<img>
kubectl edit <type>/<name>
kubectl delete <type>/<name>
kubectl apply -f <file-or-url>.yaml # declarative; idempotent
# Namespaces
kubectl get pods -A # across ALL namespaces
kubectl config set-context --current --namespace=<ns> # set your default
# Tip: shell completion
source <(kubectl completion bash) # or zshBefore reaching for the solutions branch, these five tools resolve most problems:
kubectl explain <type>.<field>— built-in schema docs, works offline. Drill in:kubectl explain deployment.spec.template.spec.containers. Every field is documented.kubectl create <thing> ... --dry-run=client -o yaml— scaffold a valid manifest to start from, then edit it. e.g.kubectl create deployment x --image=nginx --dry-run=client -o yaml.kubectl describe <type>/<name>— the Events at the bottom usually say exactly what's wrong (bad image, unbound PVC, failed mount, no endpoints).kubectl logs <pod>(add--previousfor a crashed container) — the app's own errors.kubectl get <type>/<name> -o yaml— the full object including its livestatus.
Plus the docs, and an LLM makes a strong Kubernetes tutor.
- Declare desired state; let Kubernetes reconcile it. Don't script how — describe what you want and apply it.
- Keep config and content out of images — use ConfigMaps, Secrets, and env vars so changes don't need a rebuild and survive rollouts.
- Pods are disposable. Never store anything you care about in a Pod's filesystem; use a PVC for data that must persist.
- Reach workloads by Service name, not Pod IP. Pod IPs churn; Service DNS is stable.
- Pin image tags (e.g.
nginx:1.27), don't use:latest— same rule as Workshop #1. - Namespace your work so you can clean up in one command and avoid collisions.
- k9s — terminal dashboard for navigating cluster objects fast.
- kubectl krew — plugin manager for
kubectl. - Rancher (web + RBAC), Headlamp (desktop), Kubernetes Dashboard (web) — UIs.
- Deploy your own image from Workshop #1. Once the Workshop #1 images are published to a registry, deploy one to your cluster, expose it with a Service, and route to it with an Ingress — the same artifact you built, now running on Kubernetes. (See deck-notes/reuse-workshop1-images.md.)
- Bring the whole
edu-multi-componentsystem up and add a second backend behind the same Ingress on a different host. - Explore the cluster with k9s instead of raw
kubectlfor a module. - Tear everything down with a single
kubectl delete namespace workshopand confirm it's all gone withkubectl get all -A | grep workshop.