No description
  • Kotlin 86%
  • TypeScript 10%
  • CSS 2.1%
  • Shell 0.8%
  • Dockerfile 0.8%
  • Other 0.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Adam Urban d421cc4818
All checks were successful
LinkLab CI / Run tests (push) Successful in 2m58s
LinkLab CI / Build JAR (push) Successful in 2m19s
LinkLab CI / Build and push Docker image (push) Successful in 27s
Merge pull request 'Week 8: Kubernetes blueprint with completed Week 7 authentication' (#4) from week8-kubernetes into main
Reviewed-on: #4
2026-10-27 14:10:19 +00:00
.forgejo/workflows Week 8 blueprint 2026-10-26 10:40:59 +00:00
docs Preserve earlier lab evidence and exclude private instructions 2026-10-27 14:06:32 +00:00
frontend Week 8 blueprint 2026-10-26 10:40:59 +00:00
gradle/wrapper Week 8 blueprint 2026-10-26 10:40:59 +00:00
jenkins Week 8 blueprint 2026-10-26 10:40:59 +00:00
k8s Week 8 blueprint 2026-10-26 10:40:59 +00:00
monitoring Week 8 blueprint 2026-10-26 10:40:59 +00:00
nginx Week 8 blueprint 2026-10-26 10:40:59 +00:00
scripts Week 8 blueprint 2026-10-26 10:40:59 +00:00
src Week 8 blueprint 2026-10-26 10:40:59 +00:00
.dockerignore Preserve earlier lab evidence and exclude private instructions 2026-10-27 14:06:32 +00:00
.gitignore Preserve earlier lab evidence and exclude private instructions 2026-10-27 14:06:32 +00:00
build.gradle.kts Week 8 blueprint 2026-10-26 10:40:59 +00:00
docker-compose.yml Week 8 blueprint 2026-10-26 10:40:59 +00:00
Dockerfile Week 8 blueprint 2026-10-26 10:40:59 +00:00
Dockerfile.ci Week 8 blueprint 2026-10-26 10:40:59 +00:00
gradlew Week 8 blueprint 2026-10-26 10:40:59 +00:00
gradlew.bat Week 8 blueprint 2026-10-26 10:40:59 +00:00
linklab.postman_collection.json Week 8 blueprint 2026-10-26 10:40:59 +00:00
README.md Preserve earlier lab evidence and exclude private instructions 2026-10-27 14:06:32 +00:00
RESULT.MD Preserve earlier lab evidence and exclude private instructions 2026-10-27 14:06:32 +00:00
settings.gradle.kts Week 8 blueprint 2026-10-26 10:40:59 +00:00

LinkLab — Week 8 Blueprint

CS4297 Applied System Design · Week 8 — Saying What You Want, Not How To Get It

Complete Week 7 solution included. Clone this and start here.

Previous lab evidence is kept in RESULT.MD, which contains the Week 6 results and the filled Week 5 worksheet. The instructions below are for Week 8.


What you are building this week

Part The problem it poses Time
A Nobody is going to restart it for you 0:00–0:30
B A process can be running and useless. Who decides it is healthy? 0:30–0:55
C You just ran four copies. What actually got four times better? 0:55–1:30
D So what would it take to replicate the part that matters? 1:30–2:00

Part D is written and discussed — no commands. The lab sheet is the authority on what happens when; this table is orientation only.


Prerequisites

0. Push this repo and let CI build your image — DO THIS BEFORE THE LAB

Part C verifies that JWT works across pods, so the image you deploy must contain the Week 7 auth code. The image your Week 5 pipeline pushed does not — JWT did not exist until Week 7. Deploy an image built from this repo.

A fresh clone has nothing new to push, so a plain git push does nothing and CI never runs. Give it a commit:

git commit --allow-empty -m "Week 8: trigger build"
git push origin main          # triggers .forgejo/workflows/ci.yml

Then Forgejo → your repo → Packages and confirm a package appears. The CI pushes cs4297.skynet.ie/<your-username>/<your-repo-name>:latest. This takes a few minutes. Do it at home, not at 09:05 on Tuesday.

1. Enable Kubernetes in Docker Desktop

Settings → Kubernetes → Enable Kubernetes → cluster type kubeadm (not kind) → Apply & Restart

On the kind cluster type, images are pulled through a local mirror that ignores your forgejo-registry secret entirely, and every pod sits in ImagePullBackOff.

Verify: kubectl get nodes — it should show exactly one node, docker-desktop, Ready. Three nodes means you are on kind.

Give Docker at least 8 GB. Settings → Resources → Memory. You are about to run MySQL, Redis, DynamoDB, ElasticSearch and Kafka and a Kubernetes control plane and two application pods at 768 MiB each. At the 2–4 GB default, pods are evicted and you will debug the wrong problem all afternoon.

2. Start Docker Compose databases

docker compose up -d mysql redis dynamodb-local elasticsearch kafka

3. Verify host.docker.internal works

kubectl run test --image=curlimages/curl --restart=Never -it --rm \
  -- curl -sf http://host.docker.internal:9200 2>/dev/null \
  && echo "ElasticSearch reachable" || echo "PROBLEM"

(Uses ElasticSearch, not MySQL — MySQL speaks a binary protocol on 3306, not HTTP, so curl against it always fails even when the connection is fine. ElasticSearch answers plain HTTP on 9200, so this actually tests what it claims to.)

If not reachable: Docker Desktop → Settings → Resources → Network → enable "host.docker.internal should resolve to the host" → restart.


Kubernetes manifests

k8s/
├── 00-namespace.yml          # linklab namespace
├── 01-secret.yml             # JWT secret, DB passwords
├── 02-configmap.yml          # non-sensitive config, host.docker.internal URLs
├── 03-deployment.yml         # 2 replicas, readiness + liveness probes
├── 04-service.yml            # ClusterIP service (use port-forward to access locally)
├── 05-hpa.yml                # not used in this year's lab (autoscaling is CS4135)
└── broken/
    └── deployment-broken.yml # not used this year — Part B now has you break
                              # the readiness probe in 03-deployment.yml yourself

Quick kubectl reference

# Apply a manifest
kubectl apply -f k8s/03-deployment.yml

# Watch pods
kubectl get pods -n linklab -w

# Describe a pod (events, env vars, probes)
kubectl describe pod <pod-name> -n linklab

# View logs
kubectl logs <pod-name> -n linklab
kubectl logs <pod-name> -n linklab --previous   # crashed container

# Execute a command inside a pod
kubectl exec -n linklab <pod-name> -- curl localhost:8080/actuator/health

# Port-forward to reach the service locally
kubectl port-forward svc/linklab -n linklab 8090:80

# Undo the last change to a deployment (Part B uses this)
kubectl rollout undo deployment/linklab -n linklab

# Scale manually
kubectl scale deployment linklab --replicas=3 -n linklab

# Clean up everything
kubectl delete namespace linklab

Finding your Forgejo image tag

The registry host is cs4297.skynet.ie — the same REGISTRY value in .forgejo/workflows/ci.yml. CI pushes ${REGISTRY}/${{ forgejo.repository }}, so the image name is your repository name, not a fixed product name:

cs4297.skynet.ie/<your-username>/<your-repo-name>:latest

To confirm yours: Forgejo → your repository → Packages tab. Copy the path shown there verbatim — do not type it from memory.

Put that exact path into the image: value in 03-deployment.yml. That is the only file that needs it.


Finding your JWT in the browser (alternative to C.1)

Only if you are also running the frontend (docker compose up -d frontend, then log in at http://localhost:3001) -- the lab itself does not start it. After logging in:

  1. Open DevTools (F12)
  2. Application tab → Local Storage → http://localhost:3001
  3. Click token → copy the value

Use it as: Authorization: Bearer <token>


Common issues

Pods stuck in Pending / ImagePullBackOff Check kubectl describe pod -n linklab and read the Events at the bottom. In order of likelihood:

  1. The image path is wrong. It must be cs4297.skynet.ie/<username>/<repo-name>:latest, copied from your Forgejo Packages tab. A wrong host or a wrong repo name both look like this.
  2. CI never ran, so nothing was pushed. Check your repo's Actions tab.
  3. The pull secret is wrong. Most often the token has no package permission: it still logs in fine, but every pull gets 401. Use the Week 5 token with write:packages, then delete and recreate forgejo-registry with --namespace=linklab.
  4. Insufficient memory: Docker Desktop → Settings → Resources (see above).

host.docker.internal not resolving from pods Enable in Docker Desktop → Settings → Resources → Network. Restart Docker Desktop after changing this setting.

CrashLoopBackOff on correct deployment (not Part B) Two causes, and they look identical:

  • A database is unreachable. kubectl logs <pod> -n linklab names it. Verify with docker compose ps.
  • The app was still starting when the liveness probe killed it. Spring Boot booting against five datastores on a busy laptop can take over two minutes. kubectl logs <pod> --previous showing a normal, incomplete startup with no error is the signature. Raise livenessProbe.initialDelaySeconds.

A pod shows Running but never 1/1 READY The container is up but the readiness probe keeps failing, so the Service sends it nothing. kubectl describe pod -n linklab and read the Events — kubectl logs will look perfectly normal, which is Part B's whole point.


Week-by-week progression

Week Content Blueprint
2 REST API + MySQL Stubs for LINK-001
3 Docker · Redis · NGINX · Grafana Complete Week 2
4 SQL vs NoSQL · DynamoDB Complete Week 3
5 CI/CD · ElasticSearch Complete Week 4
6 Kafka async messaging Complete Week 5
7 JWT + OAuth2 + RBAC Complete Week 6
8 Kubernetes ← you are here
9 — Complete Week 8
10 Capstone —