Skip to content

intility/appservices-devplatform-101

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 

Repository files navigation

Kubernetes Workshop: Hands-On Assignments

Time: 12:00 - 15:00 | Work at your own pace. Use AI tools (Claude, Claude Code) to help you learn!

You'll deploy a todo web app step by step — starting with the simplest possible thing, breaking it on purpose, and building up to a properly exposed, self-healing deployment.

The image is already built and ready: ghcr.io/alfredtm/k8s-todo-app:latest


Getting Started

Install the tools

On Windows (PowerShell):

winget install -e --id RedHat.OpenShift-Client
winget install -e --id Derailed.k9s

Restart your terminal after installing so the commands are available.

If winget isn't available, you can download oc.exe directly from the cluster console: open the console URL in your browser, click the ? icon in the top-right corner, and select Command Line Tools.

On Mac:

brew install openshift-cli k9s

Verify both are installed:

oc version
k9s version

Log in

Log in to the cluster with the command your instructor gave you:

oc login --token=<your-token> --server=<cluster-url>

Create your own namespace so you're not stepping on each other:

oc new-project <yourname>-todo

Project vs Namespace: In plain Kubernetes, isolated environments are called namespaces. OpenShift wraps this concept in a project, which is just a namespace with some extra metadata. oc new-project creates both at the same time — they refer to the same thing. Every kubectl/oc command accepts a -n <namespace> flag to target a specific namespace. We'll use it consistently so it's always clear where things are running.

Replace <yourname>-todo with your actual namespace name in every command below.


Task 1: Run a Pod

The simplest thing you can do in Kubernetes is run a single pod. No YAML needed:

oc run todo --image=ghcr.io/alfredtm/k8s-todo-app:latest --port=8080 -n <yourname>-todo

You'll see a security warning about missing securityContext settings — that's expected. The pod still runs. The Deployment you create in Task 4 fixes this properly by setting those fields explicitly.

Check that it started:

oc get pods -n <yourname>-todo
oc get pod todo -n <yourname>-todo

Wait until the STATUS column shows Running. You can also describe the pod to see more detail:

oc describe pod todo -n <yourname>-todo

Look at the Events section at the bottom — this is where Kubernetes logs what it did to start your pod (pulled the image, assigned it to a node, started the container).


Task 2: Port-Forward — Access It

The pod is running, but it's not reachable from the outside yet. Port-forward creates a temporary tunnel from your laptop directly to the pod:

oc port-forward pod/todo 8080:8080 -n <yourname>-todo

Open http://localhost:8080 in your browser. You have a running todo app. Add a few todos.

Press Ctrl+C to stop the port-forward when you're done, then move on.


Task 3: Delete It — Oh No

oc delete pod todo -n <yourname>-todo

Watch what happens:

oc get pods -n <yourname>-todo

The pod is gone. There's nothing left. If you port-forwarded again, there would be nothing to connect to.

Your todos are also gone — they lived in that pod's memory.

This is the problem with bare pods. Kubernetes doesn't restart them when they die. If the node crashes, if you delete it by accident, if the container crashes — it's just gone. You need something smarter.


Task 4: Deployment — Self-Healing

A Deployment tells Kubernetes "I want X copies of this pod running at all times." If a pod dies, Kubernetes starts a new one automatically.

Create a file called deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: todo-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: todo-app
  template:
    metadata:
      labels:
        app: todo-app
    spec:
      securityContext:
        runAsNonRoot: true
      containers:
        - name: todo-app
          image: ghcr.io/alfredtm/k8s-todo-app:latest
          ports:
            - containerPort: 8080
          securityContext:
            allowPrivilegeEscalation: false
            capabilities:
              drop: ["ALL"]

Apply it:

oc apply -f deployment.yaml -n <yourname>-todo
oc get pods -n <yourname>-todo -w

You'll see two pods start up. Now delete one of them:

oc delete pod <one-of-the-pod-names> -n <yourname>-todo

Watch what happens — Kubernetes immediately starts a replacement. The Deployment controller is constantly reconciling: making sure the actual state (running pods) matches the desired state (2 replicas).

