42 Kubernetes Deployment and Runtime Validation

Deploy your application to Kubernetes and validate runtime behavior, resilience, and observability.

RES509 — Day 1 (session 4 overall). In this lab, you will deploy your containerized application to Kubernetes and validate that it behaves correctly under real orchestration conditions.

You will move from single-host Docker Compose to cluster-managed execution. PostgreSQL is optional today and required through a guided setup in Lab 52. Lab 42 is assessed in RES509; outstanding Lab 30 work remains part of RES507.

Context and goals

So far, you have:

  • Built a Node.js application
  • Containerized it with Docker
  • Orchestrated services with Docker Compose
  • Automated validation using CI

In this lab, you will:

  • Deploy your application to Kubernetes
  • Expose it via a Service
  • Validate runtime behavior
  • Simulate failures
  • Explore scaling and resilience

For this four-hour session, prioritize the core work: deploy the app, access /health, scale to three replicas, observe Pod replacement, and deliberately break and recover readiness.

Start an extension when the core work is verified. You do not need to finish the extension during this session. The final assessment takes place later; its timing will be announced in class. One verified extension is needed for full marks under the rubric below.

Check your progress

Use these checkpoints as you work. If an expected result is missing, inspect status, events, and logs before continuing. You do not need to write an answer for every step.

MilestoneExpected result
EnvironmentYour node is Ready; ordinary kubectl and Docker work inside the VM.
ImageThe k3s image list contains docker.io/library/quote-app:local.
First deploymentThe rollout completes; one application Pod is 1/1 Ready.
Service and accessThe Service maps port 80 to 3000; the forwarded /health request returns JSON containing "ok": true.
ScalingThe saved manifest requests three replicas; three application Pods are Ready.
ReconciliationAfter deletion, a replacement Pod has a new name and becomes Ready.
Deliberate failureA new Pod is 0/1 Ready; its events report a readiness probe HTTP 404.
RecoveryThe restored rollout completes and three application Pods are Ready.

Save the failure output before fixing it; the failed Pod and its events may disappear after recovery. Keep only the short evidence listed near the end of this lab.


Restart checklist

Returning for RS509 session 2? Reopen your existing VM, fork, and manifests. Start at the first checkpoint you cannot demonstrate. Keep working files and the existing cluster; you do not need to reinstall k3s or start the lab again.

Before class, have your VM and repository accessible and identify your last successful checkpoint. Bring the exact error if blocked; you are not expected to solve every blocker before class.

Run these checks inside your Ubuntu VM, from your own course repository:

Terminal window
git status
git branch --show-current
kubectl get nodes -o wide
kubectl -n quote-lab get deployments,pods,services

Expect your existing work branch, a Ready node, and the resources you previously created. NotFound for quote-lab means you have not reached the namespace checkpoint yet. If the API is unreachable, use the troubleshooting sheet below before running more Kubernetes commands. Preserve uncommitted work before switching branches.

  • Locate your completed application in labs/20-containerized-node-app and any manifests in labs/42-kubernetes.
  • Identify your first missing result in the progress table above.
  • Show your instructor that checkpoint, its expected result, and the actual symptom.
  • Resume from the corresponding lab section; save failure events before fixing the problem.

Ready to move forward?

Ask for a short instructor check-in when you can show:

  • Your Deployment and Service, plus successful /health requests through port-forwarding and the in-cluster Service.
  • Three Ready app replicas matching the saved manifest, and evidence of a deleted Pod being replaced.
  • The deliberate readiness failure, its HTTP 404 event, and a recovered rollout with /health restored.
  • Your working manifests and concise evidence saved in labs/42-kubernetes.

Explain one observation in your own words. These checks establish the core handoff to Lab 52. An extension is not required to move forward; one verified extension remains part of the later Lab 42 assessment. Once the instructor releases Lab 52, begin its guided PostgreSQL section. If you already verified the database extension, reuse it.

Troubleshooting sheet

Choose the symptom you actually see. Run the checks below inside the VM. Replace POD_NAME with a real Pod name. After the API works, use the explicit quote-lab namespace so an old terminal context does not hide your resources.

Cluster access or missing resources

Terminal window
sudo systemctl status k3s --no-pager
sudo journalctl -u k3s -n 50 --no-pager
kubectl config current-context
kubectl config view --minify -o jsonpath='{..namespace}'
kubectl get namespaces

