Kubernetes Fundamentals · Module 14 of 14

Capstone: deploy, expose, persist and operate an application

Combine namespace-scoped configuration, Deployments, Services, probes, resource controls, a PVC, service discovery and operational verification into one reproducible project.

Learning objectives

By the end of this module, you should be able to:

  • Deploy a multi-resource manifest safely.
  • Verify rollout, Service discovery, health and persistent state.
  • Recover the stateful component and prove that persistent data survives Pod replacement.

Capstones should prove behavior, not only object creation

A useful Kubernetes lab verifies the result from inside the cluster: Pods become ready, Services resolve, health checks pass and persistent data survives replacement.

Separate stateless and stateful concerns

The web tier is disposable and replicated. Redis uses a PVC so its append-only data can survive Pod replacement. This is a teaching example, not a production Redis architecture; production stateful systems need topology, backup, HA and failure-domain design.

Operational verification closes the loop

kubectl apply is only the start. rollout status, get/describe, in-cluster requests and persistence checks prove whether the declared state became a functioning system.

Worked examples

See the idea in practice.

Capstone verification sequence

kubectl -n gnu-k8s-fundamentals rollout status deployment/cap-web
kubectl -n gnu-k8s-fundamentals rollout status deployment/cap-redis
kubectl -n gnu-k8s-fundamentals get pods,svc,pvc
kubectl -n gnu-k8s-fundamentals get endpointslices
  • Wait for controllers.
  • Inspect service endpoints.
  • Verify the claim is bound before testing persistence.
Hands-on lab

Lab — Multi-tier Kubernetes capstone

Goal: Deploy nginx from a ConfigMap, Redis with a PVC, health probes and Services; write a Redis key, replace the Redis Pod and prove the key survives.

Before you start

  • kubectl is installed and can reach the lab cluster.
  • Use the dedicated gnu-k8s-fundamentals namespace unless a step explicitly says otherwise.
STEP 1

Apply the capstone

cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
  name: cap-web-content
  namespace: gnu-k8s-fundamentals
data:
  index.html: |
    <h1>GNU Group Kubernetes Fundamentals</h1>
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: cap-redis-data
  namespace: gnu-k8s-fundamentals
spec:
  accessModes: [ReadWriteOnce]
  resources:
    requests: {storage: 100Mi}
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cap-redis
  namespace: gnu-k8s-fundamentals
spec:
  replicas: 1
  selector: {matchLabels: {app: cap-redis}}
  template:
    metadata: {labels: {app: cap-redis}}
    spec:
      containers:
      - name: redis
        image: redis:7-alpine
        args: ["redis-server","--appendonly","yes"]
        ports: [{containerPort: 6379}]
        readinessProbe: {exec: {command: ["redis-cli","ping"]}, periodSeconds: 2}
        resources:
          requests: {cpu: 10m, memory: 32Mi}
          limits: {cpu: 250m, memory: 128Mi}
        volumeMounts: [{name: data, mountPath: /data}]
      volumes: [{name: data, persistentVolumeClaim: {claimName: cap-redis-data}}]
---
apiVersion: v1
kind: Service
metadata:
  name: cap-redis
  namespace: gnu-k8s-fundamentals
spec:
  selector: {app: cap-redis}
  ports: [{port: 6379, targetPort: 6379}]
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cap-web
  namespace: gnu-k8s-fundamentals
spec:
  replicas: 2
  selector: {matchLabels: {app: cap-web}}
  template:
    metadata: {labels: {app: cap-web}}
    spec:
      containers:
      - name: nginx
        image: nginx:alpine
        ports: [{containerPort: 80}]
        readinessProbe: {httpGet: {path: /, port: 80}, periodSeconds: 2}
        resources:
          requests: {cpu: 10m, memory: 16Mi}
          limits: {cpu: 100m, memory: 64Mi}
        volumeMounts: [{name: content, mountPath: /usr/share/nginx/html, readOnly: true}]
      volumes: [{name: content, configMap: {name: cap-web-content}}]
---
apiVersion: v1
kind: Service
metadata:
  name: cap-web
  namespace: gnu-k8s-fundamentals
spec:
  selector: {app: cap-web}
  ports: [{port: 80, targetPort: 80}]
EOF
STEP 2

Wait for storage and rollouts

kubectl -n gnu-k8s-fundamentals wait --for=jsonpath='{.status.phase}'=Bound pvc/cap-redis-data --timeout=120s
kubectl -n gnu-k8s-fundamentals rollout status deployment/cap-redis --timeout=180s
kubectl -n gnu-k8s-fundamentals rollout status deployment/cap-web --timeout=180s
STEP 3

Verify web Service DNS

kubectl -n gnu-k8s-fundamentals run cap-client --rm -i --restart=Never --image=busybox:1.36 -- wget -qO- http://cap-web
STEP 4

Write Redis state through the Service

kubectl -n gnu-k8s-fundamentals run redis-client --rm -i --restart=Never --image=redis:7-alpine -- redis-cli -h cap-redis SET course kubernetes
STEP 5

Replace the Redis Pod

kubectl -n gnu-k8s-fundamentals delete pod -l app=cap-redis --wait=true
kubectl -n gnu-k8s-fundamentals rollout status deployment/cap-redis --timeout=180s
STEP 6

Read the persisted key

kubectl -n gnu-k8s-fundamentals run redis-check --rm -i --restart=Never --image=redis:7-alpine -- redis-cli -h cap-redis GET course
STEP 7

Clean up all capstone objects

kubectl -n gnu-k8s-fundamentals delete service cap-web cap-redis
kubectl -n gnu-k8s-fundamentals delete deployment cap-web cap-redis
kubectl -n gnu-k8s-fundamentals delete configmap cap-web-content
kubectl -n gnu-k8s-fundamentals delete pvc cap-redis-data

Verify

  • Both Deployments roll out successfully.
  • The web Service returns GNU Group Kubernetes Fundamentals.
  • Redis returns OK for SET and kubernetes for GET after its Pod is replaced.

Expected outcome

  • The capstone demonstrates stateless replication, Service discovery, configuration injection, readiness and persistent storage.

If it fails

  • If PVC is Pending, inspect the default StorageClass and events.
  • If Redis GET fails after replacement, confirm the replacement mounted the same PVC and AOF completed its initial write.
  • If Services have no endpoints, compare selectors and Pod readiness.
Real-world connection

This pattern mirrors real platform work: declare multiple cooperating resources, verify control-plane convergence, then test behavior and recovery.

Avoid these traps

Common mistakes

  • Calling apply success the same thing as application success.
  • Treating a single-replica Redis Deployment as a production HA design.
  • Deleting the PVC before verifying persistence.
Knowledge check

Can you explain it without looking back?

Open each answer only after you have tried to answer the question yourself.

1What proves the web tier is serving through Kubernetes discovery?

An in-cluster client successfully calls the Service DNS name.

2What proves the Redis data outlives a Pod?

The key remains readable after the Redis Pod is replaced while the PVC is preserved.

3Why wait for rollout status?

To verify controllers converged before functional testing.