Port-forward to verify the app still works:

oc port-forward deployment/todo-app 8080:8080 -n <yourname>-todo

Questions to think about:

  • What happens if you set replicas: 0? Try it: oc scale deployment todo-app --replicas=0 -n <yourname>-todo
  • What happens when you scale back up to 2?

Task 5: Service + HTTPRoute — Expose It Properly

Port-forwarding is only for local development. To make your app reachable on a real URL, you need two things:

  1. A Service — a stable internal address that load-balances across your pods
  2. An HTTPRoute — routes external traffic from the gateway into your Service

Create a file called expose.yaml:

apiVersion: v1
kind: Service
metadata:
  name: todo-app
spec:
  selector:
    app: todo-app
  ports:
    - port: 80
      targetPort: 8080
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: todo-app
spec:
  parentRefs:
    - name: aa
      namespace: envoy-gateway-system
  hostnames:
    - "todo-<yourname>.devapp-jmxg8t.intility.dev"
  rules:
    - backendRefs:
        - name: todo-app
          port: 80

Replace <yourname> with your actual name, then apply:

oc apply -f expose.yaml -n <yourname>-todo

Check that the HTTPRoute was accepted:

oc get httproute todo-app -n <yourname>-todo

The HOSTNAMES column should show your URL. Open it in your browser.

What you just built:

Internet → Gateway (aa) → HTTPRoute → Service → Pods

The Service selects pods using the app: todo-app label. Scale your deployment up and the Service automatically includes the new pods. Delete a pod and the Service stops sending traffic to it while Kubernetes restarts it.


Bonus: PostgreSQL — Persistent Storage

The app stores todos in memory. They disappear when a pod restarts. To make them persist, deploy a real database.

The cluster has the CloudNativePG operator installed. Create database.yaml:

apiVersion: v1
kind: Secret
metadata:
  name: postgres-credentials
type: kubernetes.io/basic-auth
stringData:
  username: todos
  password: todos
---
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: postgres
spec:
  instances: 1
  storage:
    size: 1Gi
  bootstrap:
    initdb:
      database: todos
      owner: todos
      secret:
        name: postgres-credentials

Apply and wait for postgres-1 to be Running:

oc apply -f database.yaml -n <yourname>-todo
oc get pods -n <yourname>-todo -w

Now update deployment.yaml to add the database URL. Add an env block inside the todo-app container, alongside the existing ports:

          ports:
            - containerPort: 8080
          env:
            - name: DATABASE_URL
              value: "postgres://todos:todos@postgres-rw:5432/todos?sslmode=disable"

Apply and restart:

oc apply -f deployment.yaml -n <yourname>-todo
oc rollout restart deployment/todo-app -n <yourname>-todo

The app header now shows "PostgreSQL (persistent)". Add some todos, delete a pod, scale to 0 and back — your todos survive everything.


Bonus: Case Studies

You've deployed an app on Kubernetes. Now see how companies run it at a scale that's hard to imagine.

Go to kubernetes.io/case-studies and pick a company that interests you. Read their write-up and find answers to these questions:

  • What problem were they trying to solve before Kubernetes?
  • What scale are they running at — how many containers, pods, or deployments?
  • What was the biggest benefit they got out of it?
  • What surprised you?

Tips & Troubleshooting

Pod not starting?

oc describe pod <pod-name> -n <yourname>-todo    # look at the Events section
oc logs <pod-name> -n <yourname>-todo

HTTPRoute not working?

oc get httproute todo-app -n <yourname>-todo     # check HOSTNAMES and status
oc get svc todo-app -n <yourname>-todo           # check the Service exists
oc get pods -n <yourname>-todo                   # check pods are Running

Need to start over?

oc delete project <yourname>-todo && oc new-project <yourname>-todo

This fully wipes and recreates your namespace — including things oc delete all misses, like the CNPG Cluster and HPA.

Need help?

  • Ask your neighbor
  • Ask Claude or Claude Code
  • oc explain <resource> — built-in docs, e.g. oc explain deployment.spec

About

Kubernetes workshop material for App Services Developer Platform 101

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors