Skip to content

A Worked Example

Scope: one small web app, taken through the whole mortal lifecycle — install, reach it, scale it, update it, kill a pod and watch the system heal. All cluster steps are (documented): they are written for your cluster, per the README's note. The manifests themselves ship in examples/ and were schema-verified (kubeconform, strict, 1.31).

The app

hello-web: the nginx:alpine web server, serving a home page that lives in a ConfigMap instead of the image (Chapter 7), run by a Deployment at 2 copies, exposed by a NodePort Service on 30080. Four small files:

examples/configmap.yaml     the page itself, as data
examples/deployment.yaml    2 pods, the page mounted in
examples/service.yaml       NodePort 30080 → port 80
examples/secret.yaml        a specimen for Chapter 7; unused here

Step 0 — a cluster to stand on

Any local cluster from Chapter 11 works. With kind, one extra detail: to reach a NodePort from your machine, the kind cluster must map the port when created. Save this as kind-config.yaml (documented — kind's documented config format):

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
    extraPortMappings:
      - containerPort: 30080
        hostPort: 30080

Then: kind create cluster --config kind-config.yaml. (On minikube, minikube start is the whole step, and its NodePorts are reachable at minikube ip.)

Step 1 — apply the three files

kubectl apply -f examples/configmap.yaml
kubectl apply -f examples/deployment.yaml
kubectl apply -f examples/service.yaml
kubectl get pods

Order matters exactly once: the ConfigMap must exist before pods that mount it can start (apply the Deployment first and its pods wait in an error state until the ConfigMap appears — Troubleshooting covers that state). kubectl get pods should settle at two pods, both Running, readiness 1/1 — names with the Deployment's suffix pattern from Chapter 3.

Check: kubectl describe deployment hello-web shows desired 2 and available 2.

Step 2 — reach it

From inside the cluster's world, the Service is just a name (Chapter 6). From your machine, with the kind port mapping above:

curl http://localhost:30080/

Expected: the two lines from the ConfigMap — Hello from a ConfigMap. and the sentence after it. That page exists in no image: the stock nginx:alpine is serving content the cluster handed it at start. Repeat the curl; answers may come from either pod, indistinguishably — the Service is doing its one job.

Step 3 — scale it

kubectl scale deployment hello-web --replicas=4
kubectl get pods -w        # watch two more arrive; Ctrl-C to stop

Four copies, no ceremony: you edited a count and reconciliation closed the gap. (In file-first working you'd change replicas: in the manifest and re-apply — try that too, back to 2.)

Step 4 — roll out an update

kubectl set image deployment/hello-web web=nginx:1.27-alpine
kubectl rollout status deployment/hello-web

New pods (new ReplicaSet) start, report ready, and old pods retire, a few at a time per Chapter 5's strategy. kubectl get pods mid-roll shows both generations coexisting. kubectl rollout undo deployment/hello-web walks it back — do it once, just to have felt the safety net.

Step 5 — kill a pod, watch it heal

kubectl delete pod <one of the pod names>
kubectl get pods

A replacement is already being created before you look: the ReplicaSet counted 1 where 2 were desired and closed the gap. The Service never noticed — its endpoints simply point at the living. This thirty-second experiment is the entire self-healing claim of Chapter 1, made visible.

Step 6 — clean up

kubectl delete -f examples/service.yaml \
            -f examples/deployment.yaml \
            -f examples/configmap.yaml

Deleting the files' objects removes everything the example created; the cluster itself (kind's, say) goes with kind delete cluster.