Expected: k3s is active, your kubeconfig reaches the course cluster, and quote-lab exists once you have created it. A blank namespace means the default namespace, not missing workloads.

For permission errors, check the home-directory kubeconfig instructions in “Verify your environment”. For NotFound, confirm the namespace and whether you created that resource. If the service is failed or the node stays unready, show the service logs to the instructor. Reinstalling k3s is not the recovery step.

Image cannot be pulled

Terminal window
kubectl -n quote-lab get pods
kubectl -n quote-lab describe pod POD_NAME
sudo k3s ctr images list | grep 'quote-app'

Expected: the Deployment’s image tag exists in k3s’s image store. The baseline tag is docker.io/library/quote-app:local; keep your newer tag if you deliberately built and imported one.

For ErrImagePull or ImagePullBackOff, compare the exact image name in events with the imported tag. Return to “Build and import your image for Kubernetes” on this same VM. After correcting the import, delete only the failing app Pod and verify its replacement. If app code changed, build/import a new tag and update the saved manifest. A Docker image listing alone does not verify the k3s import.

Pod is Pending, restarting, or not Ready

Terminal window
kubectl -n quote-lab get pods -l app=quote-app -o wide
kubectl -n quote-lab describe pod POD_NAME
kubectl -n quote-lab logs POD_NAME
kubectl -n quote-lab get events --sort-by=.metadata.creationTimestamp

Expected: each settled app Pod shows 1/1 Ready. Events identify scheduling, configuration, or probe problems; logs show application errors. For a restarting container, also inspect its previous instance:

Terminal window
kubectl -n quote-lab logs POD_NAME --previous

Use --previous only when a previous container instance exists. For Pending, inspect scheduling events and VM capacity. For app crashes, check the startup error and configuration. A Running Pod with 0/1 and readiness HTTP 404 is the intended bad-probe symptom: save its event, restore /health in the manifest, apply, and wait for recovery. Keep the readiness probe.

Service or health request fails

Terminal window
kubectl -n quote-lab get svc quote-app -o yaml
kubectl -n quote-lab get pods -l app=quote-app --show-labels
kubectl -n quote-lab get endpointslices -l kubernetes.io/service-name=quote-app -o yaml
kubectl -n quote-lab get pod service-check

Expected: the Service selector matches app: quote-app, Service port 80 targets application port 3000, and settled healthy Pods have ready endpoints. Reuse the Ready service-check client from “Verify the in-cluster Service path”, or create it there if missing. Then check:

Terminal window
kubectl -n quote-lab exec service-check -- wget -qO- http://quote-app/health

Expect JSON containing "ok": true. If the client image cannot download, inspect its events first. If the app Pod is Ready but the Service has no ready endpoints, compare selectors and labels. Port-forward success alone does not verify this Service path.

VM access works but laptop access fails

Restart this command in a VM terminal and leave it running:

Terminal window
kubectl -n quote-lab port-forward svc/quote-app 8080:80

In a second VM terminal, check:

Terminal window
curl --fail --show-error --max-time 5 http://127.0.0.1:8080/health

Expected: "ok": true inside the VM. Your laptop’s localhost is a different machine. Use the SSH tunnel or VS Code Ports instructions in “Access the health endpoint” for laptop access. If the selected Pod was replaced, restart the VM port-forward. If port 8080 is already in use, check for an existing forward before starting another.

Health works but the quote page fails

Expected for the Lab 42 baseline: /health can work before PostgreSQL is configured. Quote retrieval and insertion require the app’s database handlers, DATABASE_URL, and an initialized database. Finish the Lab 42 core, then use Lab 52’s guided database setup when released. A successful health check does not prove database functionality.

If you spend 10 minutes without a new diagnostic finding, request a check-in. Show the command, expected result, actual result, and the latest relevant event or log excerpt. Keep credentials out of shared output.


Verify your environment

Your goal is to end up with a working Kubernetes cluster and kubectl access. Whenever a command contains POD_NAME, replace it with an actual name from kubectl get pods; do not paste the placeholder literally.

Use k3s on your course Ubuntu Server VM, reusing the VM and your own GitHub fork from RES507. Run all shell commands in this lab inside that VM, including Docker builds and image imports.

