Pod vs ReplicaSet vs Deployment vs Service
Four objects that are easy to mix up. Three of them form one chain of ownership; the fourth sits beside them and only deals with traffic. This chapter puts them side by side with one complete example.
In one line each
Pod
Runs your container. Temporary, with a temporary IP.
ReplicaSet
Counts Pods and keeps exactly N alive.
Deployment
Changes versions safely by managing ReplicaSets.
Service
Routes traffic to Pods through one stable name.
An analogy: in a restaurant, Pods are the cooks. The ReplicaSet is the shift manager who makes sure 3 cooks are always on duty. The Deployment is the head chef who switches the kitchen to a new menu one cook at a time, and switches back if diners complain. The Service is the restaurant's phone number: customers call it, never a cook's personal number.
Side by side
| Pod | ReplicaSet | Deployment | Service | |
|---|---|---|---|---|
| Job | Run containers | Keep N Pods alive | Roll out and roll back versions | Stable address + load balancing |
apiVersion | v1 | apps/v1 | apps/v1 | v1 |
| Contains | Containers | Pod template | Pod template (makes ReplicaSets) | Selector + ports, no Pods |
| Creates | — | Pods | ReplicaSets | EndpointSlices (the IP list) |
| Uses labels to | be found | count its Pods | find its ReplicaSets' Pods | pick where traffic goes |
| Self-healing | Restarts its containers only | ✓ replaces Pods | ✓ (through ReplicaSet) | Skips Pods that are not ready |
| Scaling | ✗ | ✓ | ✓ | — |
| Rolling update / rollback | ✗ | ✗ | ✓ | — |
| Stable IP / DNS name | ✗ changes | ✗ | ✗ | ✓ |
| Do you write it? | Rarely (debugging, one-offs) | Almost never | Yes, for every app | Yes, for every app that gets called |
How they connect
One complete example
This is the file you will actually write for the posts app: a Deployment and a Service, separated by ---. Click the highlighted parts to see what must match what.
# posts.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: posts
spec:
replicas: 3
selector:
matchLabels:
app: posts
template:
metadata:
labels:
app: posts
spec:
containers:
- name: posts
image: micro-posts:v1
ports:
- containerPort: 4000
---
apiVersion: v1
kind: Service
metadata:
name: posts-service
spec:
selector:
app: posts
ports:
- port: 80
targetPort: 4000
The three app: posts
The Deployment's selector, the Pod template's labels and the Service's selector must all agree. The Deployment (and its ReplicaSet) use it to count Pods; the Service uses it to find Pods for traffic. This is the only link between the two halves of the file.
containerPort ↔ targetPort
Both are 4000: where your Node app listens. The Service accepts on 80 and forwards to 4000.
replicas: 3
Read by the ReplicaSet the Deployment creates. The Service does not care how many Pods there are; it uses whatever matches.
image
Change it and apply: the Deployment makes a new ReplicaSet and rolls Pods over. The Service keeps working throughout and sends traffic to whichever Pods are ready, old or new.
posts-service
The name other apps use: http://posts-service.
$ kubectl apply -f posts.yaml
deployment.apps/posts created
service/posts-service created
What kubectl shows
Click each line of kubectl get all to see which object it is and who created it:
$ kubectl get all
NAME READY STATUS
pod/posts-7d9f8c6b5d-4kq2p 1/1 Running
pod/posts-7d9f8c6b5d-m7x4t 1/1 Running
pod/posts-7d9f8c6b5d-zt9wn 1/1 Running
NAME TYPE CLUSTER-IP PORT(S)
service/posts-service ClusterIP 10.96.40.12 80/TCP
NAME READY UP-TO-DATE AVAILABLE
deployment.apps/posts 3/3 3 3
NAME DESIRED CURRENT READY
replicaset.apps/posts-7d9f8c6b5d 3 3 3
Deployment posts
The one you wrote. UP-TO-DATE = Pods running the latest template; AVAILABLE = ready Pods.
ReplicaSet posts-7d9f8c6b5d
Created by the Deployment. The suffix is a hash of the Pod template; a new image gives a new hash and a new ReplicaSet.
Pods posts-7d9f8c6b5d-xxxxx
Created by the ReplicaSet: its name plus a random suffix. Disposable; the names change every time.
Service posts-service
The one you wrote. Not owned by anything above and not named after them; it only matches Pods by label.
Who handles it?
For each event, pick the object that deals with it.
- A Pod crashes and must be replaced
The ReplicaSet counts 2 of 3 and creates a Pod. (Its container restarting inside the same Pod would be the kubelet's job.)
- Ship micro-posts:v2 without downtime
Only the Deployment does rolling updates, by moving Pods from the old ReplicaSet to a new one.
- The comments app needs to call posts
It calls
http://posts-service; the Service picks a ready Pod. - v2 is broken; go back to v1
kubectl rollout undo deployment/postsscales the old ReplicaSet back up. - Give the app container a shared network with a log sidecar
Containers in one Pod share its network and volumes (chapters 5–6).
- A Pod is starting and not ready yet; keep traffic away from it
Services only send to ready Pods; unready ones are left out of the endpoints.
Which one do I write?
| Situation | Write |
|---|---|
| A web app or API (posts, users, comments) | Deployment + Service |
| A background worker nobody calls | Deployment only |
| Quick debugging container | A bare Pod (kubectl run), deleted afterwards |
| Anything with ReplicaSet in it | Let the Deployment create it |
| A database that needs stable names and disks | StatefulSet + Service (later chapter) |
Pod vs ReplicaSet vs Deployment vs Service: common questions
Do I need both a Deployment and a Service?
Usually yes. The Deployment keeps your Pods running and updated; the Service gives them a stable name so other apps can call them. They connect through matching labels.