Lesson 1 of 9 · 20m
Pods, Deployments, and Services: the shape of a workload
The three objects that carry almost every workload, and why you manage ReplicaSets by declaring a Deployment.
Your service just got its first real traffic and someone asks the question that matters: what happens when a node dies at 2am? In Kubernetes you don't deploy processes — you declare desired state, and controllers keep converging reality toward it. Three objects carry almost every workload you'll ever ship: the Pod, the Deployment, and the Service.
A Pod is the scheduling unit: one or more containers that share a network namespace (same IP, same localhost) and can share volumes. A pod is also disposable by design — when its node dies, the pod is gone. You almost never create bare Pods in production, because nothing reschedules them.
A Deployment is what you actually write. It manages a ReplicaSet, which keeps n identical pods alive, and it gives you rolling updates by creating a new ReplicaSet per pod-template change. A Service gives a stable virtual IP and DNS name in front of a shifting set of pods, selecting them by labels:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: registry.example.com/api:1.4.2
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: api
spec:
selector:
app: api
ports:
- port: 80
targetPort: 8080Apply it and watch the machinery work:
kubectl apply -f api.yaml
kubectl rollout status deployment/api
kubectl get pods -o wide # which node is each pod on?
kubectl get endpointslices -l kubernetes.io/service-name=apiThe Service doesn't know about pods directly — the controller writes EndpointSlice objects listing ready pod IPs, and kube-proxy (or your CNI's replacement) programs the dataplane. Inside the cluster, api.default.svc.cluster.local resolves to the Service IP. A Deployment's pod template labels must match its selector, and the selector is effectively immutable: changing it is rejected, because it would orphan every running pod.
Try this
Deploy the manifest above against kind or minikube, scale to five replicas with kubectl scale deployment/api --replicas=5, then delete a pod by name. Watch kubectl get pods -w replace it within seconds. Then delete the whole node if you're on kind (docker stop a worker container) and watch the pods reschedule elsewhere.
Pitfalls
- Running bare Pods. They die with their node and nothing brings them back. Always go through a Deployment, StatefulSet, or DaemonSet.
- Confusing
port(what the Service listens on) withtargetPort(what the pod listens on). A mismatched targetPort is a silent blackhole: the Service exists, endpoints exist, and every request times out. - Expecting Services to route by path. They don't — that's what Ingress or the Gateway API is for.