Use the branch containing your completed Session 2 application. Do not replace it with a fresh copy of the scaffold. If it is missing, fetch your fork, find that branch with git branch -a, and ask for help before switching with uncommitted changes. Verify /health in your existing app before troubleshooting Kubernetes.

Check capacity before installing:

Terminal window
nproc
free -h
df -h / /tmp
cat /etc/os-release

K3s documents a server minimum of two CPU cores and 2 GB RAM, before your workloads. For this lab, 4 GB RAM and 10 GB free disk are planning recommendations for the builds, images, and database work, not a measured guarantee. Use the course Ubuntu Server VM; ask for help if capacity is low. See the k3s requirements.

If your node is not Ready after 20 minutes of setup work, request an instructor check-in. If it is already Ready, continue; you do not need to wait for the rest of the class.

Set up k3s and kubectl access

Click to expand: k3s + kubectl setup on Ubuntu Server

Check whether k3s is already installed:

Terminal window
command -v k3s

If this prints a path, skip installation and check the service below before configuring kubectl. Reuse your existing cluster; do not reinstall or change its runtime during this lab.

If it prints nothing, install a single-node cluster with the default containerd runtime:

Terminal window
sudo apt update
curl -sfL https://get.k3s.io -o /tmp/install-k3s.sh
sudo env INSTALL_K3S_CHANNEL=stable sh /tmp/install-k3s.sh

The official installer selects a release from the stable channel; you do not need a version number beforehand. Reuse an existing working cluster rather than upgrading it for this lab. After installation, record k3s --version and your Ubuntu version in your short verification notes.

The installer needs administrator privileges to create the system service. Docker remains your image build tool; k3s runs the Kubernetes workloads.

Check the service:

Terminal window
sudo systemctl status k3s --no-pager

If it is inactive or failed, inspect its logs and ask for help before continuing. Do not rerun the installer as a troubleshooting step.

Terminal window
sudo journalctl -u k3s -n 50 --no-pager

This replaces the VM user’s default kubeconfig with this course cluster’s configuration. If you already use that file for another cluster, preserve it and ask for help first. Do not commit kubeconfig to Git.

Copy the cluster configuration into your own home directory. Run these commands as your normal VM user:

Terminal window
mkdir -p "$HOME/.kube"
sudo install -m 600 -o "$(id -u)" -g "$(id -g)" \
/etc/rancher/k3s/k3s.yaml "$HOME/.kube/config"
echo 'export KUBECONFIG="$HOME/.kube/config"' >> ~/.bashrc
export KUBECONFIG="$HOME/.kube/config"

The append command configures future Bash terminals; the export configures your current terminal. Running the append command again adds a harmless duplicate of the same assignment.

Use ordinary kubectl, not sudo kubectl. Setting KUBECONFIG explicitly ensures it uses your writable home-directory copy rather than the protected system file.

Wait for the node to become Ready, then verify access:

Terminal window
kubectl wait --for=condition=Ready node --all --timeout=120s
kubectl get nodes -o wide
kubectl get pods -A

Check the CONTAINER-RUNTIME column in kubectl get nodes -o wide: this lab expects containerd. If it shows another runtime, ask for help instead of changing it. Record k3s --version in your verification notes.

Continue only when your node is Ready. If the wait times out, inspect the service logs above and ask for help.

Tools checklist

Click to expand: tools you should have

You should have:

  • kubectl
  • docker (for building the image)

On the course VM:

  • kubectl is provided by k3s (it installs a kubectl binary)
  • Docker should already be available from earlier labs

Quick checks:

Terminal window
kubectl version --client
docker version

Create a dedicated namespace

Create a namespace for this lab:

Terminal window
kubectl create namespace quote-lab --dry-run=client -o yaml | kubectl apply -f -

A namespace groups this lab’s resources. Set it as the default in your current kubeconfig context (this persists across terminals and future sessions):

Terminal window
kubectl config set-context --current --namespace=quote-lab

Verify:

Terminal window
kubectl config view --minify -o jsonpath='{..namespace}'
kubectl get pods

Expect quote-lab; an empty Pod list is normal before deploying. Use kubectl -n quote-lab ... if another exercise changes your default namespace.


Build and import your image for Kubernetes

