Lab 2: Deployments, Services, Rolling Updates
Pairs with Phase 2. You'll deploy whoami (a tiny HTTP server that echoes its own hostname), watch self-healing and load balancing with your own eyes, then do a rolling update and roll it back.
Step 1: Try writing the Deployment yourself
Before copying anything: open a blank lab-2/deployment.yaml and try to write a Deployment named whoami, 3 replicas, image traefik/whoami:v1.10, container port 80. Use kubectl explain deployment.spec --recursive | less when stuck. Give it ten honest minutes.
Solution: deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: whoami
spec:
replicas: 3
selector:
matchLabels:
app: whoami
template:
metadata:
labels:
app: whoami
spec:
containers:
- name: whoami
image: traefik/whoami:v1.10
ports:
- containerPort: 80The part everyone gets wrong first: spec.selector.matchLabels must match spec.template.metadata.labels. The selector is how the Deployment finds its own pods - labels, not names.
kubectl apply -f lab-2/deployment.yaml
kubectl get podsExpected:
NAME READY STATUS RESTARTS AGE
whoami-7d4b9c8f6d-4kx2p 1/1 Running 0 15s
whoami-7d4b9c8f6d-9mwlz 1/1 Running 0 15s
whoami-7d4b9c8f6d-tr8vn 1/1 Running 0 15sNote the names: whoami (Deployment) + 7d4b9c8f6d (ReplicaSet hash) + random pod suffix. The layers are visible in the naming.
Step 2: The self-healing demo
kubectl get pods -wIn a second terminal, delete one pod (use a real name from your output):
kubectl delete pod whoami-7d4b9c8f6d-4kx2pWatch the first terminal: the pod goes Terminating, and a new pod with a new name immediately appears. You declared 3 replicas; the ReplicaSet noticed 2 and fixed it. This is the difference from Lab 1's bare pod. Ctrl-C when done.
Step 3: The Service
Try writing lab-2/service.yaml yourself: a ClusterIP Service named whoami, port 80, selecting the app's pods.
Solution: service.yaml
apiVersion: v1
kind: Service
metadata:
name: whoami
spec:
selector:
app: whoami
ports:
- port: 80
targetPort: 80No type: means ClusterIP. The selector matches the pod labels - the same duct tape again.
kubectl apply -f lab-2/service.yaml
kubectl get svc whoami
kubectl get endpoints whoamiThe endpoints output shows three IPs - the Service found all three pods via the label selector. If endpoints is empty, your selector doesn't match the pod labels: that's the #1 Service bug in the wild.
Step 4: See load balancing + cluster DNS
port-forward tunnels to a single pod, so it can't show load balancing. Instead, run a client inside the cluster:
kubectl run client --image=curlimages/curl --rm -it --restart=Never -- shInside, call the service by name a few times:
curl -s http://whoami | grep Hostname
curl -s http://whoami | grep Hostname
curl -s http://whoami | grep Hostname
exitExpected: the Hostname changes between requests - different pods answering. You just used cluster DNS (whoami resolves because the client pod is in the same namespace) and load balancing in one step.
Step 5: Rolling update and rollback
Watch in one terminal:
kubectl get pods -wIn the other, upgrade the image:
kubectl set image deployment/whoami whoami=traefik/whoami:v1.11
kubectl rollout status deployment/whoamiWatch old pods terminate one by one while new ones start - never all at once. Then check history and roll back:
kubectl rollout history deployment/whoami
kubectl rollout undo deployment/whoami
kubectl get pods -o jsonpath='{.items[*].spec.containers[*].image}'Expected: all pods back on traefik/whoami:v1.10.
In real life
kubectl set image is for labs and emergencies. The real workflow is: edit the YAML, kubectl apply. The YAML in git is the source of truth.
Verify it worked
- [ ] Killing a pod resulted in automatic replacement, and you can say which component noticed (the ReplicaSet)
- [ ]
kubectl get endpoints whoamilists 3 pod IPs - [ ] You saw different hostnames answering requests to one service name
- [ ] You rolled forward to v1.11 and back to v1.10
Keep these two files - Lab 6 reuses them. You can leave the app running.
Next: Lab 3: Config and State