In your Ubuntu VM terminal, return to the root of your own lab repository, then enter the existing application directory:

Terminal window
cd "$(git rev-parse --show-toplevel)"
git status
git branch --show-current
cd labs/20-containerized-node-app
ls app/package.json app/package-lock.json docker/Dockerfile

Build the image using the lab directory as its context:

Terminal window
docker build -t quote-app:local -f docker/Dockerfile .

Import the image before deploying it. Default k3s uses containerd, whose image store is separate from Docker’s. A successful Docker build alone does not make the image available to Kubernetes.

Terminal window
docker save quote-app:local -o /tmp/quote-app.tar
sudo k3s ctr images import /tmp/quote-app.tar
sudo k3s ctr images list | grep 'quote-app:local'

Continue only when the image list contains docker.io/library/quote-app:local. All three operations must happen on the same Ubuntu VM that runs your single-node cluster.

If you change application code later, build and import a new tag, such as quote-app:v2, then update the Deployment’s image to that tag. Applying unchanged YAML does not deploy a rebuilt image automatically.

Troubleshooting: image not found or cluster setup

For ImagePullBackOff or ErrImagePull, inspect the Pod’s events with kubectl describe pod POD_NAME. Confirm that the Deployment image matches the imported tag exactly and that the import happened on this VM.

After correcting an import for the existing tag, delete the failing Pod to let its Deployment create a replacement:

Terminal window
kubectl delete pod POD_NAME
kubectl get pods -w

Press Ctrl+C to stop watching.

If you are unsure whether the problem is your application image, try a public image:

Terminal window
kubectl create deployment nginx-test --image=nginx
kubectl rollout status deployment/nginx-test --timeout=120s

If nginx becomes available, the cluster can run that public workload; investigate your application image and configuration. If it fails, inspect its events too: registry/network access may be the cause.

Remove the diagnostic Deployment when finished:

Terminal window
kubectl delete deployment nginx-test

Prepare your manifest directory

Keep your Kubernetes manifests separate from the existing application files. In the same VM terminal:

Terminal window
cd "$(git rev-parse --show-toplevel)"
mkdir -p labs/42-kubernetes
cd labs/42-kubernetes

Create deployment.yaml and service.yaml here, and run the following manifest commands from this directory. Commit these files in your own fork.


Create a Deployment

The baseline deploys the application without PostgreSQL. Its /health route returns {"ok": true} without querying the database. Your completed quote page and quote insertion may fail until you add database integration; that does not invalidate this health-only baseline.

Create a file named deployment.yaml with the following content:

apiVersion: apps/v1
kind: Deployment
metadata:
name: quote-app
spec:
replicas: 1
selector:
matchLabels:
app: quote-app
template:
metadata:
labels:
app: quote-app
spec:
containers:
- name: quote-app
image: quote-app:local
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3000
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 3
periodSeconds: 5

Before applying, identify replicas, the Pod template’s app: quote-app label, and the Deployment selector matching that label. A Deployment manages ReplicaSets, which maintain the requested Pods.

Readiness determines eligibility for normal Service traffic. Liveness can trigger container restart. This baseline uses readiness only. Neither probe validates database behavior unless its endpoint actually checks the database.

Apply it:

Terminal window
kubectl apply -f deployment.yaml
kubectl rollout status deployment/quote-app --timeout=120s

Observe:

Terminal window
kubectl get pods
kubectl describe pod POD_NAME

Continue when the rollout completes and your application Pod shows 1/1 in the READY column. Running alone is not enough.


Expose the application with a Service

Create service.yaml:

apiVersion: v1
kind: Service
metadata:
name: quote-app
spec:
selector:
app: quote-app
ports:
- port: 80
targetPort: 3000
type: ClusterIP

Identify the Service selector matching the Pod label. port: 80 is the Service port; targetPort: 3000 is the application’s listening port. A containerPort declaration alone does not make the application listen there.

Apply it:

Terminal window
kubectl apply -f service.yaml

Verify:

Terminal window
kubectl get svc

Access the health endpoint

In a VM terminal, start port-forwarding and leave it running:

Terminal window
kubectl port-forward svc/quote-app 8080:80

Wait for the Forwarding from 127.0.0.1:8080 message. In a second terminal inside the same VM, check the endpoint:

Terminal window
curl --fail --show-error --max-time 5 http://127.0.0.1:8080/health

Expect JSON containing "ok": true. You are checking application health; quote retrieval and insertion are part of the database extension.

ClusterIP is internal to the cluster. This port-forward listens on the VM’s loopback interface, not your laptop’s. To use your laptop browser, keep the VM port-forward running and open another terminal on your laptop, using Bash or zsh (on Windows, use WSL Bash):

Terminal window
printf "VM SSH username: "
read -r vm_user
printf "VM IP address: "
read -r vm_host
ssh -N -L 8080:127.0.0.1:8080 "$vm_user@$vm_host"

Use the same SSH destination and connection options you normally use to reach the course VM. Leave this tunnel running, then open the health endpoint in your laptop browser. If using VS Code Remote SSH, you can instead forward the VM’s port 8080 through its Ports panel.

A port-forward to a Service selects one Pod and forwards directly to it. It does not demonstrate Service load balancing or readiness-based traffic selection. If that Pod disappears, restart the VM port-forward; other Ready replicas do not keep that particular forwarding session alive.

Verify the in-cluster Service path

From your VM, create a temporary client Pod in quote-lab:

Terminal window
kubectl run service-check --image=busybox:1.37 --restart=Never --command -- sleep 3600
kubectl wait --for=condition=Ready pod/service-check --timeout=120s
kubectl exec service-check -- wget -qO- http://quote-app/health

Expect {"ok":true}. This request uses cluster DNS and the Service path, unlike port-forwarding. If it fails, inspect the client Pod’s events, the Service selector, and its EndpointSlices. An image download error is a client setup problem, not proof that the Service is broken. If service-check already exists from an earlier attempt, reuse it if Ready or delete that diagnostic Pod and recreate it.

Keep the client for the readiness exercise. Save this result beside the port-forward result.

Scale the Deployment

In deployment.yaml, change replicas: 1 to replicas: 3. Apply the file so the saved desired state matches the cluster:

Terminal window
kubectl apply -f deployment.yaml
kubectl rollout status deployment/quote-app --timeout=120s
kubectl get pods -l app=quote-app -o wide

Confirm that three Pods have READY equal to 1/1.

Discuss:

  • What does scaling change?
  • Does scaling the application also create a database or validate quote operations?

Keep replicas: 3 in the file for the following exercises. Reapplying a file with replicas: 1 would undo the scaling change.

Observe Pod replacement

Predict: which object will create the replacement, and will its name be the same? Three Pods on this single-node cluster do not protect against losing the VM.

In one VM terminal, watch the Pods:

Terminal window
kubectl get pods -l app=quote-app -w

In another VM terminal, list the Pods and delete one. Replace POD_NAME below with a name from the list:

Terminal window
kubectl get pods -l app=quote-app
kubectl delete pod POD_NAME

Observe a replacement Pod appearing with a new name and becoming Ready. The Deployment’s ReplicaSet reconciles the replica count back to three. Press Ctrl+C to stop watching once the replacement is Ready.

The remaining Ready Pods can remain eligible for Service traffic while the replacement starts. This exercise demonstrates reconciliation, not a guarantee of zero downtime. If the deleted Pod was selected by your port-forward, restart that command and repeat the health request.

Introduce a controlled readiness failure

This step is required for grading. Predict first: will the container restart, and can old healthy replicas still serve requests?

In deployment.yaml, change only the readiness probe’s path from /health to /does-not-exist. Keep the image, application port, and three replicas unchanged. Apply the change:

Terminal window
kubectl apply -f deployment.yaml
kubectl rollout status deployment/quote-app --timeout=60s
kubectl get pods -l app=quote-app
kubectl get rs -l app=quote-app

The rollout command is expected to time out because the new Pods cannot become Ready. The timeout stops your wait; Kubernetes continues trying to reconcile the Deployment.

Find a new Pod showing 0/1 in the READY column. Replace POD_NAME with that Pod’s name and inspect it:

Terminal window
kubectl describe pod POD_NAME
kubectl logs POD_NAME
kubectl get events --sort-by=.metadata.creationTimestamp

Look for an Unhealthy event reporting a readiness probe failure with HTTP status 404. The container can be Running while its Pod is not Ready. A failing readiness probe does not itself restart the container.

Inspect the Service’s endpoint readiness:

Terminal window
kubectl get endpointslices -l kubernetes.io/service-name=quote-app -o yaml

In each endpoint, compare targetRef.name with your Pod list and find conditions.ready. Expect false for the new failing Pod and true for healthy Pods.

The new unready Pods are not eligible for normal Service traffic. With the default rolling-update strategy, old healthy replicas can remain available while the rollout is stalled. The Service may therefore still work; a total outage is not the expected proof of this failure. Do not use a successful port-forward request as proof that the new Pods are Ready.

An invalid image tag would produce a different failure (ErrImagePull or ImagePullBackOff), rather than a readiness probe failure. Use the bad probe path for this exercise so you can distinguish the two.

Restore the probe path to /health in the file, then verify recovery:

Terminal window
kubectl apply -f deployment.yaml
kubectl rollout status deployment/quote-app --timeout=120s
kubectl get pods -l app=quote-app

Confirm the rollout completes and three Pods are Ready once the rollout settles. Restart port-forwarding if needed, and repeat the /health request.


Compare your diagnostic signals

Compare your observations verbally: status showed 0/1, events identified the HTTP 404, and logs showed the application’s activity. Which source explained the cause most directly?

Repeat the in-cluster request after recovery, then remove the diagnostic client:

Terminal window
kubectl exec service-check -- wget -qO- http://quote-app/health
kubectl delete pod service-check

Extension lanes

If unsure, start with Lane B. Time estimates below are planning guides after the core, not deadlines. Lanes A/B overlap with Lab 52; carry that work forward rather than repeating it.

Choose one lane for the extension part of the final assessment. Complete working changes, show the verification result, and explain what it demonstrates. Adding YAML without checking its effect or writing an explanation alone is insufficient.

Begin as soon as the core work is verified, including while waiting for an instructor review. Continue unfinished extension work before the later assessment. Extra lanes are optional; focus on one well-understood result rather than the number completed.

Lane A: Resource management and limits

Intermediate · 45–60 min. Start with requests of 100m CPU / 128Mi memory and limits of 250m / 256Mi. Use a separate disposable Node.js test Pod with a small memory limit and retained, touched buffers to investigate memory pressure; do not stress the whole VM. Inspect lastState.terminated.reason and kubectl logs POD_NAME --previous; a JavaScript heap error alone is not proof of OOMKilled.

  • Add resource requests and limits to the container
  • Observe scheduling behavior
  • Simulate memory pressure
  • Explain how limits protect cluster stability

Verification: show the requests/limits in your manifest and an observed effect, such as an OOMKilled termination after exceeding the memory limit. Restore a working configuration and explain the difference between a request and a limit.

Lane B: Liveness probes

Recommended starting lane · 20–35 min. Copy the readiness HTTP probe into livenessProbe using /health. Change only liveness to /does-not-exist, wait for failed checks, and inspect restart counts and events. Restore the path afterward. This deliberate bad URL models a failed check; it is not a real application deadlock.

  • Add a livenessProbe
  • Simulate an internal failure
  • Observe container restarts
  • Explain readiness vs liveness differences

Verification: show a deliberate liveness failure causing the container’s restart count to increase, then restore a healthy probe. Explain why the readiness failure in the core lab did not itself restart the container.

Lane C: Configuration management

Intermediate · 30–45 min. Start with the app’s existing PORT setting. Put PORT=3000 in a ConfigMap and use configMapKeyRef. Change it to 3001, restart the Deployment, and update the Service targetPort, probe ports, and containerPort together. Verify a request through the Service; restore consistent ports afterward.

  • Move configuration to a ConfigMap
  • Inject environment variables
  • Change config without rebuilding the image
  • Explain why immutable images matter

Verification: show the app consuming a ConfigMap value, change that value, and roll out Pods that use it without rebuilding the image. Environment variables in existing containers do not update automatically when a ConfigMap changes.

Lane D: Database integration

Advanced · 60–90+ min. Follow the guided database setup in Lab 52 when the instructor makes it available. Reuse labs/20-containerized-node-app/db/init.sql, supply the app’s DATABASE_URL, and mount PostgreSQL 18 storage at /var/lib/postgresql. Persistence across a Pod replacement is required; creating an empty database alone is insufficient. This is optional today and required in Lab 52. Keep your verified work for that lab.

  • Deploy PostgreSQL inside Kubernetes
  • Use a Service for connectivity
  • Add a PersistentVolumeClaim
  • Validate data persistence across restarts

Verification: insert a distinct quote through the app, replace the PostgreSQL Pod while preserving its PVC, and retrieve the same quote afterwards. Include the database Service and the app’s connection configuration.

Lane E: Horizontal scaling reasoning

Intermediate · 45–60 min. First check kubectl top pods and kubectl top nodes. If metrics are unavailable, resolve that with the instructor before choosing this lane. Generate a bounded stream of /health requests from a temporary client Pod and compare idle/load CPU readings. CPU-utilization HPA also needs CPU requests. Clean up the load client afterward.

  • Simulate traffic load
  • Observe CPU usage
  • Discuss what HPA would require
  • Explain why autoscaling depends on metrics

Verification: show measured CPU usage during your load test and explain what an HPA would need to use it. State any missing metrics or resource configuration; do not claim automatic scaling unless you actually configured and observed it.

Lane F: Multi-stage validation

Advanced · 60–90 min. Add an observable /version response, build and import two distinct image tags, then apply each namespace’s manifests with an explicit -n. Verify each version with separate port-forwards. Namespaces separate names and scope; they do not automatically isolate network traffic.

  • Create separate namespaces for dev and staging
  • Deploy different versions
  • Compare behaviors
  • Explain how this fits into CI/CD pipelines

Verification: show two namespaces running different image versions, access each version, and identify an observable difference. Explain how you kept their resources and configuration separate.


Reflect on orchestration

Answer the following verbally during review:

  • What does Kubernetes automate?
  • What remains your responsibility?
  • What signals determine application health?
  • How does this differ from Docker Compose?

Prepare your review evidence

Keep these items in labs/42-kubernetes in your own fork:

  • Your final working deployment.yaml and service.yaml, plus any extension manifests or code.
  • A short verification.md with selected terminal output or linked screenshots showing: the forwarded and in-cluster health requests, three Ready replicas, the deleted and replacement Pod names, the deliberate readiness failure, and the recovered rollout. Include the probe failure event; a screenshot of 0/1 alone does not explain the cause.
  • Three brief bullets in that same file: symptom, cause, and fix. Name the probe path you changed and explain why restoring it recovered readiness.
  • For an extension, include the change and its verification result. Identify what the result demonstrates.

Use short excerpts with labels; do not paste whole logs or write a report for each step. The conceptual questions above are for the oral review.

Commit and push your final working files and evidence. Before review, open your fork and have the VM ready to show the current Deployment, Pods, and Service. Explain what your evidence proves and what it leaves untested.

Grading rubric

The final assessment uses four broad categories. In-session check-ins provide feedback; you are not expected to complete the final assessment in this first session.

CategoryPointsFull-credit evidence
Deployment and runtime validation6Working Deployment and Service, successful health request, three Ready replicas, and observed Pod replacement. Explain how the manifests produce that behavior.
Failure diagnosis and recovery6Deliberate readiness failure, the event identifying its cause, a justified fix, and verified recovery. Keep the probe rather than removing it to get Ready.
Meaningful extension4One lane with working changes and the specified verification evidence. Explain the behavior it adds.
Oral understanding4Explain reconciliation, Deployment versus Service, readiness versus liveness, and what the health-only baseline leaves untested.
Total20

Points reflect demonstrated behavior and reasoning, not the number of completed steps. Partial work can earn partial credit when you can show and explain it. A working baseline alone does not earn extension or oral-understanding points.


Wrap-up

You have:

  • Deployed an application to Kubernetes
  • Exposed it with a Service
  • Validated runtime behavior
  • Simulated failures
  • Explored scaling and resilience

This establishes the orchestration baseline for RES509. Next, Lab 52 adds a required, guided PostgreSQL deployment and connects this system to VMs, isolation, and production architecture. Keep your cluster, manifests, and evidence for that work.

In larger systems, this foundation expands to:

  • Ingress controllers
  • Service meshes
  • Observability stacks
  • GitOps workflows
  • Infrastructure automation

You now understand the core control loop that powers modern cloud-native systems.